112 lines
4.3 KiB
Go
112 lines
4.3 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"k8s.io/klog/v2"
|
|
|
|
workloadv1alpha1 "github.com/karmada-io/karmada/examples/customresourceinterpreter/apis/workload/v1alpha1"
|
|
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
|
"github.com/karmada-io/karmada/pkg/webhook/interpreter"
|
|
)
|
|
|
|
// Check if our workloadInterpreter implements necessary interface
|
|
var _ interpreter.Handler = &workloadInterpreter{}
|
|
var _ interpreter.DecoderInjector = &workloadInterpreter{}
|
|
|
|
// workloadInterpreter explore resource with request operation.
|
|
type workloadInterpreter struct {
|
|
decoder *interpreter.Decoder
|
|
}
|
|
|
|
// Handle implements interpreter.Handler interface.
|
|
// It yields a response to an ExploreRequest.
|
|
func (e *workloadInterpreter) Handle(ctx context.Context, req interpreter.Request) interpreter.Response {
|
|
workload := &workloadv1alpha1.Workload{}
|
|
err := e.decoder.Decode(req, workload)
|
|
if err != nil {
|
|
return interpreter.Errored(http.StatusBadRequest, err)
|
|
}
|
|
klog.Infof("Explore workload(%s/%s) for request: %s", workload.GetNamespace(), workload.GetName(), req.Operation)
|
|
|
|
switch req.Operation {
|
|
case configv1alpha1.InterpreterOperationInterpretReplica:
|
|
return e.responseWithExploreReplica(workload)
|
|
case configv1alpha1.InterpreterOperationReviseReplica:
|
|
return e.responseWithExploreReviseReplica(workload, req)
|
|
case configv1alpha1.InterpreterOperationRetain:
|
|
return e.responseWithExploreRetaining(workload, req)
|
|
case configv1alpha1.InterpreterOperationAggregateStatus:
|
|
return e.responseWithExploreAggregateStatus(workload, req)
|
|
default:
|
|
return interpreter.Errored(http.StatusBadRequest, fmt.Errorf("wrong request operation type: %s", req.Operation))
|
|
}
|
|
}
|
|
|
|
// InjectDecoder implements interpreter.DecoderInjector interface.
|
|
func (e *workloadInterpreter) InjectDecoder(d *interpreter.Decoder) {
|
|
e.decoder = d
|
|
}
|
|
|
|
func (e *workloadInterpreter) responseWithExploreReplica(workload *workloadv1alpha1.Workload) interpreter.Response {
|
|
res := interpreter.Succeeded("")
|
|
res.Replicas = workload.Spec.Replicas
|
|
return res
|
|
}
|
|
|
|
func (e *workloadInterpreter) responseWithExploreReviseReplica(workload *workloadv1alpha1.Workload, req interpreter.Request) interpreter.Response {
|
|
wantedWorkload := workload.DeepCopy()
|
|
wantedWorkload.Spec.Replicas = req.DesiredReplicas
|
|
marshaledBytes, err := json.Marshal(wantedWorkload)
|
|
if err != nil {
|
|
return interpreter.Errored(http.StatusInternalServerError, err)
|
|
}
|
|
return interpreter.PatchResponseFromRaw(req.Object.Raw, marshaledBytes)
|
|
}
|
|
|
|
func (e *workloadInterpreter) responseWithExploreRetaining(desiredWorkload *workloadv1alpha1.Workload, req interpreter.Request) interpreter.Response {
|
|
if req.ObservedObject == nil {
|
|
err := fmt.Errorf("nil observedObject in exploreReview with operation type: %s", req.Operation)
|
|
return interpreter.Errored(http.StatusBadRequest, err)
|
|
}
|
|
observerWorkload := &workloadv1alpha1.Workload{}
|
|
err := e.decoder.DecodeRaw(*req.ObservedObject, observerWorkload)
|
|
if err != nil {
|
|
return interpreter.Errored(http.StatusBadRequest, err)
|
|
}
|
|
|
|
// Suppose we want to retain the `.spec.paused` field of the actual observed workload object in member cluster,
|
|
// and prevent from being overwritten by karmada controller-plane.
|
|
wantedWorkload := desiredWorkload.DeepCopy()
|
|
wantedWorkload.Spec.Paused = observerWorkload.Spec.Paused
|
|
marshaledBytes, err := json.Marshal(wantedWorkload)
|
|
if err != nil {
|
|
return interpreter.Errored(http.StatusInternalServerError, err)
|
|
}
|
|
return interpreter.PatchResponseFromRaw(req.Object.Raw, marshaledBytes)
|
|
}
|
|
|
|
func (e *workloadInterpreter) responseWithExploreAggregateStatus(workload *workloadv1alpha1.Workload, req interpreter.Request) interpreter.Response {
|
|
wantedWorkload := workload.DeepCopy()
|
|
var readyReplicas int32
|
|
for _, item := range req.AggregatedStatus {
|
|
if item.Status == nil {
|
|
continue
|
|
}
|
|
status := &workloadv1alpha1.WorkloadStatus{}
|
|
if err := json.Unmarshal(item.Status.Raw, status); err != nil {
|
|
return interpreter.Errored(http.StatusInternalServerError, err)
|
|
}
|
|
readyReplicas += status.ReadyReplicas
|
|
}
|
|
wantedWorkload.Status.ReadyReplicas = readyReplicas
|
|
marshaledBytes, err := json.Marshal(wantedWorkload)
|
|
if err != nil {
|
|
return interpreter.Errored(http.StatusInternalServerError, err)
|
|
}
|
|
return interpreter.PatchResponseFromRaw(req.Object.Raw, marshaledBytes)
|
|
}
|