package thirdparty import ( "sort" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/sets" "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/configmanager" "github.com/karmada-io/karmada/pkg/resourceinterpreter/customized/declarative/luavm" ) // ConfigurableInterpreter interprets resources with third party resource interpreter. type ConfigurableInterpreter struct { configManager configmanager.ConfigManager luaVM *luavm.VM } // HookEnabled tells if any hook exist for specific resource gvk and operation type. func (p *ConfigurableInterpreter) HookEnabled(kind schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) bool { customAccessor, exist := p.getCustomAccessor(kind) if !exist { return exist } if operationType == configv1alpha1.InterpreterOperationInterpretDependency { scripts := customAccessor.GetDependencyInterpretationLuaScripts() return scripts != nil } var script string switch operationType { case configv1alpha1.InterpreterOperationAggregateStatus: script = customAccessor.GetStatusAggregationLuaScript() case configv1alpha1.InterpreterOperationInterpretHealth: script = customAccessor.GetHealthInterpretationLuaScript() case configv1alpha1.InterpreterOperationInterpretReplica: script = customAccessor.GetReplicaResourceLuaScript() case configv1alpha1.InterpreterOperationInterpretStatus: script = customAccessor.GetStatusReflectionLuaScript() case configv1alpha1.InterpreterOperationRetain: script = customAccessor.GetRetentionLuaScript() case configv1alpha1.InterpreterOperationReviseReplica: script = customAccessor.GetReplicaRevisionLuaScript() } return len(script) > 0 } // GetReplicas returns the desired replicas of the object as well as the requirements of each replica. func (p *ConfigurableInterpreter) GetReplicas(object *unstructured.Unstructured) (replicas int32, requires *workv1alpha2.ReplicaRequirements, enabled bool, err error) { klog.V(4).Infof("Get replicas for object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind()) if !enabled { return } script := customAccessor.GetReplicaResourceLuaScript() if len(script) == 0 { enabled = false return } replicas, requires, err = p.luaVM.GetReplicas(object, script) return } // ReviseReplica revises the replica of the given object. func (p *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (revised *unstructured.Unstructured, enabled bool, err error) { klog.V(4).Infof("Revise replicas for object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind()) if !enabled { return } script := customAccessor.GetReplicaRevisionLuaScript() if len(script) == 0 { enabled = false return } revised, err = p.luaVM.ReviseReplica(object, replica, script) return } // Retain returns the objects that based on the "desired" object but with values retained from the "observed" object. func (p *ConfigurableInterpreter) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, enabled bool, err error) { klog.V(4).Infof("Retain object: %v %s/%s with thirdparty configurable interpreter.", desired.GroupVersionKind(), desired.GetNamespace(), desired.GetName()) customAccessor, enabled := p.getCustomAccessor(desired.GroupVersionKind()) if !enabled { return } script := customAccessor.GetRetentionLuaScript() if len(script) == 0 { enabled = false return } retained, err = p.luaVM.Retain(desired, observed, script) return } // AggregateStatus returns the objects that based on the 'object' but with status aggregated. func (p *ConfigurableInterpreter) AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (status *unstructured.Unstructured, enabled bool, err error) { klog.V(4).Infof("Aggregate status of object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind()) if !enabled { return } script := customAccessor.GetStatusAggregationLuaScript() if len(script) == 0 { enabled = false return } status, err = p.luaVM.AggregateStatus(object, aggregatedStatusItems, script) return } // GetDependencies returns the dependent resources of the given object. func (p *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, enabled bool, err error) { klog.V(4).Infof("Get dependencies of object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind()) if !enabled { return } scripts := customAccessor.GetDependencyInterpretationLuaScripts() if scripts == nil { enabled = false return } refs := sets.New[configv1alpha1.DependentObjectReference]() for _, luaScript := range scripts { var references []configv1alpha1.DependentObjectReference references, err = p.luaVM.GetDependencies(object, luaScript) if err != nil { klog.Errorf("Failed to get DependentObjectReferences from object: %v %s/%s, error: %v", object.GroupVersionKind(), object.GetNamespace(), object.GetName(), err) return } refs.Insert(references...) } dependencies = refs.UnsortedList() // keep returned items in the same order between each call. sort.Slice(dependencies, func(i, j int) bool { if dependencies[i].APIVersion != dependencies[j].APIVersion { return dependencies[i].APIVersion < dependencies[j].APIVersion } if dependencies[i].Kind != dependencies[j].Kind { return dependencies[i].Kind < dependencies[j].Kind } if dependencies[i].Namespace != dependencies[j].Namespace { return dependencies[i].Namespace < dependencies[j].Namespace } return dependencies[i].Name < dependencies[j].Name }) return } // ReflectStatus returns the status of the object. func (p *ConfigurableInterpreter) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, enabled bool, err error) { klog.V(4).Infof("Reflect status of object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind()) if !enabled { return } script := customAccessor.GetStatusReflectionLuaScript() if len(script) == 0 { enabled = false return } status, err = p.luaVM.ReflectStatus(object, script) return } // InterpretHealth returns the health state of the object. func (p *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructured) (health bool, enabled bool, err error) { klog.V(4).Infof("Get health status of object: %v %s/%s with thirdparty configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) customAccessor, enabled := p.getCustomAccessor(object.GroupVersionKind()) if !enabled { return } script := customAccessor.GetHealthInterpretationLuaScript() if len(script) == 0 { enabled = false return } health, err = p.luaVM.InterpretHealth(object, script) return } func (p *ConfigurableInterpreter) getCustomAccessor(kind schema.GroupVersionKind) (configmanager.CustomAccessor, bool) { customAccessor, exist := p.configManager.CustomAccessors()[kind] return customAccessor, exist } // NewConfigurableInterpreter return a new ConfigurableInterpreter. func NewConfigurableInterpreter() *ConfigurableInterpreter { return &ConfigurableInterpreter{ configManager: NewThirdPartyConfigManager(), luaVM: luavm.New(false, 10), } }