diff --git a/pkg/resourceinterpreter/configurableinterpreter/configmanager/accessor.go b/pkg/resourceinterpreter/configurableinterpreter/configmanager/accessor.go index 9afa0135d..ff987c3cf 100644 --- a/pkg/resourceinterpreter/configurableinterpreter/configmanager/accessor.go +++ b/pkg/resourceinterpreter/configurableinterpreter/configmanager/accessor.go @@ -58,31 +58,52 @@ func NewResourceCustomAccessorAccessor(customization *configv1alpha1.ResourceInt } func (a *resourceCustomAccessor) GetRetentionLuaScript() string { + if a.retention == nil { + return "" + } return a.retention.LuaScript } func (a *resourceCustomAccessor) GetReplicaResourceLuaScript() string { - return a.replicaRevision.LuaScript -} - -func (a *resourceCustomAccessor) GetReplicaRevisionLuaScript() string { + if a.replicaResource == nil { + return "" + } return a.replicaResource.LuaScript } +func (a *resourceCustomAccessor) GetReplicaRevisionLuaScript() string { + if a.replicaRevision == nil { + return "" + } + return a.replicaRevision.LuaScript +} + func (a *resourceCustomAccessor) GetStatusReflectionLuaScript() string { + if a.statusReflection == nil { + return "" + } return a.statusReflection.LuaScript } func (a *resourceCustomAccessor) GetStatusAggregationLuaScript() string { + if a.statusAggregation == nil { + return "" + } return a.statusAggregation.LuaScript } func (a *resourceCustomAccessor) GetHealthInterpretationLuaScript() string { + if a.healthInterpretation == nil { + return "" + } return a.healthInterpretation.LuaScript } func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScript() string { - return a.healthInterpretation.LuaScript + if a.dependencyInterpretation == nil { + return "" + } + return a.dependencyInterpretation.LuaScript } func (a *resourceCustomAccessor) Name() string { diff --git a/pkg/resourceinterpreter/configurableinterpreter/configmanager/manager.go b/pkg/resourceinterpreter/configurableinterpreter/configmanager/manager.go index b79a16170..c20daf69a 100644 --- a/pkg/resourceinterpreter/configurableinterpreter/configmanager/manager.go +++ b/pkg/resourceinterpreter/configurableinterpreter/configmanager/manager.go @@ -24,7 +24,7 @@ var resourceInterpreterCustomizationsGVR = schema.GroupVersionResource{ // ConfigManager can list custom resource interpreter. type ConfigManager interface { - LuaScriptAccessors() map[schema.GroupVersionKind]LuaScriptAccessor + LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor HasSynced() bool } @@ -36,8 +36,8 @@ type interpreterConfigManager struct { } // LuaScriptAccessors returns all cached configurations. -func (configManager *interpreterConfigManager) LuaScriptAccessors() map[schema.GroupVersionKind]LuaScriptAccessor { - return configManager.configuration.Load().(map[schema.GroupVersionKind]LuaScriptAccessor) +func (configManager *interpreterConfigManager) LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor { + return configManager.configuration.Load().(map[schema.GroupVersionKind]CustomAccessor) } // HasSynced returns true when the cache is synced. @@ -46,8 +46,13 @@ func (configManager *interpreterConfigManager) HasSynced() bool { return true } - if configManager.HasSynced() { + if configuration, err := configManager.lister.List(labels.Everything()); err == nil && len(configuration) == 0 { + // the empty list we initially stored is valid to use. + // Setting initialSynced to true, so subsequent checks + // would be able to take the fast path on the atomic boolean in a + // cluster without any customization configured. configManager.initialSynced.Store(true) + // the informer has synced, and we don't have any items return true } return false @@ -61,7 +66,7 @@ func NewInterpreterConfigManager(inform genericmanager.SingleClusterInformerMana initialSynced: &atomic.Value{}, configuration: &atomic.Value{}, } - manager.configuration.Store(make(map[schema.GroupVersionKind]LuaScriptAccessor)) + manager.configuration.Store(make(map[schema.GroupVersionKind]CustomAccessor)) manager.initialSynced.Store(false) configHandlers := fedinformer.NewHandlerOnEvents( func(_ interface{}) { manager.updateConfiguration() }, @@ -88,7 +93,6 @@ func (configManager *interpreterConfigManager) updateConfiguration() { key := schema.FromAPIVersionAndKind(config.Spec.Target.APIVersion, config.Spec.Target.Kind) configs[key] = NewResourceCustomAccessorAccessor(config) } - configManager.configuration.Store(configs) configManager.initialSynced.Store(true) } diff --git a/pkg/resourceinterpreter/configurableinterpreter/configurable.go b/pkg/resourceinterpreter/configurableinterpreter/configurable.go index d89b177c2..82449d329 100644 --- a/pkg/resourceinterpreter/configurableinterpreter/configurable.go +++ b/pkg/resourceinterpreter/configurableinterpreter/configurable.go @@ -1,8 +1,6 @@ package configurableinterpreter import ( - "fmt" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -11,6 +9,7 @@ import ( 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/configurableinterpreter/configmanager" + "github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/luavm" "github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager" ) @@ -30,128 +29,112 @@ func NewConfigurableInterpreter(informer genericmanager.SingleClusterInformerMan // HookEnabled tells if any hook exist for specific resource gvk and operation type. func (c *ConfigurableInterpreter) HookEnabled(kind schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) bool { - if !c.configManager.HasSynced() { - klog.Errorf("not yet ready to handle request") - return false - } - accessors, exist := c.configManager.LuaScriptAccessors()[kind] - if !exist { - return false - } - switch operationType { - case configv1alpha1.InterpreterOperationAggregateStatus: - return len(accessors.GetStatusAggregationLuaScript()) > 0 - case configv1alpha1.InterpreterOperationInterpretHealth: - return len(accessors.GetHealthInterpretationLuaScript()) > 0 - case configv1alpha1.InterpreterOperationInterpretDependency: - return len(accessors.GetDependencyInterpretationLuaScript()) > 0 - case configv1alpha1.InterpreterOperationInterpretReplica: - return len(accessors.GetReplicaResourceLuaScript()) > 0 - case configv1alpha1.InterpreterOperationInterpretStatus: - return len(accessors.GetStatusReflectionLuaScript()) > 0 - case configv1alpha1.InterpreterOperationRetain: - return len(accessors.GetRetentionLuaScript()) > 0 - case configv1alpha1.InterpreterOperationReviseReplica: - return len(accessors.GetReplicaRevisionLuaScript()) > 0 - } - return false + _, exist := c.getInterpreter(kind, operationType) + return exist } // GetReplicas returns the desired replicas of the object as well as the requirements of each replica. -func (c *ConfigurableInterpreter) GetReplicas(object *unstructured.Unstructured) (int32, *workv1alpha2.ReplicaRequirements, error) { - klog.Infof("ConfigurableInterpreter Execute ReviseReplica") - customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] - if len(customAccessor.GetReplicaResourceLuaScript()) == 0 { - return 0, nil, fmt.Errorf("customized interpreter operation GetReplicas for %q not found", object.GroupVersionKind()) +func (c *ConfigurableInterpreter) GetReplicas(object *unstructured.Unstructured) (replicas int32, requires *workv1alpha2.ReplicaRequirements, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretReplica) + if !enabled { + return } - - luaScript := customAccessor.GetReplicaResourceLuaScript - klog.Infof("lua script %s", luaScript) - - return 0, nil, nil + vm := luavm.VM{UseOpenLibs: false} + replicas, requires, err = vm.GetReplicas(object, luaScript) + return } // ReviseReplica revises the replica of the given object. -func (c *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (*unstructured.Unstructured, error) { - klog.Infof("ConfigurableInterpreter Execute ReviseReplica") - customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] - if len(customAccessor.GetReplicaRevisionLuaScript()) == 0 { - return nil, fmt.Errorf("customized interpreter operation ReviseReplica for %q not found", object.GroupVersionKind()) +func (c *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (revised *unstructured.Unstructured, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationReviseReplica) + if !enabled { + return } - - luaScript := customAccessor.GetReplicaRevisionLuaScript() - klog.Infof("lua script %s", luaScript) - - return nil, nil + vm := luavm.VM{UseOpenLibs: false} + revised, err = vm.ReviseReplica(object, replica, luaScript) + return } // Retain returns the objects that based on the "desired" object but with values retained from the "observed" object. -func (c *ConfigurableInterpreter) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, err error) { - klog.Infof("ConfigurableInterpreter Execute Retain") - customAccessor := c.configManager.LuaScriptAccessors()[desired.GroupVersionKind()] - if len(customAccessor.GetRetentionLuaScript()) == 0 { - return nil, fmt.Errorf("customized interpreter operation Retain for %q not found", desired.GroupVersionKind()) +func (c *ConfigurableInterpreter) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(desired.GroupVersionKind(), configv1alpha1.InterpreterOperationRetain) + if !enabled { + return } - - luaScript := customAccessor.GetRetentionLuaScript() - klog.Infof("lua script %s", luaScript) - - return nil, err + vm := luavm.VM{UseOpenLibs: false} + retained, err = vm.Retain(desired, observed, luaScript) + return } // AggregateStatus returns the objects that based on the 'object' but with status aggregated. -func (c *ConfigurableInterpreter) AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (*unstructured.Unstructured, error) { - klog.Infof("ConfigurableInterpreter Execute AggregateStatus") - customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] - if len(customAccessor.GetStatusAggregationLuaScript()) == 0 { - return nil, fmt.Errorf("customized interpreter AggregateStatus Retain for %q not found", object.GroupVersionKind()) +func (c *ConfigurableInterpreter) AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (status *unstructured.Unstructured, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationAggregateStatus) + if !enabled { + return } - - luaScript := customAccessor.GetStatusAggregationLuaScript() - klog.Infof("lua script %s", luaScript) - - return nil, nil + vm := luavm.VM{UseOpenLibs: false} + status, err = vm.AggregateStatus(object, aggregatedStatusItems, luaScript) + return } // GetDependencies returns the dependent resources of the given object. -func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, err error) { - klog.Infof("ConfigurableInterpreter Execute GetDependencies") - - customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] - if len(customAccessor.GetDependencyInterpretationLuaScript()) == 0 { - return nil, fmt.Errorf("customized interpreter GetDependencies Retain for %q not found", object.GroupVersionKind()) +func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) + if !enabled { + return } - - luaScript := customAccessor.GetDependencyInterpretationLuaScript() - klog.Infof("lua script %s", luaScript) - - return nil, err + vm := luavm.VM{UseOpenLibs: false} + dependencies, err = vm.GetDependencies(object, luaScript) + return } // ReflectStatus returns the status of the object. -func (c *ConfigurableInterpreter) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, err error) { - klog.Infof("ConfigurableInterpreter Execute ReflectStatus") - customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] - if len(customAccessor.GetStatusAggregationLuaScript()) == 0 { - return nil, fmt.Errorf("customized interpreter GetDependencies Retain for %q not found", object.GroupVersionKind()) +func (c *ConfigurableInterpreter) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretStatus) + if !enabled { + return } - - luaScript := customAccessor.GetStatusAggregationLuaScript() - klog.Infof("lua script %s", luaScript) - - return nil, err + vm := luavm.VM{UseOpenLibs: false} + status, err = vm.ReflectStatus(object, luaScript) + return } // InterpretHealth returns the health state of the object. -func (c *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructured) (bool, error) { - klog.Infof("ConfigurableInterpreter Execute InterpretHealth") - customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] - if len(customAccessor.GetHealthInterpretationLuaScript()) == 0 { - return false, fmt.Errorf("customized interpreter GetHealthInterpretation for %q not found", object.GroupVersionKind()) +func (c *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructured) (health bool, enabled bool, err error) { + luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretHealth) + if !enabled { + return } - - luaScript := customAccessor.GetHealthInterpretationLuaScript() - klog.Infof("lua script %s", luaScript) - - return false, nil + vm := luavm.VM{UseOpenLibs: false} + health, err = vm.InterpretHealth(object, luaScript) + return +} + +func (c *ConfigurableInterpreter) getInterpreter(kind schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) (string, bool) { + if !c.configManager.HasSynced() { + klog.Errorf("not yet ready to handle request") + return "", false + } + accessors, exist := c.configManager.LuaScriptAccessors()[kind] + if !exist { + return "", false + } + var script string + switch operationType { + case configv1alpha1.InterpreterOperationAggregateStatus: + script = accessors.GetStatusAggregationLuaScript() + case configv1alpha1.InterpreterOperationInterpretHealth: + script = accessors.GetHealthInterpretationLuaScript() + case configv1alpha1.InterpreterOperationInterpretDependency: + script = accessors.GetDependencyInterpretationLuaScript() + case configv1alpha1.InterpreterOperationInterpretReplica: + script = accessors.GetReplicaResourceLuaScript() + case configv1alpha1.InterpreterOperationInterpretStatus: + script = accessors.GetStatusReflectionLuaScript() + case configv1alpha1.InterpreterOperationRetain: + script = accessors.GetRetentionLuaScript() + case configv1alpha1.InterpreterOperationReviseReplica: + script = accessors.GetReplicaRevisionLuaScript() + } + return script, len(script) > 0 } diff --git a/pkg/resourceinterpreter/configurableinterpreter/luavm/lua.go b/pkg/resourceinterpreter/configurableinterpreter/luavm/lua.go index d2f09a085..1a06b5634 100644 --- a/pkg/resourceinterpreter/configurableinterpreter/luavm/lua.go +++ b/pkg/resourceinterpreter/configurableinterpreter/luavm/lua.go @@ -15,7 +15,6 @@ import ( 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/util/helper" "github.com/karmada-io/karmada/pkg/util/lifted" ) @@ -214,7 +213,7 @@ func (vm VM) Retain(desired *unstructured.Unstructured, observed *unstructured.U } // AggregateStatus returns the objects that based on the 'object' but with status aggregated by lua. -func (vm VM) AggregateStatus(object *unstructured.Unstructured, item []map[string]interface{}, script string) (*unstructured.Unstructured, error) { +func (vm VM) AggregateStatus(object *unstructured.Unstructured, items []workv1alpha2.AggregatedStatusItem, script string) (*unstructured.Unstructured, error) { l := lua.NewState(lua.Options{ SkipOpenLibs: !vm.UseOpenLibs, }) @@ -245,7 +244,7 @@ func (vm VM) AggregateStatus(object *unstructured.Unstructured, item []map[strin if err != nil { return nil, err } - args[1], err = decodeValue(l, item) + args[1], err = decodeValue(l, items) if err != nil { return nil, err } @@ -344,7 +343,7 @@ func (vm VM) ReflectStatus(object *unstructured.Unstructured, script string) (st if err != nil { return } - err = l.CallByParam(lua.P{Fn: f, NRet: 2, Protect: true}, args...) + err = l.CallByParam(lua.P{Fn: f, NRet: 1, Protect: true}, args...) if err != nil { return nil, err } @@ -354,26 +353,9 @@ func (vm VM) ReflectStatus(object *unstructured.Unstructured, script string) (st return nil, fmt.Errorf("expect the returned replica type is table but got %s", luaStatusResult.Type()) } - luaExistResult := l.Get(l.GetTop()) - var exist bool - exist, err = ConvertLuaResultToBool(luaExistResult) - if err != nil { - return nil, err - } - - if exist { - resultMap := make(map[string]interface{}) - jsonBytes, err := luajson.Encode(luaStatusResult) - if err != nil { - return nil, err - } - err = json.Unmarshal(jsonBytes, &resultMap) - if err != nil { - return nil, err - } - return helper.BuildStatusRawExtension(resultMap) - } - return nil, err + status = &runtime.RawExtension{} + err = ConvertLuaResultInto(luaStatusResult, status) + return status, err } // GetDependencies returns the dependent resources of the given object by lua. @@ -414,18 +396,11 @@ func (vm VM) GetDependencies(object *unstructured.Unstructured, script string) ( } luaResult := l.Get(l.GetTop()) - if luaResult.Type() == lua.LTTable { - jsonBytes, err := luajson.Encode(luaResult) - if err != nil { - return nil, err - } - err = json.Unmarshal(jsonBytes, &dependencies) - if err != nil { - return nil, err - } - } else { + + if luaResult.Type() != lua.LTTable { return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type()) } + err = ConvertLuaResultInto(luaResult, &dependencies) return } diff --git a/pkg/resourceinterpreter/configurableinterpreter/luavm/lua_test.go b/pkg/resourceinterpreter/configurableinterpreter/luavm/lua_test.go index 6b9fb3124..df0374913 100644 --- a/pkg/resourceinterpreter/configurableinterpreter/luavm/lua_test.go +++ b/pkg/resourceinterpreter/configurableinterpreter/luavm/lua_test.go @@ -11,22 +11,23 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/klog/v2" "k8s.io/utils/pointer" + 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/util/helper" ) func TestGetReplicas(t *testing.T) { var replicas int32 = 1 - // quantity := *resource.NewQuantity(1000, resource.BinarySI) vm := VM{UseOpenLibs: false} tests := []struct { - name string - deploy *appsv1.Deployment - luaScript string - expected bool + name string + deploy *appsv1.Deployment + luaScript string + expected bool + wantReplica int32 + wantRequires *workv1alpha2.ReplicaRequirements }{ { name: "Test GetReplica", @@ -63,6 +64,8 @@ func TestGetReplicas(t *testing.T) { result.nodeClaim = nil return replica, {} end`, + wantReplica: 1, + wantRequires: &workv1alpha2.ReplicaRequirements{}, }, } @@ -70,11 +73,15 @@ func TestGetReplicas(t *testing.T) { t.Run(tt.name, func(t *testing.T) { toUnstructured, _ := helper.ToUnstructured(tt.deploy) replicas, requires, err := vm.GetReplicas(toUnstructured, tt.luaScript) - klog.Infof("replicas %v", replicas) - klog.Infof("requires %v", requires) if err != nil { t.Errorf(err.Error()) } + if !reflect.DeepEqual(replicas, tt.wantReplica) { + t.Errorf("GetReplicas() got = %v, want %v", replicas, tt.wantReplica) + } + if !reflect.DeepEqual(requires, tt.wantRequires) { + t.Errorf("GetReplicas() got = %v, want %v", requires, tt.wantRequires) + } }) } } @@ -166,9 +173,9 @@ func TestReviseDeploymentReplica(t *testing.T) { func TestAggregateDeploymentStatus(t *testing.T) { statusMap := map[string]interface{}{ "replicas": 0, - "readyReplicas": 0, + "readyReplicas": 1, "updatedReplicas": 0, - "availableReplicas": 1, + "availableReplicas": 0, "unavailableReplicas": 0, } raw, _ := helper.BuildStatusRawExtension(statusMap) @@ -186,37 +193,25 @@ func TestAggregateDeploymentStatus(t *testing.T) { oldDeploy.Status = appsv1.DeploymentStatus{ Replicas: 0, ReadyReplicas: 1, UpdatedReplicas: 0, AvailableReplicas: 0, UnavailableReplicas: 0} - newDeploy := &appsv1.Deployment{Status: appsv1.DeploymentStatus{Replicas: 0, ReadyReplicas: 0, UpdatedReplicas: 0, AvailableReplicas: 2, UnavailableReplicas: 0}} + newDeploy := &appsv1.Deployment{Status: appsv1.DeploymentStatus{Replicas: 0, ReadyReplicas: 3, UpdatedReplicas: 0, AvailableReplicas: 0, UnavailableReplicas: 0}} oldObj, _ := helper.ToUnstructured(oldDeploy) newObj, _ := helper.ToUnstructured(newDeploy) - var aggregateItem []map[string]interface{} - for _, item := range aggregatedStatusItems { - if item.Status == nil { - continue - } - temp := make(map[string]interface{}) - if err := json.Unmarshal(item.Status.Raw, &temp); err != nil { - t.Error(err.Error()) - } - aggregateItem = append(aggregateItem, temp) - } - tests := []struct { name string curObj *unstructured.Unstructured - aggregatedStatusItems []map[string]interface{} + aggregatedStatusItems []workv1alpha2.AggregatedStatusItem expectedObj *unstructured.Unstructured luaScript string }{ { name: "Test AggregateDeploymentStatus", curObj: oldObj, - aggregatedStatusItems: aggregateItem, + aggregatedStatusItems: aggregatedStatusItems, expectedObj: newObj, - luaScript: `function AggregateStatus(desiredObj, statusItems) + luaScript: `function AggregateStatus(desiredObj, statusItems) for i = 1, #statusItems do - desiredObj.status.readyReplicas = desiredObj.status.readyReplicas + statusItems[i].readyReplicas + desiredObj.status.readyReplicas = desiredObj.status.readyReplicas + statusItems[i].status.readyReplicas end return desiredObj end`, @@ -236,8 +231,8 @@ func TestAggregateDeploymentStatus(t *testing.T) { if err != nil { t.Error(err.Error()) } - if reflect.DeepEqual(expectDeploy, actualDeploy) { - t.Log("Success \n") + if !reflect.DeepEqual(expectDeploy, actualDeploy) { + t.Errorf("AggregateStatus() got = %v, want %v", actualDeploy, expectDeploy) } } } @@ -273,10 +268,13 @@ func TestHealthDeploymentStatus(t *testing.T) { vm := VM{UseOpenLibs: false} for _, tt := range tests { - flag, _ := vm.InterpretHealth(tt.curObj, tt.luaScript) + flag, err := vm.InterpretHealth(tt.curObj, tt.luaScript) - if reflect.DeepEqual(flag, tt.expectedObj) { - t.Log("Success \n") + if err != nil { + t.Error(err.Error()) + } + if !reflect.DeepEqual(flag, tt.expectedObj) { + t.Errorf("AggregateStatus() got = %v, want %v", flag, tt.expectedObj) } } } @@ -290,7 +288,7 @@ func TestRetainDeployment(t *testing.T) { luaScript string }{ { - name: "Test RetainDeployment", + name: "Test RetainDeployment1", desiredObj: &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "apps/v1", @@ -299,7 +297,7 @@ func TestRetainDeployment(t *testing.T) { "name": "fake-deployment", }, "spec": map[string]interface{}{ - "replicas": 1, + "replicas": 2, }, }, }, @@ -319,7 +317,7 @@ func TestRetainDeployment(t *testing.T) { luaScript: "function Retain(desiredObj, observedObj)\n desiredObj = observedObj\n return desiredObj\n end", }, { - name: "revise deployment replica", + name: "Test RetainDeployment2", desiredObj: &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "apps/v1", @@ -340,7 +338,7 @@ func TestRetainDeployment(t *testing.T) { "name": "fake-deployment", }, "spec": map[string]interface{}{ - "replicas": int64(2), + "replicas": int64(1), }, }, }, @@ -359,13 +357,8 @@ func TestRetainDeployment(t *testing.T) { if err != nil { t.Errorf(err.Error()) } - deploy := &appsv1.Deployment{} - err = runtime.DefaultUnstructuredConverter.FromUnstructured(res.UnstructuredContent(), deploy) - if err == nil && reflect.DeepEqual(deploy, tt.observedObj) { - t.Log("Success Test") - } - if err != nil { - t.Errorf(err.Error()) + if !reflect.DeepEqual(res.UnstructuredContent(), tt.observedObj.Object) { + t.Errorf("Retain() got = %v, want %v", res.UnstructuredContent(), tt.observedObj.Object) } }) } @@ -397,9 +390,9 @@ func TestStatusReflection(t *testing.T) { false, `function ReflectStatus (observedObj) if observedObj.status == nil then - return false, nil + return nil end - return true, observedObj.status + return observedObj.status end`, }, } @@ -438,11 +431,18 @@ func TestGetDeployPodDependencies(t *testing.T) { }, } newObj, _ := helper.ToUnstructured(&newDeploy) - + expect := make([]configv1alpha1.DependentObjectReference, 1) + expect[0] = configv1alpha1.DependentObjectReference{ + APIVersion: "v1", + Kind: "ServiceAccount", + Namespace: "test", + Name: "test", + } tests := []struct { name string curObj *unstructured.Unstructured luaScript string + want []configv1alpha1.DependentObjectReference }{ { name: "Get GetDeployPodDependencies", @@ -450,30 +450,36 @@ func TestGetDeployPodDependencies(t *testing.T) { luaScript: `function GetDependencies(desiredObj) dependentSas = {} refs = {} - if desiredObj.spec.template.spec.serviceAccountName ~= \"\" and desiredObj.spec.template.spec.serviceAccountName ~= \"default\" then + if desiredObj.spec.template.spec.serviceAccountName ~= '' and desiredObj.spec.template.spec.serviceAccountName ~= 'default' then dependentSas[desiredObj.spec.template.spec.serviceAccountName] = true end local idx = 1 for key, value in pairs(dependentSas) do dependObj = {} - dependObj.apiVersion = \"v1\" - dependObj.kind = \"ServiceAccount\" + dependObj.apiVersion = 'v1' + dependObj.kind = 'ServiceAccount' dependObj.name = key - dependObj.namespace = desiredObj.namespace + dependObj.namespace = desiredObj.metadata.namespace refs[idx] = {} refs[idx] = dependObj idx = idx + 1 end return refs end`, + want: expect, }, } vm := VM{UseOpenLibs: false} for _, tt := range tests { - res, _ := vm.GetDependencies(tt.curObj, tt.luaScript) - t.Logf("res %v", res) + res, err := vm.GetDependencies(tt.curObj, tt.luaScript) + if err != nil { + t.Errorf("GetDependencies err %v", err) + } + if !reflect.DeepEqual(res, tt.want) { + t.Errorf("GetDependencies() got = %v, want %v", res, tt.want) + } } } diff --git a/pkg/resourceinterpreter/interpreter.go b/pkg/resourceinterpreter/interpreter.go index cf7e449a9..1631d2456 100644 --- a/pkg/resourceinterpreter/interpreter.go +++ b/pkg/resourceinterpreter/interpreter.go @@ -84,8 +84,9 @@ func (i *customResourceInterpreterImpl) Start(ctx context.Context) (err error) { // 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.customizedInterpreter.HookEnabled(objGVK, operation) || - i.defaultInterpreter.HookEnabled(objGVK, operation) + return i.defaultInterpreter.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. @@ -93,6 +94,15 @@ func (i *customResourceInterpreterImpl) GetReplicas(object *unstructured.Unstruc klog.V(4).Infof("Begin to get replicas for request object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) 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(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationInterpretReplica, Object: object, @@ -112,7 +122,15 @@ func (i *customResourceInterpreterImpl) GetReplicas(object *unstructured.Unstruc func (i *customResourceInterpreterImpl) ReviseReplica(object *unstructured.Unstructured, replica int64) (*unstructured.Unstructured, error) { klog.V(4).Infof("Begin to revise replicas for object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) - obj, hookEnabled, err := i.customizedInterpreter.Patch(context.TODO(), &webhook.RequestAttributes{ + obj, hookEnabled, err := i.configurableInterpreter.ReviseReplica(object, replica) + if err != nil { + return nil, err + } + if hookEnabled { + return obj, nil + } + + obj, hookEnabled, err = i.customizedInterpreter.Patch(context.TODO(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationReviseReplica, Object: object, ReplicasSet: int32(replica), @@ -131,7 +149,15 @@ func (i *customResourceInterpreterImpl) ReviseReplica(object *unstructured.Unstr func (i *customResourceInterpreterImpl) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (*unstructured.Unstructured, error) { klog.V(4).Infof("Begin to retain object: %v %s/%s.", desired.GroupVersionKind(), desired.GetNamespace(), desired.GetName()) - obj, hookEnabled, err := i.customizedInterpreter.Patch(context.TODO(), &webhook.RequestAttributes{ + obj, hookEnabled, err := i.configurableInterpreter.Retain(desired, observed) + if err != nil { + return nil, err + } + if hookEnabled { + return obj, nil + } + + obj, hookEnabled, err = i.customizedInterpreter.Patch(context.TODO(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationRetain, Object: desired, ObservedObj: observed, @@ -150,7 +176,15 @@ func (i *customResourceInterpreterImpl) Retain(desired *unstructured.Unstructure func (i *customResourceInterpreterImpl) AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (*unstructured.Unstructured, error) { klog.V(4).Infof("Begin to aggregate status for object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) - obj, hookEnabled, err := i.customizedInterpreter.Patch(context.TODO(), &webhook.RequestAttributes{ + obj, hookEnabled, err := i.configurableInterpreter.AggregateStatus(object, aggregatedStatusItems) + if err != nil { + return nil, err + } + if hookEnabled { + return obj, nil + } + + obj, hookEnabled, err = i.customizedInterpreter.Patch(context.TODO(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationAggregateStatus, Object: object.DeepCopy(), AggregatedStatus: aggregatedStatusItems, @@ -168,8 +202,15 @@ func (i *customResourceInterpreterImpl) AggregateStatus(object *unstructured.Uns // GetDependencies returns the dependent resources of the given object. func (i *customResourceInterpreterImpl) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, err error) { klog.V(4).Infof("Begin to get dependencies for object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) + dependencies, hookEnabled, err := i.configurableInterpreter.GetDependencies(object) + if err != nil { + return + } + if hookEnabled { + return + } - dependencies, hookEnabled, err := i.customizedInterpreter.GetDependencies(context.TODO(), &webhook.RequestAttributes{ + dependencies, hookEnabled, err = i.customizedInterpreter.GetDependencies(context.TODO(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationInterpretDependency, Object: object, }) @@ -188,7 +229,14 @@ func (i *customResourceInterpreterImpl) GetDependencies(object *unstructured.Uns func (i *customResourceInterpreterImpl) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, err error) { klog.V(4).Infof("Begin to grab status for object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) - status, hookEnabled, err := i.customizedInterpreter.ReflectStatus(context.TODO(), &webhook.RequestAttributes{ + status, hookEnabled, err := i.configurableInterpreter.ReflectStatus(object) + if err != nil { + return + } + if hookEnabled { + return + } + status, hookEnabled, err = i.customizedInterpreter.ReflectStatus(context.TODO(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationInterpretStatus, Object: object, }) @@ -207,7 +255,15 @@ func (i *customResourceInterpreterImpl) ReflectStatus(object *unstructured.Unstr func (i *customResourceInterpreterImpl) InterpretHealth(object *unstructured.Unstructured) (healthy bool, err error) { klog.V(4).Infof("Begin to check health for object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName()) - healthy, hookEnabled, err := i.customizedInterpreter.InterpretHealth(context.TODO(), &webhook.RequestAttributes{ + healthy, hookEnabled, err := i.configurableInterpreter.InterpretHealth(object) + if err != nil { + return + } + if hookEnabled { + return + } + + healthy, hookEnabled, err = i.customizedInterpreter.InterpretHealth(context.TODO(), &webhook.RequestAttributes{ Operation: configv1alpha1.InterpreterOperationInterpretHealth, Object: object, })