support multiple dependencyInterpreter lua script for one gvk
Signed-off-by: changzhen <changzhen5@huawei.com>
This commit is contained in:
parent
37cca1cd8d
commit
5ed2100ee1
|
@ -19,7 +19,7 @@ type LuaScriptAccessor interface {
|
|||
GetStatusReflectionLuaScript() string
|
||||
GetStatusAggregationLuaScript() string
|
||||
GetHealthInterpretationLuaScript() string
|
||||
GetDependencyInterpretationLuaScript() string
|
||||
GetDependencyInterpretationLuaScripts() []string
|
||||
}
|
||||
|
||||
// CustomAccessor provides a common interface to get custom interpreter configuration.
|
||||
|
@ -28,13 +28,13 @@ type CustomAccessor interface {
|
|||
}
|
||||
|
||||
type resourceCustomAccessor struct {
|
||||
retention *configv1alpha1.LocalValueRetention
|
||||
replicaResource *configv1alpha1.ReplicaResourceRequirement
|
||||
replicaRevision *configv1alpha1.ReplicaRevision
|
||||
statusReflection *configv1alpha1.StatusReflection
|
||||
statusAggregation *configv1alpha1.StatusAggregation
|
||||
healthInterpretation *configv1alpha1.HealthInterpretation
|
||||
dependencyInterpretation *configv1alpha1.DependencyInterpretation
|
||||
retention *configv1alpha1.LocalValueRetention
|
||||
replicaResource *configv1alpha1.ReplicaResourceRequirement
|
||||
replicaRevision *configv1alpha1.ReplicaRevision
|
||||
statusReflection *configv1alpha1.StatusReflection
|
||||
statusAggregation *configv1alpha1.StatusAggregation
|
||||
healthInterpretation *configv1alpha1.HealthInterpretation
|
||||
dependencyInterpretations []*configv1alpha1.DependencyInterpretation
|
||||
}
|
||||
|
||||
// NewResourceCustomAccessor creates an accessor for resource interpreter customization.
|
||||
|
@ -63,7 +63,7 @@ func (a *resourceCustomAccessor) Merge(rules configv1alpha1.CustomizationRules)
|
|||
a.setHealthInterpretation(rules.HealthInterpretation)
|
||||
}
|
||||
if rules.DependencyInterpretation != nil {
|
||||
a.setDependencyInterpretation(rules.DependencyInterpretation)
|
||||
a.appendDependencyInterpretation(rules.DependencyInterpretation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,11 +109,18 @@ func (a *resourceCustomAccessor) GetHealthInterpretationLuaScript() string {
|
|||
return a.healthInterpretation.LuaScript
|
||||
}
|
||||
|
||||
func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScript() string {
|
||||
if a.dependencyInterpretation == nil {
|
||||
return ""
|
||||
func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScripts() []string {
|
||||
if a.dependencyInterpretations == nil {
|
||||
return nil
|
||||
}
|
||||
return a.dependencyInterpretation.LuaScript
|
||||
|
||||
var scripts []string
|
||||
for _, interpretation := range a.dependencyInterpretations {
|
||||
if interpretation.LuaScript != "" {
|
||||
scripts = append(scripts, interpretation.LuaScript)
|
||||
}
|
||||
}
|
||||
return scripts
|
||||
}
|
||||
|
||||
func (a *resourceCustomAccessor) setRetain(retention *configv1alpha1.LocalValueRetention) {
|
||||
|
@ -182,13 +189,6 @@ func (a *resourceCustomAccessor) setHealthInterpretation(healthInterpretation *c
|
|||
}
|
||||
}
|
||||
|
||||
func (a *resourceCustomAccessor) setDependencyInterpretation(dependencyInterpretation *configv1alpha1.DependencyInterpretation) {
|
||||
if a.dependencyInterpretation == nil {
|
||||
a.dependencyInterpretation = dependencyInterpretation
|
||||
return
|
||||
}
|
||||
|
||||
if dependencyInterpretation.LuaScript != "" && a.dependencyInterpretation.LuaScript == "" {
|
||||
a.dependencyInterpretation.LuaScript = dependencyInterpretation.LuaScript
|
||||
}
|
||||
func (a *resourceCustomAccessor) appendDependencyInterpretation(dependencyInterpretation *configv1alpha1.DependencyInterpretation) {
|
||||
a.dependencyInterpretations = append(a.dependencyInterpretations, dependencyInterpretation)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ var resourceInterpreterCustomizationsGVR = schema.GroupVersionResource{
|
|||
|
||||
// ConfigManager can list custom resource interpreter.
|
||||
type ConfigManager interface {
|
||||
LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor
|
||||
CustomAccessors() map[schema.GroupVersionKind]CustomAccessor
|
||||
HasSynced() bool
|
||||
LoadConfig(customizations []*configv1alpha1.ResourceInterpreterCustomization)
|
||||
}
|
||||
|
@ -37,8 +37,8 @@ type interpreterConfigManager struct {
|
|||
configuration atomic.Value
|
||||
}
|
||||
|
||||
// LuaScriptAccessors returns all cached configurations.
|
||||
func (configManager *interpreterConfigManager) LuaScriptAccessors() map[schema.GroupVersionKind]CustomAccessor {
|
||||
// CustomAccessors returns all cached configurations.
|
||||
func (configManager *interpreterConfigManager) CustomAccessors() map[schema.GroupVersionKind]CustomAccessor {
|
||||
return configManager.configuration.Load().(map[schema.GroupVersionKind]CustomAccessor)
|
||||
}
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ func Test_interpreterConfigManager_LuaScriptAccessors(t *testing.T) {
|
|||
t.Errorf("informer has not been synced")
|
||||
}
|
||||
|
||||
gotAccessors := configManager.LuaScriptAccessors()
|
||||
gotAccessors := configManager.CustomAccessors()
|
||||
for gvk, gotAccessor := range gotAccessors {
|
||||
wantAccessor, ok := tt.want[gvk]
|
||||
if !ok {
|
||||
|
|
|
@ -32,114 +32,185 @@ 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 {
|
||||
_, exist := c.getInterpreter(kind, operationType)
|
||||
return exist
|
||||
accessor, exist := c.getCustomAccessor(kind)
|
||||
if !exist {
|
||||
return exist
|
||||
}
|
||||
|
||||
if operationType == configv1alpha1.InterpreterOperationInterpretDependency {
|
||||
scripts := accessor.GetDependencyInterpretationLuaScripts()
|
||||
return scripts != nil
|
||||
}
|
||||
|
||||
var script string
|
||||
switch operationType {
|
||||
case configv1alpha1.InterpreterOperationAggregateStatus:
|
||||
script = accessor.GetStatusAggregationLuaScript()
|
||||
case configv1alpha1.InterpreterOperationInterpretHealth:
|
||||
script = accessor.GetHealthInterpretationLuaScript()
|
||||
case configv1alpha1.InterpreterOperationInterpretReplica:
|
||||
script = accessor.GetReplicaResourceLuaScript()
|
||||
case configv1alpha1.InterpreterOperationInterpretStatus:
|
||||
script = accessor.GetStatusReflectionLuaScript()
|
||||
case configv1alpha1.InterpreterOperationRetain:
|
||||
script = accessor.GetRetentionLuaScript()
|
||||
case configv1alpha1.InterpreterOperationReviseReplica:
|
||||
script = accessor.GetReplicaRevisionLuaScript()
|
||||
}
|
||||
return len(script) > 0
|
||||
}
|
||||
|
||||
// GetReplicas returns the desired replicas of the object as well as the requirements of each replica.
|
||||
func (c *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 configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
||||
luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretReplica)
|
||||
|
||||
accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
replicas, requires, err = c.luaVM.GetReplicas(object, luaScript)
|
||||
|
||||
script := accessor.GetReplicaResourceLuaScript()
|
||||
if len(script) == 0 {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
replicas, requires, err = c.luaVM.GetReplicas(object, script)
|
||||
return
|
||||
}
|
||||
|
||||
// ReviseReplica revises the replica of the given object.
|
||||
func (c *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 configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
||||
luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationReviseReplica)
|
||||
|
||||
accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
revised, err = c.luaVM.ReviseReplica(object, replica, luaScript)
|
||||
|
||||
script := accessor.GetReplicaRevisionLuaScript()
|
||||
if len(script) == 0 {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
revised, err = c.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 (c *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 configurable interpreter.", desired.GroupVersionKind(), desired.GetNamespace(), desired.GetName())
|
||||
luaScript, enabled := c.getInterpreter(desired.GroupVersionKind(), configv1alpha1.InterpreterOperationRetain)
|
||||
|
||||
accessor, enabled := c.getCustomAccessor(desired.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
retained, err = c.luaVM.Retain(desired, observed, luaScript)
|
||||
|
||||
script := accessor.GetRetentionLuaScript()
|
||||
if len(script) == 0 {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
retained, err = c.luaVM.Retain(desired, observed, script)
|
||||
return
|
||||
}
|
||||
|
||||
// AggregateStatus returns the objects that based on the 'object' but with status aggregated.
|
||||
func (c *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 configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
||||
luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationAggregateStatus)
|
||||
accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
status, err = c.luaVM.AggregateStatus(object, aggregatedStatusItems, luaScript)
|
||||
|
||||
script := accessor.GetStatusAggregationLuaScript()
|
||||
if len(script) == 0 {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
status, err = c.luaVM.AggregateStatus(object, aggregatedStatusItems, script)
|
||||
return
|
||||
}
|
||||
|
||||
// GetDependencies returns the dependent resources of the given object.
|
||||
func (c *ConfigurableInterpreter) GetDependencies(object *unstructured.Unstructured) (dependencies []configv1alpha1.DependentObjectReference, enabled bool, err error) {
|
||||
klog.V(4).Infof("Get dependencies of object: %v %s/%s with configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
||||
luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency)
|
||||
|
||||
accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
dependencies, err = c.luaVM.GetDependencies(object, luaScript)
|
||||
|
||||
scripts := accessor.GetDependencyInterpretationLuaScripts()
|
||||
if scripts == nil {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
referenceSet := newDependencySet()
|
||||
for _, luaScript := range scripts {
|
||||
var references []configv1alpha1.DependentObjectReference
|
||||
references, err = c.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
|
||||
}
|
||||
referenceSet.insert(references...)
|
||||
}
|
||||
dependencies = referenceSet.list()
|
||||
return
|
||||
}
|
||||
|
||||
// ReflectStatus returns the status of the object.
|
||||
func (c *ConfigurableInterpreter) ReflectStatus(object *unstructured.Unstructured) (status *runtime.RawExtension, enabled bool, err error) {
|
||||
klog.V(4).Infof("Reflect status of object: %v %s/%s with configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
||||
luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretStatus)
|
||||
|
||||
accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
status, err = c.luaVM.ReflectStatus(object, luaScript)
|
||||
|
||||
script := accessor.GetStatusReflectionLuaScript()
|
||||
if len(script) == 0 {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
status, err = c.luaVM.ReflectStatus(object, script)
|
||||
return
|
||||
}
|
||||
|
||||
// InterpretHealth returns the health state of the object.
|
||||
func (c *ConfigurableInterpreter) InterpretHealth(object *unstructured.Unstructured) (health bool, enabled bool, err error) {
|
||||
klog.V(4).Infof("Get health status of object: %v %s/%s with configurable interpreter.", object.GroupVersionKind(), object.GetNamespace(), object.GetName())
|
||||
luaScript, enabled := c.getInterpreter(object.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretHealth)
|
||||
|
||||
accessor, enabled := c.getCustomAccessor(object.GroupVersionKind())
|
||||
if !enabled {
|
||||
return
|
||||
}
|
||||
health, err = c.luaVM.InterpretHealth(object, luaScript)
|
||||
|
||||
script := accessor.GetHealthInterpretationLuaScript()
|
||||
if len(script) == 0 {
|
||||
enabled = false
|
||||
return
|
||||
}
|
||||
|
||||
health, err = c.luaVM.InterpretHealth(object, script)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *ConfigurableInterpreter) getInterpreter(kind schema.GroupVersionKind, operationType configv1alpha1.InterpreterOperation) (string, bool) {
|
||||
func (c *ConfigurableInterpreter) getCustomAccessor(kind schema.GroupVersionKind) (configmanager.CustomAccessor, bool) {
|
||||
if !c.configManager.HasSynced() {
|
||||
klog.Errorf("not yet ready to handle request")
|
||||
return "", false
|
||||
return nil, 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
|
||||
|
||||
accessor, exist := c.configManager.CustomAccessors()[kind]
|
||||
return accessor, exist
|
||||
}
|
||||
|
||||
// LoadConfig loads and stores rules from customizations
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package configurableinterpreter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
)
|
||||
|
||||
type dependencySet map[configv1alpha1.DependentObjectReference]struct{}
|
||||
|
||||
func newDependencySet(items ...configv1alpha1.DependentObjectReference) dependencySet {
|
||||
s := make(dependencySet, len(items))
|
||||
s.insert(items...)
|
||||
return s
|
||||
}
|
||||
|
||||
func (s dependencySet) insert(items ...configv1alpha1.DependentObjectReference) dependencySet {
|
||||
for _, item := range items {
|
||||
s[item] = struct{}{}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s dependencySet) list() []configv1alpha1.DependentObjectReference {
|
||||
keys := make([]configv1alpha1.DependentObjectReference, len(s))
|
||||
index := 0
|
||||
for key := range s {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
sort.Slice(keys, func(i, j int) bool {
|
||||
return fmt.Sprintf("%s", keys[i]) < fmt.Sprintf("%s", keys[j])
|
||||
})
|
||||
return keys
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package configurableinterpreter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||
)
|
||||
|
||||
func Test_dependencySet_list(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
s dependencySet
|
||||
want []configv1alpha1.DependentObjectReference
|
||||
}{
|
||||
{
|
||||
name: "empty set",
|
||||
s: newDependencySet(),
|
||||
want: []configv1alpha1.DependentObjectReference{},
|
||||
},
|
||||
{
|
||||
name: "new with items",
|
||||
s: newDependencySet([]configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
}...),
|
||||
want: []configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "insert with different items",
|
||||
s: newDependencySet([]configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
}...).insert(
|
||||
[]configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
||||
}...),
|
||||
want: []configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "insert with same items",
|
||||
s: newDependencySet([]configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
}...).insert(
|
||||
[]configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
||||
}...).insert(
|
||||
[]configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
{APIVersion: "apps/v1", Kind: "Deployment", Namespace: "foo", Name: "foo"},
|
||||
}...),
|
||||
want: []configv1alpha1.DependentObjectReference{
|
||||
{APIVersion: "apps/v1", Kind: "Deployment", Namespace: "foo", Name: "foo"},
|
||||
{APIVersion: "v1", Kind: "Configmap", Namespace: "foo", Name: "foo"},
|
||||
{APIVersion: "v1", Kind: "Secret", Namespace: "foo", Name: "foo"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.s.list(); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("dependencySet.list() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -16,6 +16,10 @@ func validateCustomizationRule(oldRules, newRules *configv1alpha1.ResourceInterp
|
|||
return nil
|
||||
}
|
||||
for _, rule := range interpreter.AllResourceInterpreterCustomizationRules {
|
||||
// skip InterpretDependency operation because it supports multiple rules.
|
||||
if rule.Name() == string(configv1alpha1.InterpreterOperationInterpretDependency) {
|
||||
continue
|
||||
}
|
||||
oldScript := rule.GetScript(oldRules)
|
||||
newScript := rule.GetScript(newRules)
|
||||
if oldScript != "" && newScript != "" {
|
||||
|
|
|
@ -256,7 +256,7 @@ func Test_validateCustomizationRule(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
|
|
|
@ -282,327 +282,362 @@ var _ = framework.SerialDescribe("Resource interpreter customization testing", f
|
|||
// We only need to test any one of the member clusters.
|
||||
var targetCluster string
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
targetCluster = framework.ClusterNames()[rand.Intn(len(framework.ClusterNames()))]
|
||||
deployment = testhelper.NewDeployment(testNamespace, deploymentNamePrefix+rand.String(RandomStrLength))
|
||||
policy = testhelper.NewPropagationPolicy(testNamespace, deployment.Name, []policyv1alpha1.ResourceSelector{
|
||||
{
|
||||
APIVersion: deployment.APIVersion,
|
||||
Kind: deployment.Kind,
|
||||
Name: deployment.Name,
|
||||
},
|
||||
}, policyv1alpha1.Placement{
|
||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||
ClusterNames: []string{targetCluster},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.JustBeforeEach(func() {
|
||||
framework.CreateResourceInterpreterCustomization(karmadaClient, customization)
|
||||
// Wait for resource interpreter informer synced.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
framework.CreatePropagationPolicy(karmadaClient, policy)
|
||||
framework.CreateDeployment(kubeClient, deployment)
|
||||
ginkgo.DeferCleanup(func() {
|
||||
framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name)
|
||||
framework.RemovePropagationPolicy(karmadaClient, policy.Namespace, policy.Name)
|
||||
framework.DeleteResourceInterpreterCustomization(karmadaClient, customization.Name)
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation InterpretReplica testing", func() {
|
||||
ginkgo.When("Apply single ResourceInterpreterCustomization without DependencyInterpretation operation", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
targetCluster = framework.ClusterNames()[rand.Intn(len(framework.ClusterNames()))]
|
||||
deployment = testhelper.NewDeployment(testNamespace, deploymentNamePrefix+rand.String(RandomStrLength))
|
||||
policy = testhelper.NewPropagationPolicy(testNamespace, deployment.Name, []policyv1alpha1.ResourceSelector{
|
||||
{
|
||||
APIVersion: deployment.APIVersion,
|
||||
Kind: deployment.Kind,
|
||||
Name: deployment.Name,
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{
|
||||
LuaScript: `
|
||||
function GetReplicas(desiredObj)
|
||||
replica = desiredObj.spec.replicas + 1
|
||||
requirement = {}
|
||||
requirement.nodeClaim = {}
|
||||
requirement.nodeClaim.nodeSelector = desiredObj.spec.template.spec.nodeSelector
|
||||
requirement.nodeClaim.tolerations = desiredObj.spec.template.spec.tolerations
|
||||
requirement.resourceRequest = desiredObj.spec.template.spec.containers[1].resources.limits
|
||||
return replica, requirement
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.It("InterpretReplica testing", func() {
|
||||
ginkgo.By("check if workload's replica is interpreted", func() {
|
||||
resourceBindingName := names.GenerateBindingName(deployment.Kind, deployment.Name)
|
||||
// Just for the current test case to distinguish the build-in logic.
|
||||
expectedReplicas := *deployment.Spec.Replicas + 1
|
||||
expectedReplicaRequirements := &workv1alpha2.ReplicaRequirements{
|
||||
ResourceRequest: map[corev1.ResourceName]resource.Quantity{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
}}
|
||||
|
||||
gomega.Eventually(func(g gomega.Gomega) (bool, error) {
|
||||
resourceBinding, err := karmadaClient.WorkV1alpha2().ResourceBindings(deployment.Namespace).Get(context.TODO(), resourceBindingName, metav1.GetOptions{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
klog.Infof(fmt.Sprintf("ResourceBinding(%s/%s)'s replicas is %d, expected: %d.",
|
||||
resourceBinding.Namespace, resourceBinding.Name, resourceBinding.Spec.Replicas, expectedReplicas))
|
||||
if resourceBinding.Spec.Replicas != expectedReplicas {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
klog.Infof(fmt.Sprintf("ResourceBinding(%s/%s)'s replicaRequirements is %+v, expected: %+v.",
|
||||
resourceBinding.Namespace, resourceBinding.Name, resourceBinding.Spec.ReplicaRequirements, expectedReplicaRequirements))
|
||||
return reflect.DeepEqual(resourceBinding.Spec.ReplicaRequirements, expectedReplicaRequirements), nil
|
||||
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation ReviseReplica testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
ReplicaRevision: &configv1alpha1.ReplicaRevision{
|
||||
LuaScript: `
|
||||
function ReviseReplica(obj, desiredReplica)
|
||||
obj.spec.replicas = desiredReplica + 1
|
||||
return obj
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
sumWeight := 0
|
||||
staticWeightLists := make([]policyv1alpha1.StaticClusterWeight, 0)
|
||||
for index, clusterName := range framework.ClusterNames() {
|
||||
staticWeightList := policyv1alpha1.StaticClusterWeight{
|
||||
TargetCluster: policyv1alpha1.ClusterAffinity{
|
||||
ClusterNames: []string{clusterName},
|
||||
},
|
||||
Weight: int64(index + 1),
|
||||
}
|
||||
sumWeight += index + 1
|
||||
staticWeightLists = append(staticWeightLists, staticWeightList)
|
||||
}
|
||||
deployment.Spec.Replicas = pointer.Int32Ptr(int32(sumWeight))
|
||||
policy.Spec.Placement = policyv1alpha1.Placement{
|
||||
}, policyv1alpha1.Placement{
|
||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||
ClusterNames: framework.ClusterNames(),
|
||||
ClusterNames: []string{targetCluster},
|
||||
},
|
||||
ReplicaScheduling: &policyv1alpha1.ReplicaSchedulingStrategy{
|
||||
ReplicaDivisionPreference: policyv1alpha1.ReplicaDivisionPreferenceWeighted,
|
||||
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDivided,
|
||||
WeightPreference: &policyv1alpha1.ClusterPreferences{
|
||||
StaticWeightList: staticWeightLists,
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.JustBeforeEach(func() {
|
||||
framework.CreateResourceInterpreterCustomization(karmadaClient, customization)
|
||||
// Wait for resource interpreter informer synced.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
framework.CreatePropagationPolicy(karmadaClient, policy)
|
||||
framework.CreateDeployment(kubeClient, deployment)
|
||||
ginkgo.DeferCleanup(func() {
|
||||
framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name)
|
||||
framework.RemovePropagationPolicy(karmadaClient, policy.Namespace, policy.Name)
|
||||
framework.DeleteResourceInterpreterCustomization(karmadaClient, customization.Name)
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation InterpretReplica testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("ReviseReplica testing", func() {
|
||||
for index, clusterName := range framework.ClusterNames() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(clusterName, deployment.Namespace, deployment.Name, func(deployment *appsv1.Deployment) bool {
|
||||
return *deployment.Spec.Replicas == int32(index+1)+1
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation Retain testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
Retention: &configv1alpha1.LocalValueRetention{
|
||||
LuaScript: `
|
||||
function Retain(desiredObj, observedObj)
|
||||
desiredObj.spec.paused = observedObj.spec.paused
|
||||
return desiredObj
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.It("Retain testing", func() {
|
||||
ginkgo.By("wait deployment exist on the member clusters", func() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(_ *appsv1.Deployment) bool {
|
||||
return true
|
||||
configv1alpha1.CustomizationRules{
|
||||
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{
|
||||
LuaScript: `
|
||||
function GetReplicas(desiredObj)
|
||||
replica = desiredObj.spec.replicas + 1
|
||||
requirement = {}
|
||||
requirement.nodeClaim = {}
|
||||
requirement.nodeClaim.nodeSelector = desiredObj.spec.template.spec.nodeSelector
|
||||
requirement.nodeClaim.tolerations = desiredObj.spec.template.spec.tolerations
|
||||
requirement.resourceRequest = desiredObj.spec.template.spec.containers[1].resources.limits
|
||||
return replica, requirement
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.By("update deployment on the control plane", func() {
|
||||
// construct two values that need to be changed, and only one value is retained.
|
||||
framework.UpdateDeploymentPaused(kubeClient, deployment, true)
|
||||
framework.UpdateDeploymentReplicas(kubeClient, deployment, 2)
|
||||
})
|
||||
ginkgo.It("InterpretReplica testing", func() {
|
||||
ginkgo.By("check if workload's replica is interpreted", func() {
|
||||
resourceBindingName := names.GenerateBindingName(deployment.Kind, deployment.Name)
|
||||
// Just for the current test case to distinguish the build-in logic.
|
||||
expectedReplicas := *deployment.Spec.Replicas + 1
|
||||
expectedReplicaRequirements := &workv1alpha2.ReplicaRequirements{
|
||||
ResourceRequest: map[corev1.ResourceName]resource.Quantity{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
}}
|
||||
|
||||
ginkgo.By("check if deployment's spec.paused is retained", func() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(deployment *appsv1.Deployment) bool {
|
||||
return *deployment.Spec.Replicas == 2 && !deployment.Spec.Paused
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
gomega.Eventually(func(g gomega.Gomega) (bool, error) {
|
||||
resourceBinding, err := karmadaClient.WorkV1alpha2().ResourceBindings(deployment.Namespace).Get(context.TODO(), resourceBindingName, metav1.GetOptions{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
|
||||
ginkgo.Context("InterpreterOperation AggregateStatus testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
StatusAggregation: &configv1alpha1.StatusAggregation{
|
||||
LuaScript: `
|
||||
function AggregateStatus(desiredObj, statusItems)
|
||||
if statusItems == nil then
|
||||
return desiredObj
|
||||
end
|
||||
if desiredObj.status == nil then
|
||||
desiredObj.status = {}
|
||||
end
|
||||
replicas = 0
|
||||
for i = 1, #statusItems do
|
||||
if statusItems[i].status ~= nil and statusItems[i].status.replicas ~= nil then
|
||||
replicas = replicas + statusItems[i].status.replicas + 1
|
||||
end
|
||||
end
|
||||
desiredObj.status.replicas = replicas
|
||||
return desiredObj
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("AggregateStatus testing", func() {
|
||||
ginkgo.By("check whether the deployment status can be correctly collected", func() {
|
||||
// Only in the current case, a special example is constructed to distinguish the build-in logic.
|
||||
wantedReplicas := *deployment.Spec.Replicas + 1
|
||||
gomega.Eventually(func() bool {
|
||||
var currentDeployment *appsv1.Deployment
|
||||
framework.WaitDeploymentGetByClientFitWith(kubeClient, deployment.Namespace, deployment.Name, func(deployment *appsv1.Deployment) bool {
|
||||
currentDeployment = deployment
|
||||
return true
|
||||
})
|
||||
klog.Infof("deployment(%s/%s) replicas: %d, wanted replicas: %d", deployment.Namespace, deployment.Name, currentDeployment.Status.Replicas, wantedReplicas)
|
||||
return currentDeployment.Status.Replicas == wantedReplicas
|
||||
}, pollTimeout, pollInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation InterpretStatus testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
StatusReflection: &configv1alpha1.StatusReflection{
|
||||
LuaScript: `
|
||||
function ReflectStatus (observedObj)
|
||||
if observedObj.status == nil then
|
||||
return nil
|
||||
end
|
||||
return observedObj.status
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("InterpretStatus testing", func() {
|
||||
gomega.Eventually(func(g gomega.Gomega) bool {
|
||||
deploy, err := kubeClient.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
return deploy.Status.ReadyReplicas == *deploy.Spec.Replicas
|
||||
}, pollTimeout, pollInterval).Should(gomega.BeTrue())
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation InterpretHealth testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
HealthInterpretation: &configv1alpha1.HealthInterpretation{
|
||||
LuaScript: `
|
||||
function InterpretHealth(observedObj)
|
||||
return observedObj.status.readyReplicas == observedObj.spec.replicas
|
||||
end `,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("InterpretHealth testing", func() {
|
||||
resourceBindingName := names.GenerateBindingName(deployment.Kind, deployment.Name)
|
||||
SetReadyReplicas := func(readyReplicas int32) {
|
||||
clusterClient := framework.GetClusterClient(targetCluster)
|
||||
gomega.Expect(clusterClient).ShouldNot(gomega.BeNil())
|
||||
var memberDeployment *appsv1.Deployment
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(deployment *appsv1.Deployment) bool {
|
||||
memberDeployment = deployment
|
||||
return true
|
||||
})
|
||||
memberDeployment.Status.ReadyReplicas = readyReplicas
|
||||
framework.UpdateDeploymentStatus(clusterClient, memberDeployment)
|
||||
}
|
||||
|
||||
CheckResult := func(result workv1alpha2.ResourceHealth) interface{} {
|
||||
return func(g gomega.Gomega) (bool, error) {
|
||||
rb, err := karmadaClient.WorkV1alpha2().ResourceBindings(deployment.Namespace).Get(context.TODO(), resourceBindingName, metav1.GetOptions{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
if len(rb.Status.AggregatedStatus) != 1 {
|
||||
return false, nil
|
||||
}
|
||||
for _, status := range rb.Status.AggregatedStatus {
|
||||
klog.Infof("resourceBinding(%s/%s) on cluster %s got %s, want %s ", deployment.Namespace, resourceBindingName, status.ClusterName, status.Health, result)
|
||||
if status.Health != result {
|
||||
klog.Infof(fmt.Sprintf("ResourceBinding(%s/%s)'s replicas is %d, expected: %d.",
|
||||
resourceBinding.Namespace, resourceBinding.Name, resourceBinding.Spec.Replicas, expectedReplicas))
|
||||
if resourceBinding.Spec.Replicas != expectedReplicas {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
ginkgo.By("deployment healthy", func() {
|
||||
SetReadyReplicas(*deployment.Spec.Replicas)
|
||||
gomega.Eventually(CheckResult(workv1alpha2.ResourceHealthy), pollTimeout, pollInterval).Should(gomega.BeTrue())
|
||||
klog.Infof(fmt.Sprintf("ResourceBinding(%s/%s)'s replicaRequirements is %+v, expected: %+v.",
|
||||
resourceBinding.Namespace, resourceBinding.Name, resourceBinding.Spec.ReplicaRequirements, expectedReplicaRequirements))
|
||||
return reflect.DeepEqual(resourceBinding.Spec.ReplicaRequirements, expectedReplicaRequirements), nil
|
||||
}, pollTimeout, pollInterval).Should(gomega.Equal(true))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation ReviseReplica testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
ReplicaRevision: &configv1alpha1.ReplicaRevision{
|
||||
LuaScript: `
|
||||
function ReviseReplica(obj, desiredReplica)
|
||||
obj.spec.replicas = desiredReplica + 1
|
||||
return obj
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
sumWeight := 0
|
||||
staticWeightLists := make([]policyv1alpha1.StaticClusterWeight, 0)
|
||||
for index, clusterName := range framework.ClusterNames() {
|
||||
staticWeightList := policyv1alpha1.StaticClusterWeight{
|
||||
TargetCluster: policyv1alpha1.ClusterAffinity{
|
||||
ClusterNames: []string{clusterName},
|
||||
},
|
||||
Weight: int64(index + 1),
|
||||
}
|
||||
sumWeight += index + 1
|
||||
staticWeightLists = append(staticWeightLists, staticWeightList)
|
||||
}
|
||||
deployment.Spec.Replicas = pointer.Int32Ptr(int32(sumWeight))
|
||||
policy.Spec.Placement = policyv1alpha1.Placement{
|
||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||
ClusterNames: framework.ClusterNames(),
|
||||
},
|
||||
ReplicaScheduling: &policyv1alpha1.ReplicaSchedulingStrategy{
|
||||
ReplicaDivisionPreference: policyv1alpha1.ReplicaDivisionPreferenceWeighted,
|
||||
ReplicaSchedulingType: policyv1alpha1.ReplicaSchedulingTypeDivided,
|
||||
WeightPreference: &policyv1alpha1.ClusterPreferences{
|
||||
StaticWeightList: staticWeightLists,
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
ginkgo.It("ReviseReplica testing", func() {
|
||||
for index, clusterName := range framework.ClusterNames() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(clusterName, deployment.Namespace, deployment.Name, func(deployment *appsv1.Deployment) bool {
|
||||
return *deployment.Spec.Replicas == int32(index+1)+1
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation Retain testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
Retention: &configv1alpha1.LocalValueRetention{
|
||||
LuaScript: `
|
||||
function Retain(desiredObj, observedObj)
|
||||
desiredObj.spec.paused = observedObj.spec.paused
|
||||
return desiredObj
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.It("Retain testing", func() {
|
||||
ginkgo.By("wait deployment exist on the member clusters", func() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(_ *appsv1.Deployment) bool {
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.By("update deployment on the control plane", func() {
|
||||
// construct two values that need to be changed, and only one value is retained.
|
||||
framework.UpdateDeploymentPaused(kubeClient, deployment, true)
|
||||
framework.UpdateDeploymentReplicas(kubeClient, deployment, 2)
|
||||
})
|
||||
|
||||
ginkgo.By("check if deployment's spec.paused is retained", func() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(deployment *appsv1.Deployment) bool {
|
||||
return *deployment.Spec.Replicas == 2 && !deployment.Spec.Paused
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation AggregateStatus testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
StatusAggregation: &configv1alpha1.StatusAggregation{
|
||||
LuaScript: `
|
||||
function AggregateStatus(desiredObj, statusItems)
|
||||
if statusItems == nil then
|
||||
return desiredObj
|
||||
end
|
||||
if desiredObj.status == nil then
|
||||
desiredObj.status = {}
|
||||
end
|
||||
replicas = 0
|
||||
for i = 1, #statusItems do
|
||||
if statusItems[i].status ~= nil and statusItems[i].status.replicas ~= nil then
|
||||
replicas = replicas + statusItems[i].status.replicas + 1
|
||||
end
|
||||
end
|
||||
desiredObj.status.replicas = replicas
|
||||
return desiredObj
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("AggregateStatus testing", func() {
|
||||
ginkgo.By("check whether the deployment status can be correctly collected", func() {
|
||||
// Only in the current case, a special example is constructed to distinguish the build-in logic.
|
||||
wantedReplicas := *deployment.Spec.Replicas + 1
|
||||
gomega.Eventually(func() bool {
|
||||
var currentDeployment *appsv1.Deployment
|
||||
framework.WaitDeploymentGetByClientFitWith(kubeClient, deployment.Namespace, deployment.Name, func(deployment *appsv1.Deployment) bool {
|
||||
currentDeployment = deployment
|
||||
return true
|
||||
})
|
||||
klog.Infof("deployment(%s/%s) replicas: %d, wanted replicas: %d", deployment.Namespace, deployment.Name, currentDeployment.Status.Replicas, wantedReplicas)
|
||||
return currentDeployment.Status.Replicas == wantedReplicas
|
||||
}, pollTimeout, pollInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation InterpretStatus testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
StatusReflection: &configv1alpha1.StatusReflection{
|
||||
LuaScript: `
|
||||
function ReflectStatus (observedObj)
|
||||
if observedObj.status == nil then
|
||||
return nil
|
||||
end
|
||||
return observedObj.status
|
||||
end`,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("InterpretStatus testing", func() {
|
||||
gomega.Eventually(func(g gomega.Gomega) bool {
|
||||
deploy, err := kubeClient.AppsV1().Deployments(deployment.Namespace).Get(context.TODO(), deployment.Name, metav1.GetOptions{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
return deploy.Status.ReadyReplicas == *deploy.Spec.Replicas
|
||||
}, pollTimeout, pollInterval).Should(gomega.BeTrue())
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation InterpretHealth testing", func() {
|
||||
ginkgo.BeforeEach(func() {
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
HealthInterpretation: &configv1alpha1.HealthInterpretation{
|
||||
LuaScript: `
|
||||
function InterpretHealth(observedObj)
|
||||
return observedObj.status.readyReplicas == observedObj.spec.replicas
|
||||
end `,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("InterpretHealth testing", func() {
|
||||
resourceBindingName := names.GenerateBindingName(deployment.Kind, deployment.Name)
|
||||
SetReadyReplicas := func(readyReplicas int32) {
|
||||
clusterClient := framework.GetClusterClient(targetCluster)
|
||||
gomega.Expect(clusterClient).ShouldNot(gomega.BeNil())
|
||||
var memberDeployment *appsv1.Deployment
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(deployment *appsv1.Deployment) bool {
|
||||
memberDeployment = deployment
|
||||
return true
|
||||
})
|
||||
memberDeployment.Status.ReadyReplicas = readyReplicas
|
||||
framework.UpdateDeploymentStatus(clusterClient, memberDeployment)
|
||||
}
|
||||
|
||||
CheckResult := func(result workv1alpha2.ResourceHealth) interface{} {
|
||||
return func(g gomega.Gomega) (bool, error) {
|
||||
rb, err := karmadaClient.WorkV1alpha2().ResourceBindings(deployment.Namespace).Get(context.TODO(), resourceBindingName, metav1.GetOptions{})
|
||||
g.Expect(err).NotTo(gomega.HaveOccurred())
|
||||
if len(rb.Status.AggregatedStatus) != 1 {
|
||||
return false, nil
|
||||
}
|
||||
for _, status := range rb.Status.AggregatedStatus {
|
||||
klog.Infof("resourceBinding(%s/%s) on cluster %s got %s, want %s ", deployment.Namespace, resourceBindingName, status.ClusterName, status.Health, result)
|
||||
if status.Health != result {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
ginkgo.By("deployment healthy", func() {
|
||||
SetReadyReplicas(*deployment.Spec.Replicas)
|
||||
gomega.Eventually(CheckResult(workv1alpha2.ResourceHealthy), pollTimeout, pollInterval).Should(gomega.BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.Context("InterpreterOperation DependencyInterpretation testing", func() {
|
||||
ginkgo.When("Apply multi ResourceInterpreterCustomization with DependencyInterpretation operation", func() {
|
||||
var customizationAnother *configv1alpha1.ResourceInterpreterCustomization
|
||||
|
||||
var configMapName string
|
||||
var configMap *corev1.ConfigMap
|
||||
|
||||
var saName string
|
||||
var sa *corev1.ServiceAccount
|
||||
|
||||
ginkgo.BeforeEach(func() {
|
||||
targetCluster = framework.ClusterNames()[rand.Intn(len(framework.ClusterNames()))]
|
||||
|
||||
deployment = testhelper.NewDeployment(testNamespace, deploymentNamePrefix+rand.String(RandomStrLength))
|
||||
|
||||
configMapName = configMapNamePrefix + rand.String(RandomStrLength)
|
||||
configMap = testhelper.NewConfigMap(testNamespace, configMapName, map[string]string{"user": "karmada"})
|
||||
deployment.Spec.Template.Spec.Volumes = []corev1.Volume{{
|
||||
Name: "vol-configmap",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: configMapName,
|
||||
}}}}}
|
||||
|
||||
saName = saNamePrefix + rand.String(RandomStrLength)
|
||||
sa = testhelper.NewServiceaccount(testNamespace, saName)
|
||||
deployment = testhelper.NewDeploymentWithServiceAccount(testNamespace, deployment.Name, saName)
|
||||
deployment.Spec.Template.Spec.ServiceAccountName = saName
|
||||
|
||||
policy = testhelper.NewPropagationPolicy(testNamespace, deployment.Name, []policyv1alpha1.ResourceSelector{
|
||||
{
|
||||
APIVersion: deployment.APIVersion,
|
||||
Kind: deployment.Kind,
|
||||
Name: deployment.Name,
|
||||
},
|
||||
}, policyv1alpha1.Placement{
|
||||
ClusterAffinity: &policyv1alpha1.ClusterAffinity{
|
||||
ClusterNames: []string{targetCluster},
|
||||
},
|
||||
})
|
||||
policy.Spec.PropagateDeps = true
|
||||
|
||||
customization = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
|
@ -613,11 +648,11 @@ end `,
|
|||
DependencyInterpretation: &configv1alpha1.DependencyInterpretation{
|
||||
LuaScript: `
|
||||
function GetDependencies(desiredObj)
|
||||
dependentSas = {}
|
||||
refs = {}
|
||||
if desiredObj.spec.template.spec.serviceAccountName ~= '' and desiredObj.spec.template.spec.serviceAccountName ~= 'default' then
|
||||
dependentSas[desiredObj.spec.template.spec.serviceAccountName] = true
|
||||
end
|
||||
dependentSas = {}
|
||||
refs = {}
|
||||
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 = {}
|
||||
|
@ -632,25 +667,84 @@ function GetDependencies(desiredObj)
|
|||
end `,
|
||||
},
|
||||
})
|
||||
})
|
||||
ginkgo.It("DependencyInterpretation testing", func() {
|
||||
ginkgo.By("check if the serviceAccount is propagated automatically", func() {
|
||||
framework.CreateServiceAccount(kubeClient, sa)
|
||||
ginkgo.DeferCleanup(func() {
|
||||
framework.RemoveServiceAccount(kubeClient, sa.GetNamespace(), sa.GetName())
|
||||
})
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(deployment *appsv1.Deployment) bool {
|
||||
return true
|
||||
})
|
||||
|
||||
framework.WaitServiceAccountPresentOnClusterFitWith(targetCluster, deployment.Namespace, sa.GetName(),
|
||||
func(sa *corev1.ServiceAccount) bool {
|
||||
return true
|
||||
})
|
||||
customizationAnother = testhelper.NewResourceInterpreterCustomization(
|
||||
"interpreter-customization"+rand.String(RandomStrLength),
|
||||
configv1alpha1.CustomizationTarget{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
configv1alpha1.CustomizationRules{
|
||||
DependencyInterpretation: &configv1alpha1.DependencyInterpretation{
|
||||
LuaScript: `
|
||||
function GetDependencies(desiredObj)
|
||||
dependentSas = {}
|
||||
refs = {}
|
||||
if desiredObj.spec.template.spec.volumes == nil then
|
||||
return refs
|
||||
end
|
||||
local idx = 1
|
||||
for index, volume in pairs(desiredObj.spec.template.spec.volumes) do
|
||||
if volume.configMap ~= nil then
|
||||
dependObj = {}
|
||||
dependObj.apiVersion = 'v1'
|
||||
dependObj.kind = 'ConfigMap'
|
||||
dependObj.name = volume.configMap.name
|
||||
dependObj.namespace = desiredObj.metadata.namespace
|
||||
refs[idx] = dependObj
|
||||
idx = idx + 1
|
||||
end
|
||||
end
|
||||
return refs
|
||||
end `,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
ginkgo.JustBeforeEach(func() {
|
||||
framework.CreateResourceInterpreterCustomization(karmadaClient, customization)
|
||||
framework.CreateResourceInterpreterCustomization(karmadaClient, customizationAnother)
|
||||
// Wait for resource interpreter informer synced.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
framework.CreateServiceAccount(kubeClient, sa)
|
||||
framework.CreateConfigMap(kubeClient, configMap)
|
||||
|
||||
framework.CreatePropagationPolicy(karmadaClient, policy)
|
||||
framework.CreateDeployment(kubeClient, deployment)
|
||||
|
||||
ginkgo.DeferCleanup(func() {
|
||||
framework.RemoveServiceAccount(kubeClient, sa.Namespace, sa.Name)
|
||||
framework.RemoveConfigMap(kubeClient, configMap.Namespace, configMap.Name)
|
||||
|
||||
framework.RemoveDeployment(kubeClient, deployment.Namespace, deployment.Name)
|
||||
framework.RemovePropagationPolicy(karmadaClient, policy.Namespace, policy.Name)
|
||||
|
||||
framework.DeleteResourceInterpreterCustomization(karmadaClient, customization.Name)
|
||||
framework.DeleteResourceInterpreterCustomization(karmadaClient, customizationAnother.Name)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
ginkgo.Context("InterpreterOperation DependencyInterpretation testing", func() {
|
||||
ginkgo.It("DependencyInterpretation testing", func() {
|
||||
ginkgo.By("check if the resources is propagated automatically", func() {
|
||||
framework.WaitDeploymentPresentOnClusterFitWith(targetCluster, deployment.Namespace, deployment.Name,
|
||||
func(deployment *appsv1.Deployment) bool {
|
||||
return true
|
||||
})
|
||||
|
||||
framework.WaitServiceAccountPresentOnClusterFitWith(targetCluster, sa.Namespace, sa.Name,
|
||||
func(sa *corev1.ServiceAccount) bool {
|
||||
return true
|
||||
})
|
||||
|
||||
framework.WaitConfigMapPresentOnClusterFitWith(targetCluster, configMap.Namespace, configMap.Name,
|
||||
func(configmap *corev1.ConfigMap) bool {
|
||||
return true
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue