Add webhook for mutating workload object of work resource (#206)

Signed-off-by: changzhen <changzhen5@huawei.com>
This commit is contained in:
Zhen Chang 2021-03-10 17:46:58 +08:00 committed by GitHub
parent 5abf32099f
commit 1b0aa972a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 97 additions and 30 deletions

View File

@ -47,6 +47,20 @@ webhooks:
sideEffects: None
admissionReviewVersions: ["v1beta1"]
timeoutSeconds: 3
- name: work.karmada.io
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: ["work.karmada.io"]
apiVersions: ["*"]
resources: ["works"]
scope: "Namespaced"
clientConfig:
url: https://karmada-webhook.karmada-system.svc:443/mutate-work
caBundle: {{caBundle}}
failurePolicy: Fail
sideEffects: None
admissionReviewVersions: ["v1beta1"]
timeoutSeconds: 3
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration

View File

@ -19,6 +19,7 @@ import (
"github.com/karmada-io/karmada/pkg/webhook/clusterpropagationpolicy"
"github.com/karmada-io/karmada/pkg/webhook/overridepolicy"
"github.com/karmada-io/karmada/pkg/webhook/propagationpolicy"
"github.com/karmada-io/karmada/pkg/webhook/work"
)
// NewWebhookCommand creates a *cobra.Command object with default parameters
@ -73,6 +74,7 @@ func Run(opts *options.Options, stopChan <-chan struct{}) error {
hookServer.Register("/mutate-clusterpropagationpolicy", &webhook.Admission{Handler: &clusterpropagationpolicy.MutatingAdmission{}})
hookServer.Register("/validate-clusterpropagationpolicy", &webhook.Admission{Handler: &clusterpropagationpolicy.ValidatingAdmission{}})
hookServer.Register("/mutate-overridepolicy", &webhook.Admission{Handler: &overridepolicy.MutatingAdmission{}})
hookServer.Register("/mutate-work", &webhook.Admission{Handler: &work.MutatingAdmission{}})
hookServer.WebhookMux.Handle("/readyz/", http.StripPrefix("/readyz/", &healthz.Handler{}))
// blocks until the stop channel is closed.

View File

@ -155,17 +155,6 @@ func (c *ResourceBindingController) getBindingClusterNames(binding *workv1alpha1
return clusterNames
}
// removeIrrelevantField will delete irrelevant field from workload. such as uid, timestamp, status
func (c *ResourceBindingController) removeIrrelevantField(workload *unstructured.Unstructured) {
unstructured.RemoveNestedField(workload.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(workload.Object, "metadata", "generation")
unstructured.RemoveNestedField(workload.Object, "metadata", "resourceVersion")
unstructured.RemoveNestedField(workload.Object, "metadata", "selfLink")
unstructured.RemoveNestedField(workload.Object, "metadata", "managedFields")
unstructured.RemoveNestedField(workload.Object, "metadata", "uid")
unstructured.RemoveNestedField(workload.Object, "status")
}
// transformBindingToWorks will transform propagationBinding to Works
func (c *ResourceBindingController) transformBindingToWorks(binding *workv1alpha1.ResourceBinding, clusterNames []string) error {
dynamicResource, err := restmapper.GetGroupVersionResource(c.RESTMapper,
@ -193,7 +182,6 @@ func (c *ResourceBindingController) transformBindingToWorks(binding *workv1alpha
// ensureWork ensure Work to be created or updated
func (c *ResourceBindingController) ensureWork(workload *unstructured.Unstructured, clusterNames []string,
binding *workv1alpha1.ResourceBinding) error {
c.removeIrrelevantField(workload)
for _, clusterName := range clusterNames {
// apply override policies

View File

@ -81,7 +81,6 @@ func (c *HorizontalPodAutoscalerController) buildWorks(hpa *autoscalingv1.Horizo
return nil
}
hpaObj := &unstructured.Unstructured{Object: uncastObj}
util.RemoveIrrelevantField(hpaObj)
for _, clusterName := range clusters {
workNamespace, err := names.GenerateExecutionSpaceName(clusterName)
if err != nil {

View File

@ -97,7 +97,6 @@ func (c *Controller) buildWorks(namespace *v1.Namespace, clusters []v1alpha1.Clu
return nil
}
namespaceObj := &unstructured.Unstructured{Object: uncastObj}
util.RemoveIrrelevantField(namespaceObj)
for _, cluster := range clusters {
workNamespace, err := names.GenerateExecutionSpaceName(cluster.Name)

View File

@ -1,16 +0,0 @@
package util
import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
)
// RemoveIrrelevantField will delete irrelevant field from workload. such as uid, timestamp, status
func RemoveIrrelevantField(workload *unstructured.Unstructured) {
unstructured.RemoveNestedField(workload.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(workload.Object, "metadata", "generation")
unstructured.RemoveNestedField(workload.Object, "metadata", "resourceVersion")
unstructured.RemoveNestedField(workload.Object, "metadata", "selfLink")
unstructured.RemoveNestedField(workload.Object, "metadata", "managedFields")
unstructured.RemoveNestedField(workload.Object, "metadata", "uid")
unstructured.RemoveNestedField(workload.Object, "status")
}

View File

@ -0,0 +1,81 @@
package work
import (
"context"
"encoding/json"
"net/http"
"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"
)
// MutatingAdmission mutates API request if necessary.
type MutatingAdmission struct {
decoder *admission.Decoder
}
// Check if our MutatingAdmission implements necessary interface
var _ admission.Handler = &MutatingAdmission{}
var _ admission.DecoderInjector = &MutatingAdmission{}
// Handle yields a response to an AdmissionRequest.
func (a *MutatingAdmission) Handle(ctx context.Context, req admission.Request) admission.Response {
work := &workv1alpha1.Work{}
err := a.decoder.Decode(req, work)
if err != nil {
return admission.Errored(http.StatusBadRequest, err)
}
klog.V(2).Infof("Mutating work(%s) for request: %s", work.Name, req.Operation)
var manifests []workv1alpha1.Manifest
for _, manifest := range work.Spec.Workload.Manifests {
workloadObj := &unstructured.Unstructured{}
err := json.Unmarshal(manifest.Raw, workloadObj)
if err != nil {
klog.Errorf("Failed to unmarshal work(%s) manifest to Unstructured", work.Name)
return admission.Errored(http.StatusInternalServerError, err)
}
removeIrrelevantField(workloadObj)
workloadJSON, err := workloadObj.MarshalJSON()
if err != nil {
klog.Errorf("Failed to marshal workload of work(%s)", work.Name)
return admission.Errored(http.StatusInternalServerError, err)
}
manifests = append(manifests, workv1alpha1.Manifest{RawExtension: runtime.RawExtension{Raw: workloadJSON}})
}
work.Spec.Workload.Manifests = manifests
marshaledBytes, err := json.Marshal(work)
if err != nil {
return admission.Errored(http.StatusInternalServerError, err)
}
return admission.PatchResponseFromRaw(req.Object.Raw, marshaledBytes)
}
// InjectDecoder implements admission.DecoderInjector interface.
// A decoder will be automatically injected.
func (a *MutatingAdmission) InjectDecoder(d *admission.Decoder) error {
a.decoder = d
return nil
}
// RemoveIrrelevantField will delete irrelevant field from workload. such as uid, timestamp, status
func removeIrrelevantField(workload *unstructured.Unstructured) {
unstructured.RemoveNestedField(workload.Object, "metadata", "creationTimestamp")
unstructured.RemoveNestedField(workload.Object, "metadata", "generation")
unstructured.RemoveNestedField(workload.Object, "metadata", "resourceVersion")
unstructured.RemoveNestedField(workload.Object, "metadata", "selfLink")
unstructured.RemoveNestedField(workload.Object, "metadata", "managedFields")
unstructured.RemoveNestedField(workload.Object, "metadata", "uid")
unstructured.RemoveNestedField(workload.Object, "status")
unstructured.RemoveNestedField(workload.Object, "spec", "clusterIP")
}