Merge pull request #2794 from jameszhangyukun/pr-interpret-interfaces

Resource Interpreter implements the interfaces
This commit is contained in:
karmada-bot 2022-11-16 17:43:59 +08:00 committed by GitHub
commit ba084975d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 248 additions and 203 deletions

View File

@ -58,31 +58,52 @@ func NewResourceCustomAccessorAccessor(customization *configv1alpha1.ResourceInt
} }
func (a *resourceCustomAccessor) GetRetentionLuaScript() string { func (a *resourceCustomAccessor) GetRetentionLuaScript() string {
if a.retention == nil {
return ""
}
return a.retention.LuaScript return a.retention.LuaScript
} }
func (a *resourceCustomAccessor) GetReplicaResourceLuaScript() string { func (a *resourceCustomAccessor) GetReplicaResourceLuaScript() string {
return a.replicaRevision.LuaScript if a.replicaResource == nil {
} return ""
}
func (a *resourceCustomAccessor) GetReplicaRevisionLuaScript() string {
return a.replicaResource.LuaScript return a.replicaResource.LuaScript
} }
func (a *resourceCustomAccessor) GetReplicaRevisionLuaScript() string {
if a.replicaRevision == nil {
return ""
}
return a.replicaRevision.LuaScript
}
func (a *resourceCustomAccessor) GetStatusReflectionLuaScript() string { func (a *resourceCustomAccessor) GetStatusReflectionLuaScript() string {
if a.statusReflection == nil {
return ""
}
return a.statusReflection.LuaScript return a.statusReflection.LuaScript
} }
func (a *resourceCustomAccessor) GetStatusAggregationLuaScript() string { func (a *resourceCustomAccessor) GetStatusAggregationLuaScript() string {
if a.statusAggregation == nil {
return ""
}
return a.statusAggregation.LuaScript return a.statusAggregation.LuaScript
} }
func (a *resourceCustomAccessor) GetHealthInterpretationLuaScript() string { func (a *resourceCustomAccessor) GetHealthInterpretationLuaScript() string {
if a.healthInterpretation == nil {
return ""
}
return a.healthInterpretation.LuaScript return a.healthInterpretation.LuaScript
} }
func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScript() string { func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScript() string {
return a.healthInterpretation.LuaScript if a.dependencyInterpretation == nil {
return ""
}
return a.dependencyInterpretation.LuaScript
} }
func (a *resourceCustomAccessor) Name() string { func (a *resourceCustomAccessor) Name() string {

View File

@ -24,7 +24,7 @@ var resourceInterpreterCustomizationsGVR = schema.GroupVersionResource{
// ConfigManager can list custom resource interpreter. // ConfigManager can list custom resource interpreter.
type ConfigManager interface { type ConfigManager interface {
LuaScriptAccessors() map[schema.GroupVersionKind]LuaScriptAccessor LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor
HasSynced() bool HasSynced() bool
} }
@ -36,8 +36,8 @@ type interpreterConfigManager struct {
} }
// LuaScriptAccessors returns all cached configurations. // LuaScriptAccessors returns all cached configurations.
func (configManager *interpreterConfigManager) LuaScriptAccessors() map[schema.GroupVersionKind]LuaScriptAccessor { func (configManager *interpreterConfigManager) LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor {
return configManager.configuration.Load().(map[schema.GroupVersionKind]LuaScriptAccessor) return configManager.configuration.Load().(map[schema.GroupVersionKind]CustomAccessor)
} }
// HasSynced returns true when the cache is synced. // HasSynced returns true when the cache is synced.
@ -46,8 +46,13 @@ func (configManager *interpreterConfigManager) HasSynced() bool {
return true 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) configManager.initialSynced.Store(true)
// the informer has synced, and we don't have any items
return true return true
} }
return false return false
@ -61,7 +66,7 @@ func NewInterpreterConfigManager(inform genericmanager.SingleClusterInformerMana
initialSynced: &atomic.Value{}, initialSynced: &atomic.Value{},
configuration: &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) manager.initialSynced.Store(false)
configHandlers := fedinformer.NewHandlerOnEvents( configHandlers := fedinformer.NewHandlerOnEvents(
func(_ interface{}) { manager.updateConfiguration() }, func(_ interface{}) { manager.updateConfiguration() },
@ -88,7 +93,6 @@ func (configManager *interpreterConfigManager) updateConfiguration() {
key := schema.FromAPIVersionAndKind(config.Spec.Target.APIVersion, config.Spec.Target.Kind) key := schema.FromAPIVersionAndKind(config.Spec.Target.APIVersion, config.Spec.Target.Kind)
configs[key] = NewResourceCustomAccessorAccessor(config) configs[key] = NewResourceCustomAccessorAccessor(config)
} }
configManager.configuration.Store(configs) configManager.configuration.Store(configs)
configManager.initialSynced.Store(true) configManager.initialSynced.Store(true)
} }

View File

@ -1,8 +1,6 @@
package configurableinterpreter package configurableinterpreter
import ( import (
"fmt"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
@ -11,6 +9,7 @@ import (
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1" configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" 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/configmanager"
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/luavm"
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager" "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. // HookEnabled tells if any hook exist for specific resource gvk and operation type.
func (c *ConfigurableInterpreter) HookEnabled(kind schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) bool { func (c *ConfigurableInterpreter) HookEnabled(kind schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) bool {
if !c.configManager.HasSynced() { _, exist := c.getInterpreter(kind, operationType)
klog.Errorf("not yet ready to handle request") return exist
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
} }
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica. // 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) { func (c *ConfigurableInterpreter) GetReplicas(object *unstructured.Unstructured) (replicas int32, requires *workv1alpha2.ReplicaRequirements, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute ReviseReplica") luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretReplica)
customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] if !enabled {
if len(customAccessor.GetReplicaResourceLuaScript()) == 0 { return
return 0, nil, fmt.Errorf("customized interpreter operation GetReplicas for %q not found", object.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetReplicaResourceLuaScript replicas, requires, err = vm.GetReplicas(object, luaScript)
klog.Infof("lua script %s", luaScript) return
return 0, nil, nil
} }
// ReviseReplica revises the replica of the given object. // ReviseReplica revises the replica of the given object.
func (c *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (*unstructured.Unstructured, error) { func (c *ConfigurableInterpreter) ReviseReplica(object *unstructured.Unstructured, replica int64) (revised *unstructured.Unstructured, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute ReviseReplica") luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationReviseReplica)
customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] if !enabled {
if len(customAccessor.GetReplicaRevisionLuaScript()) == 0 { return
return nil, fmt.Errorf("customized interpreter operation ReviseReplica for %q not found", object.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetReplicaRevisionLuaScript() revised, err = vm.ReviseReplica(object, replica, luaScript)
klog.Infof("lua script %s", luaScript) return
return nil, nil
} }
// Retain returns the objects that based on the "desired" object but with values retained from the "observed" object. // 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) { func (c *ConfigurableInterpreter) Retain(desired *unstructured.Unstructured, observed *unstructured.Unstructured) (retained *unstructured.Unstructured, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute Retain") luaScript, enabled := c.getInterpreter(desired.GroupVersionKind(), configv1alpha1.InterpreterOperationRetain)
customAccessor := c.configManager.LuaScriptAccessors()[desired.GroupVersionKind()] if !enabled {
if len(customAccessor.GetRetentionLuaScript()) == 0 { return
return nil, fmt.Errorf("customized interpreter operation Retain for %q not found", desired.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetRetentionLuaScript() retained, err = vm.Retain(desired, observed, luaScript)
klog.Infof("lua script %s", luaScript) return
return nil, err
} }
// AggregateStatus returns the objects that based on the 'object' but with status aggregated. // 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) { func (c *ConfigurableInterpreter) AggregateStatus(object *unstructured.Unstructured, aggregatedStatusItems []workv1alpha2.AggregatedStatusItem) (status *unstructured.Unstructured, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute AggregateStatus") luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationAggregateStatus)
customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] if !enabled {
if len(customAccessor.GetStatusAggregationLuaScript()) == 0 { return
return nil, fmt.Errorf("customized interpreter AggregateStatus Retain for %q not found", object.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetStatusAggregationLuaScript() status, err = vm.AggregateStatus(object, aggregatedStatusItems, luaScript)
klog.Infof("lua script %s", luaScript) return
return nil, nil
} }
// GetDependencies returns the dependent resources of the given object. // GetDependencies returns the dependent resources of the given object.
func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, err error) { func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute GetDependencies") luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency)
if !enabled {
customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] return
if len(customAccessor.GetDependencyInterpretationLuaScript()) == 0 {
return nil, fmt.Errorf("customized interpreter GetDependencies Retain for %q not found", object.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetDependencyInterpretationLuaScript() dependencies, err = vm.GetDependencies(object, luaScript)
klog.Infof("lua script %s", luaScript) return
return nil, err
} }
// ReflectStatus returns the status of the object. // ReflectStatus returns the status of the object.
func (c *ConfigurableInterpreter) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, err error) { func (c *ConfigurableInterpreter) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute ReflectStatus") luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretStatus)
customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] if !enabled {
if len(customAccessor.GetStatusAggregationLuaScript()) == 0 { return
return nil, fmt.Errorf("customized interpreter GetDependencies Retain for %q not found", object.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetStatusAggregationLuaScript() status, err = vm.ReflectStatus(object, luaScript)
klog.Infof("lua script %s", luaScript) return
return nil, err
} }
// InterpretHealth returns the health state of the object. // InterpretHealth returns the health state of the object.
func (c *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructured) (bool, error) { func (c *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructured) (health bool, enabled bool, err error) {
klog.Infof("ConfigurableInterpreter Execute InterpretHealth") luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretHealth)
customAccessor := c.configManager.LuaScriptAccessors()[object.GroupVersionKind()] if !enabled {
if len(customAccessor.GetHealthInterpretationLuaScript()) == 0 { return
return false, fmt.Errorf("customized interpreter GetHealthInterpretation for %q not found", object.GroupVersionKind())
} }
vm := luavm.VM{UseOpenLibs: false}
luaScript := customAccessor.GetHealthInterpretationLuaScript() health, err = vm.InterpretHealth(object, luaScript)
klog.Infof("lua script %s", luaScript) return
}
return false, nil
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
} }

View File

@ -15,7 +15,6 @@ import (
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1" configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" 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" "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. // 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{ l := lua.NewState(lua.Options{
SkipOpenLibs: !vm.UseOpenLibs, SkipOpenLibs: !vm.UseOpenLibs,
}) })
@ -245,7 +244,7 @@ func (vm VM) AggregateStatus(object *unstructured.Unstructured, item []map[strin
if err != nil { if err != nil {
return nil, err return nil, err
} }
args[1], err = decodeValue(l, item) args[1], err = decodeValue(l, items)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -344,7 +343,7 @@ func (vm VM) ReflectStatus(object *unstructured.Unstructured, script string) (st
if err != nil { if err != nil {
return 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 { if err != nil {
return nil, err 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()) return nil, fmt.Errorf("expect the returned replica type is table but got %s", luaStatusResult.Type())
} }
luaExistResult := l.Get(l.GetTop()) status = &runtime.RawExtension{}
var exist bool err = ConvertLuaResultInto(luaStatusResult, status)
exist, err = ConvertLuaResultToBool(luaExistResult) return status, err
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
} }
// GetDependencies returns the dependent resources of the given object by lua. // 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()) luaResult := l.Get(l.GetTop())
if luaResult.Type() == lua.LTTable {
jsonBytes, err := luajson.Encode(luaResult) if luaResult.Type() != lua.LTTable {
if err != nil {
return nil, err
}
err = json.Unmarshal(jsonBytes, &dependencies)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type()) return nil, fmt.Errorf("expect the returned requires type is table but got %s", luaResult.Type())
} }
err = ConvertLuaResultInto(luaResult, &dependencies)
return return
} }

View File

@ -11,22 +11,23 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/klog/v2"
"k8s.io/utils/pointer" "k8s.io/utils/pointer"
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2" 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/helper"
) )
func TestGetReplicas(t *testing.T) { func TestGetReplicas(t *testing.T) {
var replicas int32 = 1 var replicas int32 = 1
// quantity := *resource.NewQuantity(1000, resource.BinarySI)
vm := VM{UseOpenLibs: false} vm := VM{UseOpenLibs: false}
tests := []struct { tests := []struct {
name string name string
deploy *appsv1.Deployment deploy *appsv1.Deployment
luaScript string luaScript string
expected bool expected bool
wantReplica int32
wantRequires *workv1alpha2.ReplicaRequirements
}{ }{
{ {
name: "Test GetReplica", name: "Test GetReplica",
@ -63,6 +64,8 @@ func TestGetReplicas(t *testing.T) {
result.nodeClaim = nil result.nodeClaim = nil
return replica, {} return replica, {}
end`, end`,
wantReplica: 1,
wantRequires: &workv1alpha2.ReplicaRequirements{},
}, },
} }
@ -70,11 +73,15 @@ func TestGetReplicas(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
toUnstructured, _ := helper.ToUnstructured(tt.deploy) toUnstructured, _ := helper.ToUnstructured(tt.deploy)
replicas, requires, err := vm.GetReplicas(toUnstructured, tt.luaScript) replicas, requires, err := vm.GetReplicas(toUnstructured, tt.luaScript)
klog.Infof("replicas %v", replicas)
klog.Infof("requires %v", requires)
if err != nil { if err != nil {
t.Errorf(err.Error()) 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) { func TestAggregateDeploymentStatus(t *testing.T) {
statusMap := map[string]interface{}{ statusMap := map[string]interface{}{
"replicas": 0, "replicas": 0,
"readyReplicas": 0, "readyReplicas": 1,
"updatedReplicas": 0, "updatedReplicas": 0,
"availableReplicas": 1, "availableReplicas": 0,
"unavailableReplicas": 0, "unavailableReplicas": 0,
} }
raw, _ := helper.BuildStatusRawExtension(statusMap) raw, _ := helper.BuildStatusRawExtension(statusMap)
@ -186,37 +193,25 @@ func TestAggregateDeploymentStatus(t *testing.T) {
oldDeploy.Status = appsv1.DeploymentStatus{ oldDeploy.Status = appsv1.DeploymentStatus{
Replicas: 0, ReadyReplicas: 1, UpdatedReplicas: 0, AvailableReplicas: 0, UnavailableReplicas: 0} 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) oldObj, _ := helper.ToUnstructured(oldDeploy)
newObj, _ := helper.ToUnstructured(newDeploy) 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 { tests := []struct {
name string name string
curObj *unstructured.Unstructured curObj *unstructured.Unstructured
aggregatedStatusItems []map[string]interface{} aggregatedStatusItems []workv1alpha2.AggregatedStatusItem
expectedObj *unstructured.Unstructured expectedObj *unstructured.Unstructured
luaScript string luaScript string
}{ }{
{ {
name: "Test AggregateDeploymentStatus", name: "Test AggregateDeploymentStatus",
curObj: oldObj, curObj: oldObj,
aggregatedStatusItems: aggregateItem, aggregatedStatusItems: aggregatedStatusItems,
expectedObj: newObj, expectedObj: newObj,
luaScript: `function AggregateStatus(desiredObj, statusItems) luaScript: `function AggregateStatus(desiredObj, statusItems)
for i = 1, #statusItems do 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 end
return desiredObj return desiredObj
end`, end`,
@ -236,8 +231,8 @@ func TestAggregateDeploymentStatus(t *testing.T) {
if err != nil { if err != nil {
t.Error(err.Error()) t.Error(err.Error())
} }
if reflect.DeepEqual(expectDeploy, actualDeploy) { if !reflect.DeepEqual(expectDeploy, actualDeploy) {
t.Log("Success \n") t.Errorf("AggregateStatus() got = %v, want %v", actualDeploy, expectDeploy)
} }
} }
} }
@ -273,10 +268,13 @@ func TestHealthDeploymentStatus(t *testing.T) {
vm := VM{UseOpenLibs: false} vm := VM{UseOpenLibs: false}
for _, tt := range tests { 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) { if err != nil {
t.Log("Success \n") 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 luaScript string
}{ }{
{ {
name: "Test RetainDeployment", name: "Test RetainDeployment1",
desiredObj: &unstructured.Unstructured{ desiredObj: &unstructured.Unstructured{
Object: map[string]interface{}{ Object: map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@ -299,7 +297,7 @@ func TestRetainDeployment(t *testing.T) {
"name": "fake-deployment", "name": "fake-deployment",
}, },
"spec": map[string]interface{}{ "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", luaScript: "function Retain(desiredObj, observedObj)\n desiredObj = observedObj\n return desiredObj\n end",
}, },
{ {
name: "revise deployment replica", name: "Test RetainDeployment2",
desiredObj: &unstructured.Unstructured{ desiredObj: &unstructured.Unstructured{
Object: map[string]interface{}{ Object: map[string]interface{}{
"apiVersion": "apps/v1", "apiVersion": "apps/v1",
@ -340,7 +338,7 @@ func TestRetainDeployment(t *testing.T) {
"name": "fake-deployment", "name": "fake-deployment",
}, },
"spec": map[string]interface{}{ "spec": map[string]interface{}{
"replicas": int64(2), "replicas": int64(1),
}, },
}, },
}, },
@ -359,13 +357,8 @@ func TestRetainDeployment(t *testing.T) {
if err != nil { if err != nil {
t.Errorf(err.Error()) t.Errorf(err.Error())
} }
deploy := &appsv1.Deployment{} if !reflect.DeepEqual(res.UnstructuredContent(), tt.observedObj.Object) {
err = runtime.DefaultUnstructuredConverter.FromUnstructured(res.UnstructuredContent(), deploy) t.Errorf("Retain() got = %v, want %v", res.UnstructuredContent(), tt.observedObj.Object)
if err == nil && reflect.DeepEqual(deploy, tt.observedObj) {
t.Log("Success Test")
}
if err != nil {
t.Errorf(err.Error())
} }
}) })
} }
@ -397,9 +390,9 @@ func TestStatusReflection(t *testing.T) {
false, false,
`function ReflectStatus (observedObj) `function ReflectStatus (observedObj)
if observedObj.status == nil then if observedObj.status == nil then
return false, nil return nil
end end
return true, observedObj.status return observedObj.status
end`, end`,
}, },
} }
@ -438,11 +431,18 @@ func TestGetDeployPodDependencies(t *testing.T) {
}, },
} }
newObj, _ := helper.ToUnstructured(&newDeploy) newObj, _ := helper.ToUnstructured(&newDeploy)
expect := make([]configv1alpha1.DependentObjectReference, 1)
expect[0] = configv1alpha1.DependentObjectReference{
APIVersion: "v1",
Kind: "ServiceAccount",
Namespace: "test",
Name: "test",
}
tests := []struct { tests := []struct {
name string name string
curObj *unstructured.Unstructured curObj *unstructured.Unstructured
luaScript string luaScript string
want []configv1alpha1.DependentObjectReference
}{ }{
{ {
name: "Get GetDeployPodDependencies", name: "Get GetDeployPodDependencies",
@ -450,30 +450,36 @@ func TestGetDeployPodDependencies(t *testing.T) {
luaScript: `function GetDependencies(desiredObj) luaScript: `function GetDependencies(desiredObj)
dependentSas = {} dependentSas = {}
refs = {} 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 dependentSas[desiredObj.spec.template.spec.serviceAccountName] = true
end end
local idx = 1 local idx = 1
for key, value in pairs(dependentSas) do for key, value in pairs(dependentSas) do
dependObj = {} dependObj = {}
dependObj.apiVersion = \"v1\" dependObj.apiVersion = 'v1'
dependObj.kind = \"ServiceAccount\" dependObj.kind = 'ServiceAccount'
dependObj.name = key dependObj.name = key
dependObj.namespace = desiredObj.namespace dependObj.namespace = desiredObj.metadata.namespace
refs[idx] = {} refs[idx] = {}
refs[idx] = dependObj refs[idx] = dependObj
idx = idx + 1 idx = idx + 1
end end
return refs return refs
end`, end`,
want: expect,
}, },
} }
vm := VM{UseOpenLibs: false} vm := VM{UseOpenLibs: false}
for _, tt := range tests { for _, tt := range tests {
res, _ := vm.GetDependencies(tt.curObj, tt.luaScript) res, err := vm.GetDependencies(tt.curObj, tt.luaScript)
t.Logf("res %v", res) 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)
}
} }
} }

View File

@ -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. // HookEnabled tells if any hook exist for specific resource type and operation.
func (i *customResourceInterpreterImpl) HookEnabled(objGVK schema.GroupVersionKind, operation configv1alpha1.InterpreterOperation) bool { func (i *customResourceInterpreterImpl) HookEnabled(objGVK schema.GroupVersionKind, operation configv1alpha1.InterpreterOperation) bool {
return i.customizedInterpreter.HookEnabled(objGVK, operation) || return i.defaultInterpreter.HookEnabled(objGVK, operation) ||
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. // 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()) klog.V(4).Infof("Begin to get replicas for request object: %v %s/%s.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
var hookEnabled bool 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{ replica, requires, hookEnabled, err = i.customizedInterpreter.GetReplicas(context.TODO(), &webhook.RequestAttributes{
Operation: configv1alpha1.InterpreterOperationInterpretReplica, Operation: configv1alpha1.InterpreterOperationInterpretReplica,
Object: object, 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) { 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()) 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, Operation: configv1alpha1.InterpreterOperationReviseReplica,
Object: object, Object: object,
ReplicasSet: int32(replica), 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) { 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()) 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, Operation: configv1alpha1.InterpreterOperationRetain,
Object: desired, Object: desired,
ObservedObj: observed, 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) { 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()) 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, Operation: configv1alpha1.InterpreterOperationAggregateStatus,
Object: object.DeepCopy(), Object: object.DeepCopy(),
AggregatedStatus: aggregatedStatusItems, AggregatedStatus: aggregatedStatusItems,
@ -168,8 +202,15 @@ func (i *customResourceInterpreterImpl) AggregateStatus(object *unstructured.Uns
// GetDependencies returns the dependent resources of the given object. // GetDependencies returns the dependent resources of the given object.
func (i *customResourceInterpreterImpl) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, err error) { 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()) 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, Operation: configv1alpha1.InterpreterOperationInterpretDependency,
Object: object, 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) { 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()) 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, Operation: configv1alpha1.InterpreterOperationInterpretStatus,
Object: object, Object: object,
}) })
@ -207,7 +255,15 @@ func (i *customResourceInterpreterImpl) ReflectStatus(object *unstructured.Unstr
func (i *customResourceInterpreterImpl) InterpretHealth(object *unstructured.Unstructured) (healthy bool, err error) { 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()) 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, Operation: configv1alpha1.InterpreterOperationInterpretHealth,
Object: object, Object: object,
}) })