diff --git a/artifacts/deploy/webhook-configuration.yaml b/artifacts/deploy/webhook-configuration.yaml index c8e1e212f28d..39dd8e02e916 100644 --- a/artifacts/deploy/webhook-configuration.yaml +++ b/artifacts/deploy/webhook-configuration.yaml @@ -47,6 +47,34 @@ webhooks: sideEffects: None admissionReviewVersions: ["v1"] timeoutSeconds: 3 + - name: resourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["resourcebindings"] + scope: "Namespaced" + clientConfig: + url: https://karmada-webhook.karmada-system.svc:443/mutate-resourcebinding + caBundle: {{caBundle}} + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 + - name: clusterresourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["clusterresourcebindings"] + scope: "Cluster" + clientConfig: + url: https://karmada-webhook.karmada-system.svc:443/mutate-clusterresourcebinding + caBundle: {{caBundle}} + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 - name: work.karmada.io rules: - operations: ["CREATE", "UPDATE"] diff --git a/charts/karmada/templates/_karmada_webhook_configuration.tpl b/charts/karmada/templates/_karmada_webhook_configuration.tpl index cd583cc61b1b..218ac0133275 100644 --- a/charts/karmada/templates/_karmada_webhook_configuration.tpl +++ b/charts/karmada/templates/_karmada_webhook_configuration.tpl @@ -52,6 +52,34 @@ webhooks: sideEffects: None admissionReviewVersions: ["v1"] timeoutSeconds: 3 + - name: resourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["resourcebindings"] + scope: "Namespaced" + clientConfig: + url: https://{{ $name }}-webhook.{{ $namespace }}.svc:443/mutate-resourcebinding + {{- include "karmada.webhook.caBundle" . | nindent 6 }} + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 + - name: clusterresourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["clusterresourcebindings"] + scope: "Namespaced" + clientConfig: + url: https://{{ $name }}-webhook.{{ $namespace }}.svc:443/mutate-clusterresourcebinding + {{- include "karmada.webhook.caBundle" . | nindent 6 }} + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 - name: work.karmada.io rules: - operations: ["CREATE", "UPDATE"] diff --git a/cmd/webhook/app/webhook.go b/cmd/webhook/app/webhook.go index 8830e2521431..96dae8d4483a 100644 --- a/cmd/webhook/app/webhook.go +++ b/cmd/webhook/app/webhook.go @@ -43,6 +43,7 @@ import ( "github.com/karmada-io/karmada/pkg/version/sharedcommand" "github.com/karmada-io/karmada/pkg/webhook/clusteroverridepolicy" "github.com/karmada-io/karmada/pkg/webhook/clusterpropagationpolicy" + "github.com/karmada-io/karmada/pkg/webhook/clusterresourcebinding" "github.com/karmada-io/karmada/pkg/webhook/configuration" "github.com/karmada-io/karmada/pkg/webhook/cronfederatedhpa" "github.com/karmada-io/karmada/pkg/webhook/federatedhpa" @@ -51,6 +52,7 @@ import ( "github.com/karmada-io/karmada/pkg/webhook/multiclusterservice" "github.com/karmada-io/karmada/pkg/webhook/overridepolicy" "github.com/karmada-io/karmada/pkg/webhook/propagationpolicy" + "github.com/karmada-io/karmada/pkg/webhook/resourcebinding" "github.com/karmada-io/karmada/pkg/webhook/resourcedeletionprotection" "github.com/karmada-io/karmada/pkg/webhook/resourceinterpretercustomization" "github.com/karmada-io/karmada/pkg/webhook/work" @@ -176,6 +178,8 @@ func Run(ctx context.Context, opts *options.Options) error { hookServer.Register("/mutate-multiclusterservice", &webhook.Admission{Handler: &multiclusterservice.MutatingAdmission{Decoder: decoder}}) hookServer.Register("/mutate-federatedhpa", &webhook.Admission{Handler: &federatedhpa.MutatingAdmission{Decoder: decoder}}) hookServer.Register("/validate-resourcedeletionprotection", &webhook.Admission{Handler: &resourcedeletionprotection.ValidatingAdmission{Decoder: decoder}}) + hookServer.Register("/mutate-resourcebinding", &webhook.Admission{Handler: &resourcebinding.MutatingAdmission{Decoder: decoder}}) + hookServer.Register("/mutate-clusterresourcebinding", &webhook.Admission{Handler: &clusterresourcebinding.MutatingAdmission{Decoder: decoder}}) hookServer.WebhookMux().Handle("/readyz/", http.StripPrefix("/readyz/", &healthz.Handler{})) // blocks until the context is done. diff --git a/operator/pkg/karmadaresource/webhookconfiguration/manifests.go b/operator/pkg/karmadaresource/webhookconfiguration/manifests.go index ca9fbcd5452f..c886e66330b1 100644 --- a/operator/pkg/karmadaresource/webhookconfiguration/manifests.go +++ b/operator/pkg/karmadaresource/webhookconfiguration/manifests.go @@ -68,6 +68,34 @@ webhooks: sideEffects: None admissionReviewVersions: ["v1"] timeoutSeconds: 3 + - name: resourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["resourcebindings"] + scope: "Namespaced" + clientConfig: + url: https://{{ .Service }}.{{ .Namespace }}.svc:443/mutate-resourcebinding + caBundle: {{ .CaBundle }} + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 + - name: clusterresourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["clusterresourcebindings"] + scope: "Cluster" + clientConfig: + url: https://{{ .Service }}.{{ .Namespace }}.svc:443/mutate-clusterresourcebinding + caBundle: {{ .CaBundle }} + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 - name: work.karmada.io rules: - operations: ["CREATE", "UPDATE"] diff --git a/pkg/karmadactl/cmdinit/karmada/webhook_configuration.go b/pkg/karmadactl/cmdinit/karmada/webhook_configuration.go index 7255ec7f1a7d..12c6f129f6f7 100644 --- a/pkg/karmadactl/cmdinit/karmada/webhook_configuration.go +++ b/pkg/karmadactl/cmdinit/karmada/webhook_configuration.go @@ -80,6 +80,34 @@ webhooks: sideEffects: None admissionReviewVersions: ["v1"] timeoutSeconds: 3 + - name: resourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["resourcebindings"] + scope: "Namespaced" + clientConfig: + url: https://karmada-webhook.%[1]s.svc:443/mutate-resourcebinding + caBundle: %[2]s + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 + - name: clusterresourcebinding.karmada.io + rules: + - operations: ["CREATE"] + apiGroups: ["work.karmada.io"] + apiVersions: ["*"] + resources: ["clusterresourcebindings"] + scope: "Cluster" + clientConfig: + url: https://karmada-webhook.%[1]s.svc:443/mutate-clusterresourcebinding + caBundle: %[2]s + failurePolicy: Fail + sideEffects: None + admissionReviewVersions: ["v1"] + timeoutSeconds: 3 - name: work.karmada.io rules: - operations: ["CREATE", "UPDATE"] diff --git a/pkg/util/label.go b/pkg/util/label.go index e626de7440a2..4ad7580ab437 100644 --- a/pkg/util/label.go +++ b/pkg/util/label.go @@ -65,7 +65,7 @@ func RetainLabels(desired *unstructured.Unstructured, observed *unstructured.Uns } // MergeLabel adds label for the given object, replace the value if key exist. -func MergeLabel(obj *unstructured.Unstructured, labelKey string, labelValue string) { +func MergeLabel(obj metav1.Object, labelKey string, labelValue string) { labels := obj.GetLabels() if labels == nil { labels = make(map[string]string, 1) diff --git a/pkg/webhook/clusterpropagationpolicy/mutating.go b/pkg/webhook/clusterpropagationpolicy/mutating.go index 20d1933a657f..bcc65bdcd296 100644 --- a/pkg/webhook/clusterpropagationpolicy/mutating.go +++ b/pkg/webhook/clusterpropagationpolicy/mutating.go @@ -22,9 +22,11 @@ import ( "fmt" "net/http" + "github.com/google/uuid" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" + "github.com/karmada-io/karmada/pkg/util" "github.com/karmada-io/karmada/pkg/util/helper" "github.com/karmada-io/karmada/pkg/util/validation" ) @@ -80,6 +82,10 @@ func (a *MutatingAdmission) Handle(_ context.Context, req admission.Request) adm } } + if util.GetLabelValue(policy.Labels, policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel) == "" { + util.MergeLabel(policy, policyv1alpha1.ClusterPropagationPolicyPermanentIDLabel, uuid.New().String()) + } + marshaledBytes, err := json.Marshal(policy) if err != nil { return admission.Errored(http.StatusInternalServerError, err) diff --git a/pkg/webhook/clusterresourcebinding/mutating.go b/pkg/webhook/clusterresourcebinding/mutating.go new file mode 100644 index 000000000000..91b3cc0cce51 --- /dev/null +++ b/pkg/webhook/clusterresourcebinding/mutating.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 The Karmada Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package clusterresourcebinding + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/google/uuid" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" + "github.com/karmada-io/karmada/pkg/util" +) + +// MutatingAdmission mutates API request if necessary. +type MutatingAdmission struct { + Decoder *admission.Decoder +} + +// Check if our MutatingAdmission implements necessary interface +var _ admission.Handler = &MutatingAdmission{} + +// Handle yields a response to an AdmissionRequest. +func (a *MutatingAdmission) Handle(_ context.Context, req admission.Request) admission.Response { + crb := &workv1alpha2.ClusterResourceBinding{} + + err := a.Decoder.Decode(req, crb) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + klog.V(2).Infof("Mutating clusterResourceBinding(%s) for request: %s", crb.Name, req.Operation) + + if util.GetLabelValue(crb.Labels, workv1alpha2.ClusterResourceBindingPermanentIDLabel) == "" { + util.MergeLabel(crb, workv1alpha2.ClusterResourceBindingPermanentIDLabel, uuid.New().String()) + } + + marshaledBytes, err := json.Marshal(crb) + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledBytes) +} diff --git a/pkg/webhook/propagationpolicy/mutating.go b/pkg/webhook/propagationpolicy/mutating.go index 7bd5879a63a3..c9742c998b05 100644 --- a/pkg/webhook/propagationpolicy/mutating.go +++ b/pkg/webhook/propagationpolicy/mutating.go @@ -22,10 +22,12 @@ import ( "fmt" "net/http" + "github.com/google/uuid" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1" + "github.com/karmada-io/karmada/pkg/util" "github.com/karmada-io/karmada/pkg/util/helper" "github.com/karmada-io/karmada/pkg/util/validation" ) @@ -92,6 +94,10 @@ func (a *MutatingAdmission) Handle(_ context.Context, req admission.Request) adm } } + if util.GetLabelValue(policy.Labels, policyv1alpha1.PropagationPolicyPermanentIDLabel) == "" { + util.MergeLabel(policy, policyv1alpha1.PropagationPolicyPermanentIDLabel, uuid.New().String()) + } + marshaledBytes, err := json.Marshal(policy) if err != nil { return admission.Errored(http.StatusInternalServerError, err) diff --git a/pkg/webhook/resourcebinding/mutating.go b/pkg/webhook/resourcebinding/mutating.go new file mode 100644 index 000000000000..7e196883aaab --- /dev/null +++ b/pkg/webhook/resourcebinding/mutating.go @@ -0,0 +1,60 @@ +/* +Copyright 2023 The Karmada Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resourcebinding + +import ( + "context" + "encoding/json" + "net/http" + + "github.com/google/uuid" + "k8s.io/klog/v2" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" + + workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" + "github.com/karmada-io/karmada/pkg/util" +) + +// MutatingAdmission mutates API request if necessary. +type MutatingAdmission struct { + Decoder *admission.Decoder +} + +// Check if our MutatingAdmission implements necessary interface +var _ admission.Handler = &MutatingAdmission{} + +// Handle yields a response to an AdmissionRequest. +func (a *MutatingAdmission) Handle(_ context.Context, req admission.Request) admission.Response { + rb := &workv1alpha2.ResourceBinding{} + + err := a.Decoder.Decode(req, rb) + if err != nil { + return admission.Errored(http.StatusBadRequest, err) + } + klog.V(2).Infof("Mutating resourceBinding(%s/%s) for request: %s", rb.Namespace, rb.Name, req.Operation) + + if util.GetLabelValue(rb.Labels, workv1alpha2.ResourceBindingPermanentIDLabel) == "" { + util.MergeLabel(rb, workv1alpha2.ResourceBindingPermanentIDLabel, uuid.New().String()) + } + + marshaledBytes, err := json.Marshal(rb) + if err != nil { + return admission.Errored(http.StatusInternalServerError, err) + } + + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledBytes) +} diff --git a/pkg/webhook/work/mutating.go b/pkg/webhook/work/mutating.go index 01ecb719679d..3ad28fa4c42f 100644 --- a/pkg/webhook/work/mutating.go +++ b/pkg/webhook/work/mutating.go @@ -21,13 +21,16 @@ import ( "encoding/json" "net/http" + "github.com/google/uuid" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog/v2" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" workv1alpha1 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha1" + workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" "github.com/karmada-io/karmada/pkg/resourceinterpreter/default/native/prune" + "github.com/karmada-io/karmada/pkg/util" ) // MutatingAdmission mutates API request if necessary. @@ -78,5 +81,9 @@ func (a *MutatingAdmission) Handle(_ context.Context, req admission.Request) adm return admission.Errored(http.StatusInternalServerError, err) } + if util.GetLabelValue(work.Labels, workv1alpha2.WorkPermanentIDLabel) == "" { + util.MergeLabel(work, workv1alpha2.WorkPermanentIDLabel, uuid.New().String()) + } + return admission.PatchResponseFromRaw(req.Object.Raw, marshaledBytes) }