168 lines
5.8 KiB
Go
168 lines
5.8 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"
|
|
"github.com/karmada-io/karmada/pkg/util/interpreter/validation"
|
|
)
|
|
|
|
// 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:
|
|
err := validation.VerifyDependencies(response.Dependencies)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
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
|
|
}
|