diff --git a/deploy/hub/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml b/deploy/hub/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml index 2dba57261..4173576f8 100644 --- a/deploy/hub/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml +++ b/deploy/hub/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml @@ -97,6 +97,7 @@ spec: description: TimeAdded represents the time at which the taint was added. type: string format: date-time + nullable: true value: description: Value is the taint value corresponding to the taint key. type: string diff --git a/deploy/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml b/deploy/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml index 58d5ba940..31c7394a2 100644 --- a/deploy/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml +++ b/deploy/hub/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml @@ -295,7 +295,7 @@ spec: integer: description: Integer is the integer value when type is integer. type: integer - format: int32 + format: int64 string: description: String is the string value when when type is string. type: string diff --git a/go.mod b/go.mod index 4d1d1d55d..53666901f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( k8s.io/klog/v2 v2.30.0 k8s.io/kube-aggregator v0.23.0 k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b - open-cluster-management.io/api v0.5.1-0.20220104024812-246f7e36d0d2 + open-cluster-management.io/api v0.5.1-0.20220107041310-9303b90b6ade sigs.k8s.io/controller-runtime v0.11.0 ) diff --git a/go.sum b/go.sum index a3cb0afc9..161c93d47 100644 --- a/go.sum +++ b/go.sum @@ -1282,8 +1282,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I= -open-cluster-management.io/api v0.5.1-0.20220104024812-246f7e36d0d2 h1:r+2z448obb2/SfLCtjKN4ge7ZTcIj2qNVWU3qp6CuMw= -open-cluster-management.io/api v0.5.1-0.20220104024812-246f7e36d0d2/go.mod h1:0IUTh8J+p4pv1THh1r9oO0luX9Z1FLDEAmvzW09qC0o= +open-cluster-management.io/api v0.5.1-0.20220107041310-9303b90b6ade h1:oPuNeeOub6iZFavW/iULi+ctyJsOjQE75Plmuyk9D9Y= +open-cluster-management.io/api v0.5.1-0.20220107041310-9303b90b6ade/go.mod h1:0IUTh8J+p4pv1THh1r9oO0luX9Z1FLDEAmvzW09qC0o= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/helpers/helpers.go b/pkg/helpers/helpers.go index 36f2b6ebf..4b083db0f 100644 --- a/pkg/helpers/helpers.go +++ b/pkg/helpers/helpers.go @@ -321,3 +321,17 @@ func ManagedClusterAssetFn(fs embed.FS, managedClusterName string) resourceapply return assets.MustCreateAssetFromTemplate(name, template, config).Data, nil } } + +// FindTaintByKey returns a taint if the managed cluster has a taint with the given key. +func FindTaintByKey(managedCluster *clusterv1.ManagedCluster, key string) *clusterv1.Taint { + if managedCluster == nil { + return nil + } + for _, taint := range managedCluster.Spec.Taints { + if key != taint.Key { + continue + } + return &taint + } + return nil +} diff --git a/pkg/helpers/helpers_test.go b/pkg/helpers/helpers_test.go index 249f773b3..981ffd84b 100644 --- a/pkg/helpers/helpers_test.go +++ b/pkg/helpers/helpers_test.go @@ -451,6 +451,60 @@ func TestCleanUpGroupFromRoleBindings(t *testing.T) { } } +func TestFindTaintByKey(t *testing.T) { + cases := []struct { + name string + cluster *clusterv1.ManagedCluster + key string + expected *clusterv1.Taint + }{ + { + name: "nil of managed cluster", + key: "taint1", + }, + { + name: "taint found", + cluster: &clusterv1.ManagedCluster{ + Spec: clusterv1.ManagedClusterSpec{ + Taints: []clusterv1.Taint{ + { + Key: "taint1", + Value: "value1", + }, + }, + }, + }, + key: "taint1", + expected: &clusterv1.Taint{ + Key: "taint1", + Value: "value1", + }, + }, + { + name: "taint not found", + cluster: &clusterv1.ManagedCluster{ + Spec: clusterv1.ManagedClusterSpec{ + Taints: []clusterv1.Taint{ + { + Key: "taint1", + Value: "value1", + }, + }, + }, + }, + key: "taint2", + }, + } + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + actual := FindTaintByKey(c.cluster, c.key) + if !reflect.DeepEqual(actual, c.expected) { + t.Errorf("expected %v but got %v", c.expected, actual) + } + }) + } +} + func getApplyFileNames(applyFiles map[string]runtime.Object) []string { keys := []string{} for key := range applyFiles { diff --git a/pkg/webhook/cluster/mutating_webhook.go b/pkg/webhook/cluster/mutating_webhook.go index 8f8f55515..eed74c512 100644 --- a/pkg/webhook/cluster/mutating_webhook.go +++ b/pkg/webhook/cluster/mutating_webhook.go @@ -2,9 +2,13 @@ package cluster import ( "encoding/json" + "fmt" "net/http" + "strings" + "time" clusterv1 "open-cluster-management.io/api/cluster/v1" + "open-cluster-management.io/registration/pkg/helpers" admissionv1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -13,7 +17,13 @@ import ( "k8s.io/klog/v2" ) -const defaultLeaseDurationSecondsPatch = `[{"op": "replace", "path": "/spec/leaseDurationSeconds", "value": 60}]` +var nowFunc = time.Now + +type jsonPatchOperation struct { + Operation string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value,omitempty"` +} // ManagedClusterMutatingAdmissionHook will mutate the creating/updating managedcluster request. type ManagedClusterMutatingAdmissionHook struct{} @@ -58,18 +68,109 @@ func (a *ManagedClusterMutatingAdmissionHook) Admit(req *admissionv1beta1.Admiss return status } - // If LeaseDurationSeconds value is zero, update it to 60 by default - if managedCluster.Spec.LeaseDurationSeconds == 0 { - status.Patch = []byte(defaultLeaseDurationSecondsPatch) - pt := admissionv1beta1.PatchTypeJSONPatch - status.PatchType = &pt + var jsonPatches []jsonPatchOperation + + // set timeAdded of taint if it is nil and reset it if it is modified + taintJsonPatches, status := a.processTaints(managedCluster, req.OldObject.Raw) + if !status.Allowed { + return status + } + jsonPatches = append(jsonPatches, taintJsonPatches...) + + if len(jsonPatches) == 0 { + return status } + patch, err := json.Marshal(jsonPatches) + if err != nil { + status.Allowed = false + status.Result = &metav1.Status{ + Status: metav1.StatusFailure, Code: http.StatusInternalServerError, Reason: metav1.StatusReasonInternalError, + Message: err.Error(), + } + return status + } + + status.Patch = patch + pt := admissionv1beta1.PatchTypeJSONPatch + status.PatchType = &pt return status } +// processTaints generates json patched for cluster taints +func (a *ManagedClusterMutatingAdmissionHook) processTaints(managedCluster *clusterv1.ManagedCluster, oldManagedClusterRaw []byte) ([]jsonPatchOperation, *admissionv1beta1.AdmissionResponse) { + status := &admissionv1beta1.AdmissionResponse{ + Allowed: true, + } + + if len(managedCluster.Spec.Taints) == 0 { + return nil, status + } + + var oldManagedCluster *clusterv1.ManagedCluster + if len(oldManagedClusterRaw) > 0 { + cluster := &clusterv1.ManagedCluster{} + if err := json.Unmarshal(oldManagedClusterRaw, cluster); err != nil { + status.Allowed = false + status.Result = &metav1.Status{ + Status: metav1.StatusFailure, Code: http.StatusInternalServerError, Reason: metav1.StatusReasonInternalError, + Message: err.Error(), + } + return nil, status + } + oldManagedCluster = cluster + } + + var invalidTaints []string + var jsonPatches []jsonPatchOperation + now := metav1.NewTime(nowFunc()) + for index, taint := range managedCluster.Spec.Taints { + originalTaint := helpers.FindTaintByKey(oldManagedCluster, taint.Key) + switch { + case originalTaint == nil: + // new taint + if !taint.TimeAdded.IsZero() { + invalidTaints = append(invalidTaints, taint.Key) + continue + } + jsonPatches = append(jsonPatches, newTaintTimeAddedJsonPatch(index, now.Time)) + case originalTaint.Value == taint.Value && originalTaint.Effect == taint.Effect: + // no change + if !originalTaint.TimeAdded.Equal(&taint.TimeAdded) { + invalidTaints = append(invalidTaints, taint.Key) + } + default: + // taint's value/effect has changed + if !taint.TimeAdded.IsZero() { + invalidTaints = append(invalidTaints, taint.Key) + continue + } + jsonPatches = append(jsonPatches, newTaintTimeAddedJsonPatch(index, now.Time)) + } + } + + if len(invalidTaints) == 0 { + return jsonPatches, status + } + + status.Allowed = false + status.Result = &metav1.Status{ + Status: metav1.StatusFailure, Code: http.StatusBadRequest, Reason: metav1.StatusReasonBadRequest, + Message: fmt.Sprintf("It is not allowed to set TimeAdded of Taint %q.", strings.Join(invalidTaints, ",")), + } + return nil, status +} + // Initialize is called by generic-admission-server on startup to setup initialization that managedclusters webhook needs. func (a *ManagedClusterMutatingAdmissionHook) Initialize(kubeClientConfig *rest.Config, stopCh <-chan struct{}) error { // do nothing return nil } + +func newTaintTimeAddedJsonPatch(index int, timeAdded time.Time) jsonPatchOperation { + return jsonPatchOperation{ + Operation: "replace", + Path: fmt.Sprintf("/spec/taints/%d/timeAdded", index), + Value: timeAdded.UTC().Format(time.RFC3339), + } +} diff --git a/pkg/webhook/cluster/mutating_webhook_test.go b/pkg/webhook/cluster/mutating_webhook_test.go index a71dcccec..debe4fc2b 100644 --- a/pkg/webhook/cluster/mutating_webhook_test.go +++ b/pkg/webhook/cluster/mutating_webhook_test.go @@ -2,8 +2,12 @@ package cluster import ( "encoding/json" + "net/http" "reflect" "testing" + "time" + + clusterv1 "open-cluster-management.io/api/cluster/v1" admissionv1beta1 "k8s.io/api/admission/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -12,7 +16,7 @@ import ( ) func TestManagedClusterMutate(t *testing.T) { - pt := admissionv1beta1.PatchTypeJSONPatch + now := time.Now() cases := []struct { name string request *admissionv1beta1.AdmissionRequest @@ -28,9 +32,7 @@ func TestManagedClusterMutate(t *testing.T) { Resource: "tests", }, }, - expectedResponse: &admissionv1beta1.AdmissionResponse{ - Allowed: true, - }, + expectedResponse: newAdmissionResponse(true).build(), }, { name: "mutate deleting operation", @@ -38,54 +40,193 @@ func TestManagedClusterMutate(t *testing.T) { Resource: managedclustersSchema, Operation: admissionv1beta1.Delete, }, - expectedResponse: &admissionv1beta1.AdmissionResponse{ - Allowed: true, - }, + expectedResponse: newAdmissionResponse(true).build(), }, { - name: "mutate a ManagedCluster without LeaseDurationSeconds setting", + name: "new taints", request: &admissionv1beta1.AdmissionRequest{ Resource: managedclustersSchema, Operation: admissionv1beta1.Create, - Object: newManagedClusterObj(), - }, - expectedResponse: &admissionv1beta1.AdmissionResponse{ - Allowed: true, - Patch: []byte(`[{"op": "replace", "path": "/spec/leaseDurationSeconds", "value": 60}]`), - PatchType: &pt, + Object: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, nil)). + addTaint(newTaint("c", "d", clusterv1.TaintEffectPreferNoSelect, nil)). + build(), }, + expectedResponse: newAdmissionResponse(true). + addJsonPatch(newTaintTimeAddedJsonPatch(0, now)). + addJsonPatch(newTaintTimeAddedJsonPatch(1, now)). + build(), }, { - name: "mutate a ManagedCluster with LeaseDurationSeconds setting", + name: "new taint request denied", request: &admissionv1beta1.AdmissionRequest{ Resource: managedclustersSchema, Operation: admissionv1beta1.Create, - Object: newManagedClusterObjWithLeaseDurationSeconds(60), - }, - expectedResponse: &admissionv1beta1.AdmissionResponse{ - Allowed: true, + Object: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, 0))). + addTaint(newTaint("c", "d", clusterv1.TaintEffectPreferNoSelect, newTime(now, 0))). + build(), }, + expectedResponse: newAdmissionResponse(false). + withResult(metav1.StatusFailure, http.StatusBadRequest, metav1.StatusReasonBadRequest, "It is not allowed to set TimeAdded of Taint \"a,c\"."). + build(), }, + { + name: "update taint", + request: &admissionv1beta1.AdmissionRequest{ + Resource: managedclustersSchema, + Operation: admissionv1beta1.Create, + OldObject: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + addTaint(newTaint("c", "d", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + build(), + Object: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). // no change + addTaint(newTaint("c", "d", clusterv1.TaintEffectNoSelectIfNew, nil)). // effect modified + build(), + }, + expectedResponse: newAdmissionResponse(true). + addJsonPatch(newTaintTimeAddedJsonPatch(1, now)). + build(), + }, + { + name: "taint update request denied", + request: &admissionv1beta1.AdmissionRequest{ + Resource: managedclustersSchema, + Operation: admissionv1beta1.Create, + OldObject: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + addTaint(newTaint("c", "d", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + build(), + Object: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, -20*time.Second))). // timeAdded modified + addTaint(newTaint("c", "d", clusterv1.TaintEffectNoSelectIfNew, newTime(now, -10*time.Second))). // effect modified with timeAdded + build(), + }, + expectedResponse: newAdmissionResponse(false). + withResult(metav1.StatusFailure, http.StatusBadRequest, metav1.StatusReasonBadRequest, "It is not allowed to set TimeAdded of Taint \"a,c\"."). + build(), + }, + { + name: "delete taint", + request: &admissionv1beta1.AdmissionRequest{ + Resource: managedclustersSchema, + Operation: admissionv1beta1.Create, + OldObject: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + addTaint(newTaint("c", "d", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + build(), + Object: newManagedCluster(). + withLeaseDurationSeconds(60). + addTaint(newTaint("a", "b", clusterv1.TaintEffectNoSelect, newTime(now, -10*time.Second))). + build(), + }, + expectedResponse: newAdmissionResponse(true).build(), + }, + } + + nowFunc = func() time.Time { + return now } for _, c := range cases { t.Run(c.name, func(t *testing.T) { admissionHook := &ManagedClusterMutatingAdmissionHook{} - actualResponse := admissionHook.Admit(c.request) - if !reflect.DeepEqual(actualResponse, c.expectedResponse) { - t.Errorf("expected %#v but got: %#v", c.expectedResponse.Result, actualResponse.Result) + t.Errorf("expected \n%#v but got: \n%#v", c.expectedResponse, actualResponse) } }) } } -func newManagedClusterObjWithLeaseDurationSeconds(leaseDurationSeconds int32) runtime.RawExtension { - managedCluster := testinghelpers.NewManagedCluster() - managedCluster.Spec.LeaseDurationSeconds = leaseDurationSeconds - clusterObj, _ := json.Marshal(managedCluster) +type admissionResponseBuilder struct { + jsonPatchOperations []jsonPatchOperation + response admissionv1beta1.AdmissionResponse +} + +func newAdmissionResponse(allowed bool) *admissionResponseBuilder { + return &admissionResponseBuilder{ + response: admissionv1beta1.AdmissionResponse{ + Allowed: allowed, + }, + } +} + +func (b *admissionResponseBuilder) addJsonPatch(jsonPatch jsonPatchOperation) *admissionResponseBuilder { + b.jsonPatchOperations = append(b.jsonPatchOperations, jsonPatch) + pt := admissionv1beta1.PatchTypeJSONPatch + b.response.PatchType = &pt + return b +} + +func (b *admissionResponseBuilder) withResult(status string, code int32, reason metav1.StatusReason, message string) *admissionResponseBuilder { + b.response.Result = &metav1.Status{ + Status: status, + Code: code, + Reason: reason, + Message: message, + } + return b +} + +func (b *admissionResponseBuilder) build() *admissionv1beta1.AdmissionResponse { + if len(b.jsonPatchOperations) > 0 { + patch, _ := json.Marshal(b.jsonPatchOperations) + b.response.Patch = patch + } + return &b.response +} + +type managedClusterBuilder struct { + cluster clusterv1.ManagedCluster +} + +func newManagedCluster() *managedClusterBuilder { + return &managedClusterBuilder{ + cluster: *testinghelpers.NewManagedCluster(), + } +} + +func (b *managedClusterBuilder) withLeaseDurationSeconds(leaseDurationSeconds int32) *managedClusterBuilder { + b.cluster.Spec.LeaseDurationSeconds = leaseDurationSeconds + return b +} + +func (b *managedClusterBuilder) addTaint(taint clusterv1.Taint) *managedClusterBuilder { + b.cluster.Spec.Taints = append(b.cluster.Spec.Taints, taint) + return b +} + +func (b *managedClusterBuilder) build() runtime.RawExtension { + clusterObj, _ := json.Marshal(b.cluster) return runtime.RawExtension{ Raw: clusterObj, } } + +func newTaint(key, value string, effect clusterv1.TaintEffect, timeAdded *metav1.Time) clusterv1.Taint { + taint := clusterv1.Taint{ + Key: key, + Value: value, + Effect: effect, + } + + if timeAdded != nil { + taint.TimeAdded = *timeAdded + } + + return taint +} + +func newTime(time time.Time, offset time.Duration) *metav1.Time { + mt := metav1.NewTime(time.Add(offset)) + return &mt +} diff --git a/test/e2e/webhook_test.go b/test/e2e/webhook_test.go index f801a7173..d4fadc049 100644 --- a/test/e2e/webhook_test.go +++ b/test/e2e/webhook_test.go @@ -82,6 +82,64 @@ var _ = ginkgo.Describe("Admission webhook", func() { gomega.Expect(deleteManageClusterAndRelatedNamespace(clusterName)).ToNot(gomega.HaveOccurred()) }) + ginkgo.It("Should have the timeAdded for taints", func() { + clusterName := fmt.Sprintf("webhook-spoke-%s", rand.String(6)) + ginkgo.By(fmt.Sprintf("create a managed cluster %q with taint", clusterName)) + + cluster := newManagedCluster(clusterName, false, validURL) + cluster.Spec.Taints = []clusterv1.Taint{ + { + Key: "a", + Value: "b", + Effect: clusterv1.TaintEffectNoSelect, + }, + } + _, err := clusterClient.ClusterV1().ManagedClusters().Create(context.TODO(), cluster, metav1.CreateOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + + ginkgo.By("check if timeAdded of the taint is set automatically") + managedCluster, err := clusterClient.ClusterV1().ManagedClusters().Get(context.TODO(), clusterName, metav1.GetOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + taint := findTaint(managedCluster.Spec.Taints, "a", "b", clusterv1.TaintEffectNoSelect) + gomega.Expect(taint).ShouldNot(gomega.BeNil()) + gomega.Expect(taint.TimeAdded.IsZero()).To(gomega.BeFalse()) + + ginkgo.By("chang the effect of the taint") + // sleep and make sure the update is performed 1 second later than the creation + time.Sleep(1 * time.Second) + gomega.Eventually(func() error { + managedCluster, err := clusterClient.ClusterV1().ManagedClusters().Get(context.TODO(), clusterName, metav1.GetOptions{}) + if err != nil { + return err + } + for index, taint := range managedCluster.Spec.Taints { + if taint.Key != "a" { + continue + } + if taint.Value != "b" { + continue + } + managedCluster.Spec.Taints[index] = clusterv1.Taint{ + Key: taint.Key, + Value: taint.Value, + Effect: clusterv1.TaintEffectNoSelectIfNew, + } + } + _, err = clusterClient.ClusterV1().ManagedClusters().Update(context.TODO(), managedCluster, metav1.UpdateOptions{}) + return err + }, 60*time.Second, 1*time.Second).Should(gomega.Succeed()) + + ginkgo.By("check if timeAdded of the taint is reset") + managedCluster, err = clusterClient.ClusterV1().ManagedClusters().Get(context.TODO(), clusterName, metav1.GetOptions{}) + gomega.Expect(err).ToNot(gomega.HaveOccurred()) + updatedTaint := findTaint(managedCluster.Spec.Taints, "a", "b", clusterv1.TaintEffectNoSelectIfNew) + gomega.Expect(updatedTaint).ShouldNot(gomega.BeNil()) + gomega.Expect(taint.TimeAdded.Equal(&updatedTaint.TimeAdded)).To(gomega.BeFalse(), + "timeAdded of taint should be updated (before=%s, after=%s)", taint.TimeAdded.Time.String(), updatedTaint.TimeAdded.Time.String()) + + gomega.Expect(deleteManageClusterAndRelatedNamespace(clusterName)).ToNot(gomega.HaveOccurred()) + }) + ginkgo.It("Should respond bad request when creating a managed cluster with invalid external server URLs", func() { clusterName := fmt.Sprintf("webhook-spoke-%s", rand.String(6)) ginkgo.By(fmt.Sprintf("create a managed cluster %q with an invalid external server URL %q", clusterName, invalidURL)) @@ -772,3 +830,23 @@ func cleanupClusterClient(saNamespace, saName string) error { return nil } + +// findTaint returns the first matched taint in the given taint array. Return nil if no taint matched. +func findTaint(taints []clusterv1.Taint, key, value string, effect clusterv1.TaintEffect) *clusterv1.Taint { + for _, taint := range taints { + if len(key) != 0 && taint.Key != key { + continue + } + + if len(value) != 0 && taint.Value != value { + continue + } + + if len(effect) != 0 && taint.Effect != effect { + continue + } + + return &taint + } + return nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 59fb059f8..1a05311b1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1180,7 +1180,7 @@ k8s.io/utils/net k8s.io/utils/path k8s.io/utils/pointer k8s.io/utils/trace -# open-cluster-management.io/api v0.5.1-0.20220104024812-246f7e36d0d2 +# open-cluster-management.io/api v0.5.1-0.20220107041310-9303b90b6ade ## explicit; go 1.17 open-cluster-management.io/api/addon/v1alpha1 open-cluster-management.io/api/client/addon/clientset/versioned diff --git a/vendor/open-cluster-management.io/api/cluster/v1/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml b/vendor/open-cluster-management.io/api/cluster/v1/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml index 2dba57261..4173576f8 100644 --- a/vendor/open-cluster-management.io/api/cluster/v1/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml +++ b/vendor/open-cluster-management.io/api/cluster/v1/0000_00_clusters.open-cluster-management.io_managedclusters.crd.yaml @@ -97,6 +97,7 @@ spec: description: TimeAdded represents the time at which the taint was added. type: string format: date-time + nullable: true value: description: Value is the taint value corresponding to the taint key. type: string diff --git a/vendor/open-cluster-management.io/api/cluster/v1/types.go b/vendor/open-cluster-management.io/api/cluster/v1/types.go index 8927465f1..95bd347b1 100644 --- a/vendor/open-cluster-management.io/api/cluster/v1/types.go +++ b/vendor/open-cluster-management.io/api/cluster/v1/types.go @@ -109,6 +109,7 @@ type Taint struct { // +required Effect TaintEffect `json:"effect"` // TimeAdded represents the time at which the taint was added. + // +nullable // +required TimeAdded metav1.Time `json:"timeAdded"` } diff --git a/vendor/open-cluster-management.io/api/work/v1/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml b/vendor/open-cluster-management.io/api/work/v1/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml index 58d5ba940..31c7394a2 100644 --- a/vendor/open-cluster-management.io/api/work/v1/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml +++ b/vendor/open-cluster-management.io/api/work/v1/0000_00_work.open-cluster-management.io_manifestworks.crd.yaml @@ -295,7 +295,7 @@ spec: integer: description: Integer is the integer value when type is integer. type: integer - format: int32 + format: int64 string: description: String is the string value when when type is string. type: string diff --git a/vendor/open-cluster-management.io/api/work/v1/types.go b/vendor/open-cluster-management.io/api/work/v1/types.go index 0a7e09f2e..0c410a01e 100644 --- a/vendor/open-cluster-management.io/api/work/v1/types.go +++ b/vendor/open-cluster-management.io/api/work/v1/types.go @@ -327,15 +327,15 @@ type FieldValue struct { // Integer is the integer value when type is integer. // +optional - Integer int32 `json:"integer,omitempty"` + Integer *int64 `json:"integer,omitempty"` // String is the string value when when type is string. // +optional - String string `json:"string,omitempty"` + String *string `json:"string,omitempty"` // Boolean is bool value when type is boolean. // +optional - Boolean bool `json:"boolean,omitempty"` + Boolean *bool `json:"boolean,omitempty"` } // +kubebuilder:validation:Enum=Integer;String;Boolean diff --git a/vendor/open-cluster-management.io/api/work/v1/zz_generated.deepcopy.go b/vendor/open-cluster-management.io/api/work/v1/zz_generated.deepcopy.go index 07b3c430f..dd619ea32 100644 --- a/vendor/open-cluster-management.io/api/work/v1/zz_generated.deepcopy.go +++ b/vendor/open-cluster-management.io/api/work/v1/zz_generated.deepcopy.go @@ -170,7 +170,7 @@ func (in *FeedbackRule) DeepCopy() *FeedbackRule { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FeedbackValue) DeepCopyInto(out *FeedbackValue) { *out = *in - out.Value = in.Value + in.Value.DeepCopyInto(&out.Value) return } @@ -187,6 +187,21 @@ func (in *FeedbackValue) DeepCopy() *FeedbackValue { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *FieldValue) DeepCopyInto(out *FieldValue) { *out = *in + if in.Integer != nil { + in, out := &in.Integer, &out.Integer + *out = new(int64) + **out = **in + } + if in.String != nil { + in, out := &in.String, &out.String + *out = new(string) + **out = **in + } + if in.Boolean != nil { + in, out := &in.Boolean, &out.Boolean + *out = new(bool) + **out = **in + } return } @@ -517,7 +532,9 @@ func (in *StatusFeedbackResult) DeepCopyInto(out *StatusFeedbackResult) { if in.Values != nil { in, out := &in.Values, &out.Values *out = make([]FeedbackValue, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return }