package webhook import ( "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/uuid" configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1" ) // CreateResourceInterpreterContext returns the unique request uid, the ResourceInterpreterContext object to send the webhook, // or an error if the webhook does not support receiving any of the versions we know to send. func CreateResourceInterpreterContext(versions []string, attributes *RequestAttributes) (uid types.UID, request runtime.Object, err error) { for _, version := range versions { switch version { case configv1alpha1.GroupVersion.Version: uid = uuid.NewUUID() request = CreateV1alpha1ResourceInterpreterContext(uid, attributes) return } } err = fmt.Errorf("webhook does not accept known ResourceInterpreterContext versions (v1alpha1)") return } // CreateV1alpha1ResourceInterpreterContext creates an ResourceInterpreterContext for the provided RequestAttributes. func CreateV1alpha1ResourceInterpreterContext(uid types.UID, attributes *RequestAttributes) *configv1alpha1.ResourceInterpreterContext { r := &configv1alpha1.ResourceInterpreterContext{ Request: &configv1alpha1.ResourceInterpreterRequest{ UID: uid, Kind: metav1.GroupVersionKind{ Group: attributes.Object.GroupVersionKind().Group, Version: attributes.Object.GroupVersionKind().Version, Kind: attributes.Object.GetKind(), }, Name: attributes.Object.GetName(), Namespace: attributes.Object.GetNamespace(), Operation: attributes.Operation, Object: runtime.RawExtension{ Object: attributes.Object.DeepCopyObject(), }, DesiredReplicas: &attributes.ReplicasSet, AggregatedStatus: attributes.AggregatedStatus, }, } if attributes.ObservedObj != nil { r.Request.ObservedObject = &runtime.RawExtension{Object: attributes.ObservedObj.DeepCopyObject()} } return r } // VerifyResourceInterpreterContext checks the validity of the provided resourceInterpreterContext, and returns ResponseAttributes, // or an error if the provided resourceInterpreterContext was not valid. func VerifyResourceInterpreterContext(uid types.UID, operation configv1alpha1.InterpreterOperation, interpreterContext runtime.Object) (response *ResponseAttributes, err error) { switch r := interpreterContext.(type) { case *configv1alpha1.ResourceInterpreterContext: if r.Response == nil { return nil, fmt.Errorf("webhook resonse was absent") } if r.Response.UID != uid { return nil, fmt.Errorf("expected response.uid %q, got %q", uid, r.Response.UID) } res, err := verifyResourceInterpreterContext(operation, r.Response) if err != nil { return nil, err } return res, nil default: return nil, fmt.Errorf("unexpected response type %T", interpreterContext) } } func verifyResourceInterpreterContext(operation configv1alpha1.InterpreterOperation, response *configv1alpha1.ResourceInterpreterResponse) (*ResponseAttributes, error) { res := &ResponseAttributes{} res.Successful = response.Successful if !response.Successful { if response.Status == nil { return nil, fmt.Errorf("webhook require response.status when response.successful is false") } res.Status = *response.Status return res, nil } switch operation { case configv1alpha1.InterpreterOperationInterpretReplica: if response.Replicas == nil { return nil, fmt.Errorf("webhook returned nil response.replicas") } res.Replicas = *response.Replicas res.ReplicaRequirements = response.ReplicaRequirements return res, nil case configv1alpha1.InterpreterOperationInterpretDependency: res.Dependencies = response.Dependencies return res, nil case configv1alpha1.InterpreterOperationPrune, configv1alpha1.InterpreterOperationReviseReplica, configv1alpha1.InterpreterOperationRetain, configv1alpha1.InterpreterOperationAggregateStatus: err := verifyResourceInterpreterContextWithPatch(response) if err != nil { return nil, err } res.Patch = response.Patch if response.PatchType != nil { res.PatchType = *response.PatchType } return res, nil case configv1alpha1.InterpreterOperationInterpretStatus: if response.RawStatus == nil { return nil, fmt.Errorf("webhook returned nill response.rawStatus") } res.RawStatus = *response.RawStatus return res, nil case configv1alpha1.InterpreterOperationInterpretHealth: if response.Healthy == nil { return nil, fmt.Errorf("webhook returned nil response.healthy") } res.Healthy = *response.Healthy return res, nil default: return nil, fmt.Errorf("input wrong operation type: %s", operation) } } func verifyResourceInterpreterContextWithPatch(response *configv1alpha1.ResourceInterpreterResponse) error { if len(response.Patch) == 0 && response.PatchType == nil { return nil } if response.PatchType == nil { return fmt.Errorf("webhook returned nil response.patchType") } if len(response.Patch) == 0 { return fmt.Errorf("webhook returned empty response.patch") } patchType := *response.PatchType if patchType != configv1alpha1.PatchTypeJSONPatch { return fmt.Errorf("webhook returned invalid response.patchType of %q", patchType) } return nil }