package defaultinterpreter import ( "fmt" batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "github.com/karmada-io/karmada/pkg/util" "github.com/karmada-io/karmada/pkg/util/helper" "github.com/karmada-io/karmada/pkg/util/lifted" ) // retentionInterpreter is the function that retains values from "observed" object. type retentionInterpreter func(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, err error) func getAllDefaultRetentionInterpreter() map[schema.GroupVersionKind]retentionInterpreter { s := make(map[schema.GroupVersionKind]retentionInterpreter) s[corev1.SchemeGroupVersion.WithKind(util.PodKind)] = retainPodFields s[corev1.SchemeGroupVersion.WithKind(util.ServiceKind)] = lifted.RetainServiceFields s[corev1.SchemeGroupVersion.WithKind(util.ServiceAccountKind)] = lifted.RetainServiceAccountFields s[corev1.SchemeGroupVersion.WithKind(util.PersistentVolumeClaimKind)] = retainPersistentVolumeClaimFields s[batchv1.SchemeGroupVersion.WithKind(util.JobKind)] = retainJobSelectorFields return s } func retainPodFields(desired, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { desiredPod := &corev1.Pod{} err := helper.ConvertToTypedObject(desired, desiredPod) if err != nil { return nil, fmt.Errorf("failed to convert desiredPod from unstructured object: %v", err) } clusterPod := &corev1.Pod{} err = helper.ConvertToTypedObject(observed, clusterPod) if err != nil { return nil, fmt.Errorf("failed to convert clusterPod from unstructured object: %v", err) } desiredPod.Spec.NodeName = clusterPod.Spec.NodeName desiredPod.Spec.ServiceAccountName = clusterPod.Spec.ServiceAccountName desiredPod.Spec.Volumes = clusterPod.Spec.Volumes // retain volumeMounts in each container for _, clusterContainer := range clusterPod.Spec.Containers { for desiredIndex, desiredContainer := range desiredPod.Spec.Containers { if desiredContainer.Name == clusterContainer.Name { desiredPod.Spec.Containers[desiredIndex].VolumeMounts = clusterContainer.VolumeMounts break } } } unstructuredObj, err := helper.ToUnstructured(desiredPod) if err != nil { return nil, fmt.Errorf("failed to transform Pod: %v", err) } return unstructuredObj, nil } func retainPersistentVolumeClaimFields(desired, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { // volumeName is allocated by member cluster and unchangeable, so it should be retained while updating volumeName, ok, err := unstructured.NestedString(observed.Object, "spec", "volumeName") if err != nil { return nil, fmt.Errorf("error retrieving volumeName from pvc: %w", err) } if ok && len(volumeName) > 0 { if err = unstructured.SetNestedField(desired.Object, volumeName, "spec", "volumeName"); err != nil { return nil, fmt.Errorf("error setting volumeName for pvc: %w", err) } } return desired, nil } func retainJobSelectorFields(desired, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { matchLabels, exist, err := unstructured.NestedStringMap(observed.Object, "spec", "selector", "matchLabels") if err != nil { return nil, err } if exist { err = unstructured.SetNestedStringMap(desired.Object, matchLabels, "spec", "selector", "matchLabels") if err != nil { return nil, err } } templateLabels, exist, err := unstructured.NestedStringMap(observed.Object, "spec", "template", "metadata", "labels") if err != nil { return nil, err } if exist { err = unstructured.SetNestedStringMap(desired.Object, templateLabels, "spec", "template", "metadata", "labels") if err != nil { return nil, err } } return desired, nil }