diff --git a/pkg/resourceinterpreter/prune/prune.go b/pkg/resourceinterpreter/prune/prune.go new file mode 100644 index 000000000..b21d84d98 --- /dev/null +++ b/pkg/resourceinterpreter/prune/prune.go @@ -0,0 +1,121 @@ +package prune + +import ( + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + "github.com/karmada-io/karmada/pkg/util" + "github.com/karmada-io/karmada/pkg/util/helper" +) + +// RemoveIrrelevantField used to remove fields that generated by kube-apiserver and no need(or can't) propagate to +// member clusters. +func RemoveIrrelevantField(workload *unstructured.Unstructured) error { + // populated by the kubernetes. + unstructured.RemoveNestedField(workload.Object, "metadata", "creationTimestamp") + + // populated by the kubernetes. + // The kubernetes will set this fields in case of graceful deletion. This field is read-only and can't propagate to + // member clusters. + unstructured.RemoveNestedField(workload.Object, "metadata", "deletionTimestamp") + + // populated by the kubernetes. + // The kubernetes will set this fields in case of graceful deletion. This field is read-only and can't propagate to + // member clusters. + unstructured.RemoveNestedField(workload.Object, "metadata", "deletionGracePeriodSeconds") + + // populated by the kubernetes. + unstructured.RemoveNestedField(workload.Object, "metadata", "generation") + + // This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. + // Remove this field to keep 'Work' clean and tidy. + unstructured.RemoveNestedField(workload.Object, "metadata", "managedFields") + + // populated by the kubernetes. + unstructured.RemoveNestedField(workload.Object, "metadata", "resourceVersion") + + // populated by the kubernetes and has been deprecated by kubernetes. + unstructured.RemoveNestedField(workload.Object, "metadata", "selfLink") + + // populated by the kubernetes. + unstructured.RemoveNestedField(workload.Object, "metadata", "uid") + + unstructured.RemoveNestedField(workload.Object, "metadata", "ownerReferences") + + unstructured.RemoveNestedField(workload.Object, "status") + + if workload.GetKind() == util.ServiceKind { + // In the case spec.clusterIP is set to `None`, means user want a headless service, then it shouldn't be removed. + clusterIP, exist, _ := unstructured.NestedString(workload.Object, "spec", "clusterIP") + if exist && clusterIP != corev1.ClusterIPNone { + unstructured.RemoveNestedField(workload.Object, "spec", "clusterIP") + unstructured.RemoveNestedField(workload.Object, "spec", "clusterIPs") + } + } + + if workload.GetKind() == util.JobKind { + job, err := helper.ConvertToJob(workload) + if err != nil { + return err + } + + if job.Spec.ManualSelector == nil || !*job.Spec.ManualSelector { + return removeGenerateSelectorOfJob(workload) + } + } + + if workload.GetKind() == util.ServiceAccountKind { + secrets, exist, _ := unstructured.NestedSlice(workload.Object, "secrets") + // If 'secrets' exists in ServiceAccount, remove the automatic generation secrets(e.g. default-token-xxx) + if exist && len(secrets) > 0 { + tokenPrefix := fmt.Sprintf("%s-token-", workload.GetName()) + for idx := 0; idx < len(secrets); idx++ { + if strings.HasPrefix(secrets[idx].(map[string]interface{})["name"].(string), tokenPrefix) { + secrets = append(secrets[:idx], secrets[idx+1:]...) + } + } + _ = unstructured.SetNestedSlice(workload.Object, secrets, "secrets") + } + } + + return nil +} + +func removeGenerateSelectorOfJob(workload *unstructured.Unstructured) error { + matchLabels, exist, err := unstructured.NestedStringMap(workload.Object, "spec", "selector", "matchLabels") + if err != nil { + return err + } + if exist { + if util.GetLabelValue(matchLabels, "controller-uid") != "" { + delete(matchLabels, "controller-uid") + } + err = unstructured.SetNestedStringMap(workload.Object, matchLabels, "spec", "selector", "matchLabels") + if err != nil { + return err + } + } + + templateLabels, exist, err := unstructured.NestedStringMap(workload.Object, "spec", "template", "metadata", "labels") + if err != nil { + return err + } + if exist { + if util.GetLabelValue(templateLabels, "controller-uid") != "" { + delete(templateLabels, "controller-uid") + } + + if util.GetLabelValue(templateLabels, "job-name") != "" { + delete(templateLabels, "job-name") + } + + err = unstructured.SetNestedStringMap(workload.Object, templateLabels, "spec", "template", "metadata", "labels") + if err != nil { + return err + } + } + return nil +} diff --git a/pkg/webhook/work/mutating.go b/pkg/webhook/work/mutating.go index cf5d9f2ce..cc09b666e 100644 --- a/pkg/webhook/work/mutating.go +++ b/pkg/webhook/work/mutating.go @@ -3,19 +3,15 @@ package work import ( "context" "encoding/json" - "fmt" "net/http" - "strings" - corev1 "k8s.io/api/core/v1" "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" - "github.com/karmada-io/karmada/pkg/util" - "github.com/karmada-io/karmada/pkg/util/helper" + "github.com/karmada-io/karmada/pkg/resourceinterpreter/prune" ) // MutatingAdmission mutates API request if necessary. @@ -47,7 +43,7 @@ func (a *MutatingAdmission) Handle(ctx context.Context, req admission.Request) a return admission.Errored(http.StatusInternalServerError, err) } - err = removeIrrelevantField(workloadObj) + err = prune.RemoveIrrelevantField(workloadObj) if err != nil { klog.Errorf("Failed to remove irrelevant field for work(%s): %v", work.Name, err) return admission.Errored(http.StatusInternalServerError, err) @@ -76,112 +72,3 @@ func (a *MutatingAdmission) InjectDecoder(d *admission.Decoder) error { a.decoder = d return nil } - -// removeIrrelevantField used to remove fields that generated by kube-apiserver and no need(or can't) propagate to -// member clusters. -func removeIrrelevantField(workload *unstructured.Unstructured) error { - // populated by the kubernetes. - unstructured.RemoveNestedField(workload.Object, "metadata", "creationTimestamp") - - // populated by the kubernetes. - // The kubernetes will set this fields in case of graceful deletion. This field is read-only and can't propagate to - // member clusters. - unstructured.RemoveNestedField(workload.Object, "metadata", "deletionTimestamp") - - // populated by the kubernetes. - // The kubernetes will set this fields in case of graceful deletion. This field is read-only and can't propagate to - // member clusters. - unstructured.RemoveNestedField(workload.Object, "metadata", "deletionGracePeriodSeconds") - - // populated by the kubernetes. - unstructured.RemoveNestedField(workload.Object, "metadata", "generation") - - // This is mostly for internal housekeeping, and users typically shouldn't need to set or understand this field. - // Remove this field to keep 'Work' clean and tidy. - unstructured.RemoveNestedField(workload.Object, "metadata", "managedFields") - - // populated by the kubernetes. - unstructured.RemoveNestedField(workload.Object, "metadata", "resourceVersion") - - // populated by the kubernetes and has been deprecated by kubernetes. - unstructured.RemoveNestedField(workload.Object, "metadata", "selfLink") - - // populated by the kubernetes. - unstructured.RemoveNestedField(workload.Object, "metadata", "uid") - - unstructured.RemoveNestedField(workload.Object, "metadata", "ownerReferences") - - unstructured.RemoveNestedField(workload.Object, "status") - - if workload.GetKind() == util.ServiceKind { - // In the case spec.clusterIP is set to `None`, means user want a headless service, then it shouldn't be removed. - clusterIP, exist, _ := unstructured.NestedString(workload.Object, "spec", "clusterIP") - if exist && clusterIP != corev1.ClusterIPNone { - unstructured.RemoveNestedField(workload.Object, "spec", "clusterIP") - unstructured.RemoveNestedField(workload.Object, "spec", "clusterIPs") - } - } - - if workload.GetKind() == util.JobKind { - job, err := helper.ConvertToJob(workload) - if err != nil { - return err - } - - if job.Spec.ManualSelector == nil || !*job.Spec.ManualSelector { - return removeGenerateSelectorOfJob(workload) - } - } - - if workload.GetKind() == util.ServiceAccountKind { - secrets, exist, _ := unstructured.NestedSlice(workload.Object, "secrets") - // If 'secrets' exists in ServiceAccount, remove the automatic generation secrets(e.g. default-token-xxx) - if exist && len(secrets) > 0 { - tokenPrefix := fmt.Sprintf("%s-token-", workload.GetName()) - for idx := 0; idx < len(secrets); idx++ { - if strings.HasPrefix(secrets[idx].(map[string]interface{})["name"].(string), tokenPrefix) { - secrets = append(secrets[:idx], secrets[idx+1:]...) - } - } - _ = unstructured.SetNestedSlice(workload.Object, secrets, "secrets") - } - } - - return nil -} - -func removeGenerateSelectorOfJob(workload *unstructured.Unstructured) error { - matchLabels, exist, err := unstructured.NestedStringMap(workload.Object, "spec", "selector", "matchLabels") - if err != nil { - return err - } - if exist { - if util.GetLabelValue(matchLabels, "controller-uid") != "" { - delete(matchLabels, "controller-uid") - } - err = unstructured.SetNestedStringMap(workload.Object, matchLabels, "spec", "selector", "matchLabels") - if err != nil { - return err - } - } - - templateLabels, exist, err := unstructured.NestedStringMap(workload.Object, "spec", "template", "metadata", "labels") - if err != nil { - return err - } - if exist { - if util.GetLabelValue(templateLabels, "controller-uid") != "" { - delete(templateLabels, "controller-uid") - } - - if util.GetLabelValue(templateLabels, "job-name") != "" { - delete(templateLabels, "job-name") - } - - err = unstructured.SetNestedStringMap(workload.Object, templateLabels, "spec", "template", "metadata", "labels") - if err != nil { - return err - } - } - return nil -}