karmada/pkg/resourceinterpreter/customized/webhook/request/resourceinterpretercontext.go

163 lines
5.7 KiB
Go

/*
Copyright 2021 The Karmada Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package request
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 *Attributes) (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 *Attributes) *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 response 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
}