karmada/pkg/resourceinterpreter/customizedinterpreter/webhook/resourceinterpretercontext.go

147 lines
5.1 KiB
Go

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
}