341 lines
11 KiB
Go
341 lines
11 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 resourceinterpreter
|
|
|
|
import (
|
|
"context"
|
|
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
corev1 "k8s.io/client-go/listers/core/v1"
|
|
"k8s.io/klog/v2"
|
|
|
|
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/declarative"
|
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook"
|
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/webhook/request"
|
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/default/native"
|
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/default/thirdparty"
|
|
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
|
|
)
|
|
|
|
// ResourceInterpreter manages both default and customized webhooks to interpret custom resource structure.
|
|
type ResourceInterpreter interface {
|
|
// Start starts running the component and will never stop running until the context is closed or an error occurs.
|
|
Start(ctx context.Context) (err error)
|
|
|
|
// HookEnabled tells if any hook exist for specific resource type and operation.
|
|
HookEnabled(objGVK schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) bool
|
|
|
|
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
|
GetReplicas(object *unstructured.Unstructured) (replica int32, replicaRequires *workv1alpha2.ReplicaRequirements, err error)
|
|
|
|
// ReviseReplica revises the replica of the given object.
|
|
ReviseReplica(object *unstructured.Unstructured, replica int64) (*unstructured.Unstructured, error)
|
|
|
|
// Retain returns the objects that based on the "desired" object but with values retained from the "observed" object.
|
|
Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, err error)
|
|
|
|
// AggregateStatus returns the objects that based on the 'object' but with status aggregated.
|
|
AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (*unstructured.Unstructured, error)
|
|
|
|
// GetDependencies returns the dependent resources of the given object.
|
|
GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, err error)
|
|
|
|
// ReflectStatus returns the status of the object.
|
|
ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, err error)
|
|
|
|
// InterpretHealth returns the health state of the object.
|
|
InterpretHealth(object *unstructured.Unstructured) (healthy bool, err error)
|
|
|
|
// other common method
|
|
}
|
|
|
|
// NewResourceInterpreter builds a new ResourceInterpreter object.
|
|
func NewResourceInterpreter(informer genericmanager.SingleClusterInformerManager, serviceLister corev1.ServiceLister) ResourceInterpreter {
|
|
return &customResourceInterpreterImpl{
|
|
informer: informer,
|
|
serviceLister: serviceLister,
|
|
}
|
|
}
|
|
|
|
type customResourceInterpreterImpl struct {
|
|
informer genericmanager.SingleClusterInformerManager
|
|
serviceLister corev1.ServiceLister
|
|
|
|
configurableInterpreter *declarative.ConfigurableInterpreter
|
|
customizedInterpreter *webhook.CustomizedInterpreter
|
|
thirdpartyInterpreter *thirdparty.ConfigurableInterpreter
|
|
defaultInterpreter *native.DefaultInterpreter
|
|
}
|
|
|
|
// Start starts running the component and will never stop running until the context is closed or an error occurs.
|
|
func (i *customResourceInterpreterImpl) Start(ctx context.Context) (err error) {
|
|
klog.Infof("Starting custom resource interpreter.")
|
|
|
|
i.customizedInterpreter, err = webhook.NewCustomizedInterpreter(i.informer, i.serviceLister)
|
|
if err != nil {
|
|
return
|
|
}
|
|
i.configurableInterpreter = declarative.NewConfigurableInterpreter(i.informer)
|
|
|
|
i.thirdpartyInterpreter = thirdparty.NewConfigurableInterpreter()
|
|
i.defaultInterpreter = native.NewDefaultInterpreter()
|
|
|
|
i.informer.Start()
|
|
<-ctx.Done()
|
|
klog.Infof("Stopped as stopCh closed.")
|
|
return nil
|
|
}
|
|
|
|
// HookEnabled tells if any hook exist for specific resource type and operation.
|
|
func (i *customResourceInterpreterImpl) HookEnabled(objGVK schema.GroupVersionKind, operation configv1alpha1.InterpreterOperation) bool {
|
|
return i.defaultInterpreter.HookEnabled(objGVK, operation) ||
|
|
i.thirdpartyInterpreter.HookEnabled(objGVK, operation) ||
|
|
i.configurableInterpreter.HookEnabled(objGVK, operation) ||
|
|
i.customizedInterpreter.HookEnabled(objGVK, operation)
|
|
}
|
|
|
|
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
|
func (i *customResourceInterpreterImpl) GetReplicas(object *unstructured.Unstructured) (replica int32, requires *workv1alpha2.ReplicaRequirements, err error) {
|
|
var hookEnabled bool
|
|
|
|
replica, requires, hookEnabled, err = i.configurableInterpreter.GetReplicas(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
|
|
replica, requires, hookEnabled, err = i.customizedInterpreter.GetReplicas(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationInterpretReplica,
|
|
Object: object,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
replica, requires, hookEnabled, err = i.thirdpartyInterpreter.GetReplicas(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
|
|
replica, requires, err = i.defaultInterpreter.GetReplicas(object)
|
|
return
|
|
}
|
|
|
|
// ReviseReplica revises the replica of the given object.
|
|
func (i *customResourceInterpreterImpl) ReviseReplica(object *unstructured.Unstructured, replica int64) (*unstructured.Unstructured, error) {
|
|
obj, hookEnabled, err := i.configurableInterpreter.ReviseReplica(object, replica)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
|
|
klog.V(4).Infof("Revise replicas for object: %v %s/%s with webhook interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
|
obj, hookEnabled, err = i.customizedInterpreter.Patch(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationReviseReplica,
|
|
Object: object,
|
|
ReplicasSet: int32(replica),
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
obj, hookEnabled, err = i.thirdpartyInterpreter.ReviseReplica(object, replica)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
|
|
return i.defaultInterpreter.ReviseReplica(object, replica)
|
|
}
|
|
|
|
// Retain returns the objects that based on the "desired" object but with values retained from the "observed" object.
|
|
func (i *customResourceInterpreterImpl) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
|
obj, hookEnabled, err := i.configurableInterpreter.Retain(desired, observed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
|
|
klog.V(4).Infof("Retain object: %v %s/%s with webhook interpreter.", desired.GroupVersionKind(), desired.GetNamespace(), desired.GetName())
|
|
obj, hookEnabled, err = i.customizedInterpreter.Patch(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationRetain,
|
|
Object: desired,
|
|
ObservedObj: observed,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
obj, hookEnabled, err = i.thirdpartyInterpreter.Retain(desired, observed)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
|
|
return i.defaultInterpreter.Retain(desired, observed)
|
|
}
|
|
|
|
// AggregateStatus returns the objects that based on the 'object' but with status aggregated.
|
|
func (i *customResourceInterpreterImpl) AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (*unstructured.Unstructured, error) {
|
|
obj, hookEnabled, err := i.configurableInterpreter.AggregateStatus(object, aggregatedStatusItems)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
|
|
klog.V(4).Infof("Aggregate status of object: %v %s/%s with webhook interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
|
obj, hookEnabled, err = i.customizedInterpreter.Patch(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationAggregateStatus,
|
|
Object: object.DeepCopy(),
|
|
AggregatedStatus: aggregatedStatusItems,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
obj, hookEnabled, err = i.thirdpartyInterpreter.AggregateStatus(object, aggregatedStatusItems)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hookEnabled {
|
|
return obj, nil
|
|
}
|
|
|
|
return i.defaultInterpreter.AggregateStatus(object, aggregatedStatusItems)
|
|
}
|
|
|
|
// GetDependencies returns the dependent resources of the given object.
|
|
func (i *customResourceInterpreterImpl) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, err error) {
|
|
dependencies, hookEnabled, err := i.configurableInterpreter.GetDependencies(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
|
|
dependencies, hookEnabled, err = i.customizedInterpreter.GetDependencies(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationInterpretDependency,
|
|
Object: object,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
dependencies, hookEnabled, err = i.thirdpartyInterpreter.GetDependencies(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
|
|
dependencies, err = i.defaultInterpreter.GetDependencies(object)
|
|
return
|
|
}
|
|
|
|
// ReflectStatus returns the status of the object.
|
|
func (i *customResourceInterpreterImpl) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, err error) {
|
|
status, hookEnabled, err := i.configurableInterpreter.ReflectStatus(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
status, hookEnabled, err = i.customizedInterpreter.ReflectStatus(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationInterpretStatus,
|
|
Object: object,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
status, hookEnabled, err = i.thirdpartyInterpreter.ReflectStatus(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
status, err = i.defaultInterpreter.ReflectStatus(object)
|
|
return
|
|
}
|
|
|
|
// InterpretHealth returns the health state of the object.
|
|
func (i *customResourceInterpreterImpl) InterpretHealth(object *unstructured.Unstructured) (healthy bool, err error) {
|
|
healthy, hookEnabled, err := i.configurableInterpreter.InterpretHealth(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
|
|
healthy, hookEnabled, err = i.customizedInterpreter.InterpretHealth(context.TODO(), &request.Attributes{
|
|
Operation: configv1alpha1.InterpreterOperationInterpretHealth,
|
|
Object: object,
|
|
})
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
healthy, hookEnabled, err = i.thirdpartyInterpreter.InterpretHealth(object)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if hookEnabled {
|
|
return
|
|
}
|
|
|
|
healthy, err = i.defaultInterpreter.InterpretHealth(object)
|
|
return
|
|
}
|