147 lines
5.1 KiB
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
|
|
}
|