support users can define multiple resourceinterpretercustomization

Signed-off-by: changzhen <changzhen5@huawei.com>
This commit is contained in:
changzhen 2022-11-22 17:33:02 +08:00
parent 4870c30f9d
commit b1977be9e5
3 changed files with 256 additions and 27 deletions

View File

@ -1,15 +1,12 @@
package configmanager
import (
"k8s.io/apimachinery/pkg/runtime/schema"
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
)
// CustomConfiguration provides base information about custom interpreter configuration
type CustomConfiguration interface {
Name() string
TargetResource() schema.GroupVersionKind
Merge(rules configv1alpha1.CustomizationRules)
}
// LuaScriptAccessor provides a common interface to get custom interpreter lua script
@ -38,22 +35,35 @@ type resourceCustomAccessor struct {
statusAggregation *configv1alpha1.StatusAggregation
healthInterpretation *configv1alpha1.HealthInterpretation
dependencyInterpretation *configv1alpha1.DependencyInterpretation
configurationName string
configurationTargetGVK schema.GroupVersionKind
}
// NewResourceCustomAccessorAccessor creates an accessor for resource interpreter customization
func NewResourceCustomAccessorAccessor(customization *configv1alpha1.ResourceInterpreterCustomization) CustomAccessor {
return &resourceCustomAccessor{
retention: customization.Spec.Customizations.Retention,
replicaResource: customization.Spec.Customizations.ReplicaResource,
replicaRevision: customization.Spec.Customizations.ReplicaRevision,
statusReflection: customization.Spec.Customizations.StatusReflection,
statusAggregation: customization.Spec.Customizations.StatusAggregation,
healthInterpretation: customization.Spec.Customizations.HealthInterpretation,
dependencyInterpretation: customization.Spec.Customizations.DependencyInterpretation,
configurationName: customization.Name,
configurationTargetGVK: schema.FromAPIVersionAndKind(customization.Spec.Target.APIVersion, customization.Spec.Target.Kind),
// NewResourceCustomAccessor creates an accessor for resource interpreter customization.
func NewResourceCustomAccessor() CustomAccessor {
return &resourceCustomAccessor{}
}
// Merge merges the given CustomizationRules with the current rules, ignore if duplicates occur.
func (a *resourceCustomAccessor) Merge(rules configv1alpha1.CustomizationRules) {
if rules.Retention != nil {
a.setRetain(rules.Retention)
}
if rules.ReplicaResource != nil {
a.setReplicaResource(rules.ReplicaResource)
}
if rules.ReplicaRevision != nil {
a.setReplicaRevision(rules.ReplicaRevision)
}
if rules.StatusReflection != nil {
a.setStatusReflection(rules.StatusReflection)
}
if rules.StatusAggregation != nil {
a.setStatusAggregation(rules.StatusAggregation)
}
if rules.HealthInterpretation != nil {
a.setHealthInterpretation(rules.HealthInterpretation)
}
if rules.DependencyInterpretation != nil {
a.setDependencyInterpretation(rules.DependencyInterpretation)
}
}
@ -106,10 +116,79 @@ func (a *resourceCustomAccessor) GetDependencyInterpretationLuaScript() string {
return a.dependencyInterpretation.LuaScript
}
func (a *resourceCustomAccessor) Name() string {
return a.configurationName
func (a *resourceCustomAccessor) setRetain(retention *configv1alpha1.LocalValueRetention) {
if a.retention == nil {
a.retention = retention
return
}
if retention.LuaScript != "" && a.retention.LuaScript == "" {
a.retention.LuaScript = retention.LuaScript
}
}
func (a *resourceCustomAccessor) TargetResource() schema.GroupVersionKind {
return a.configurationTargetGVK
func (a *resourceCustomAccessor) setReplicaResource(replicaResource *configv1alpha1.ReplicaResourceRequirement) {
if a.replicaResource == nil {
a.replicaResource = replicaResource
return
}
if replicaResource.LuaScript != "" && a.replicaResource.LuaScript == "" {
a.replicaResource.LuaScript = replicaResource.LuaScript
}
}
func (a *resourceCustomAccessor) setReplicaRevision(replicaRevision *configv1alpha1.ReplicaRevision) {
if a.replicaRevision == nil {
a.replicaRevision = replicaRevision
return
}
if replicaRevision.LuaScript != "" && a.replicaRevision.LuaScript == "" {
a.replicaRevision.LuaScript = replicaRevision.LuaScript
}
}
func (a *resourceCustomAccessor) setStatusReflection(statusReflection *configv1alpha1.StatusReflection) {
if a.statusReflection == nil {
a.statusReflection = statusReflection
return
}
if statusReflection.LuaScript != "" && a.statusReflection.LuaScript == "" {
a.statusReflection.LuaScript = statusReflection.LuaScript
}
}
func (a *resourceCustomAccessor) setStatusAggregation(statusAggregation *configv1alpha1.StatusAggregation) {
if a.statusAggregation == nil {
a.statusAggregation = statusAggregation
return
}
if statusAggregation.LuaScript != "" && a.statusAggregation.LuaScript == "" {
a.statusAggregation.LuaScript = statusAggregation.LuaScript
}
}
func (a *resourceCustomAccessor) setHealthInterpretation(healthInterpretation *configv1alpha1.HealthInterpretation) {
if a.healthInterpretation == nil {
a.healthInterpretation = healthInterpretation
return
}
if healthInterpretation.LuaScript != "" && a.healthInterpretation.LuaScript == "" {
a.healthInterpretation.LuaScript = healthInterpretation.LuaScript
}
}
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
}
}

View File

@ -2,6 +2,7 @@ package configmanager
import (
"fmt"
"sort"
"sync/atomic"
"k8s.io/apimachinery/pkg/labels"
@ -82,17 +83,34 @@ func (configManager *interpreterConfigManager) updateConfiguration() {
utilruntime.HandleError(fmt.Errorf("error updating configuration: %v", err))
return
}
configs := make(map[schema.GroupVersionKind]CustomAccessor, len(configurations))
for _, c := range configurations {
configs := make([]*configv1alpha1.ResourceInterpreterCustomization, len(configurations))
for index, c := range configurations {
config := &configv1alpha1.ResourceInterpreterCustomization{}
if err = helper.ConvertToTypedObject(c, config); err != nil {
klog.Errorf("Failed to transform ResourceInterpreterCustomization: %w", err)
return
}
key := schema.FromAPIVersionAndKind(config.Spec.Target.APIVersion, config.Spec.Target.Kind)
configs[key] = NewResourceCustomAccessorAccessor(config)
configs[index] = config
}
configManager.configuration.Store(configs)
sort.Slice(configs, func(i, j int) bool {
return configs[i].Name < configs[j].Name
})
accessors := make(map[schema.GroupVersionKind]CustomAccessor)
for _, config := range configs {
key := schema.FromAPIVersionAndKind(config.Spec.Target.APIVersion, config.Spec.Target.Kind)
var accessor CustomAccessor
var ok bool
if accessor, ok = accessors[key]; !ok {
accessor = NewResourceCustomAccessor()
}
accessor.Merge(config.Spec.Customizations)
accessors[key] = accessor
}
configManager.configuration.Store(accessors)
configManager.initialSynced.Store(true)
}

View File

@ -0,0 +1,132 @@
package configmanager
import (
"reflect"
"testing"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic/fake"
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
"github.com/karmada-io/karmada/pkg/util/fedinformer/genericmanager"
"github.com/karmada-io/karmada/pkg/util/gclient"
)
func Test_interpreterConfigManager_LuaScriptAccessors(t *testing.T) {
customization01 := &configv1alpha1.ResourceInterpreterCustomization{
ObjectMeta: metav1.ObjectMeta{Name: "customization01"},
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
Target: configv1alpha1.CustomizationTarget{
APIVersion: appsv1.SchemeGroupVersion.String(),
Kind: "Deployment",
},
Customizations: configv1alpha1.CustomizationRules{
Retention: &configv1alpha1.LocalValueRetention{LuaScript: "a=0"},
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "b=0"},
},
},
}
customization02 := &configv1alpha1.ResourceInterpreterCustomization{
ObjectMeta: metav1.ObjectMeta{Name: "customization02"},
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
Target: configv1alpha1.CustomizationTarget{
APIVersion: appsv1.SchemeGroupVersion.String(),
Kind: "Deployment",
},
Customizations: configv1alpha1.CustomizationRules{
ReplicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: "c=0"},
StatusReflection: &configv1alpha1.StatusReflection{LuaScript: "d=0"},
},
},
}
customization03 := &configv1alpha1.ResourceInterpreterCustomization{
ObjectMeta: metav1.ObjectMeta{Name: "customization03"},
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
Target: configv1alpha1.CustomizationTarget{
APIVersion: appsv1.SchemeGroupVersion.String(),
Kind: "Deployment",
},
Customizations: configv1alpha1.CustomizationRules{
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "b=1"},
ReplicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: "c=1"},
},
},
}
deploymentGVK := schema.GroupVersionKind{Group: "apps", Version: "v1", Kind: "Deployment"}
type args struct {
customizations []runtime.Object
}
tests := []struct {
name string
args args
want map[schema.GroupVersionKind]CustomAccessor
}{
{
name: "single ResourceInterpreterCustomization",
args: args{[]runtime.Object{customization01}},
want: map[schema.GroupVersionKind]CustomAccessor{
deploymentGVK: &resourceCustomAccessor{
retention: &configv1alpha1.LocalValueRetention{LuaScript: "a=0"},
replicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "b=0"},
},
},
},
{
name: "multi ResourceInterpreterCustomization with no redundant operation",
args: args{[]runtime.Object{customization01, customization02}},
want: map[schema.GroupVersionKind]CustomAccessor{
deploymentGVK: &resourceCustomAccessor{
retention: &configv1alpha1.LocalValueRetention{LuaScript: "a=0"},
replicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "b=0"},
replicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: "c=0"},
statusReflection: &configv1alpha1.StatusReflection{LuaScript: "d=0"},
},
},
},
{
name: "multi ResourceInterpreterCustomization with redundant operation ",
args: args{[]runtime.Object{customization03, customization02, customization01}},
want: map[schema.GroupVersionKind]CustomAccessor{
deploymentGVK: &resourceCustomAccessor{
retention: &configv1alpha1.LocalValueRetention{LuaScript: "a=0"},
replicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "b=0"},
replicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: "c=0"},
statusReflection: &configv1alpha1.StatusReflection{LuaScript: "d=0"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
stopCh := make(chan struct{})
defer close(stopCh)
client := fake.NewSimpleDynamicClient(gclient.NewSchema(), tt.args.customizations...)
informer := genericmanager.NewSingleClusterInformerManager(client, 0, stopCh)
configManager := NewInterpreterConfigManager(informer)
informer.Start()
defer informer.Stop()
informer.WaitForCacheSync()
if !configManager.HasSynced() {
t.Errorf("informer has not been synced")
}
gotAccessors := configManager.LuaScriptAccessors()
for gvk, gotAccessor := range gotAccessors {
wantAccessor, ok := tt.want[gvk]
if !ok {
t.Errorf("Can not find the target gvk %v", gvk)
}
if !reflect.DeepEqual(gotAccessor, wantAccessor) {
t.Errorf("LuaScriptAccessors() = %v, want %v", gotAccessor, wantAccessor)
}
}
})
}
}