katalyst-core/pkg/controller/kcc/kcct_test.go

487 lines
14 KiB
Go

/*
Copyright 2022 The Katalyst Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kcc
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/tools/cache"
"k8s.io/klog/v2"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"github.com/kubewharf/katalyst-api/pkg/apis/config/v1alpha1"
katalyst_base "github.com/kubewharf/katalyst-core/cmd/base"
"github.com/kubewharf/katalyst-core/pkg/config"
kcctarget "github.com/kubewharf/katalyst-core/pkg/controller/kcc/target"
"github.com/kubewharf/katalyst-core/pkg/metrics"
"github.com/kubewharf/katalyst-core/pkg/util"
"github.com/kubewharf/katalyst-core/pkg/util/native"
)
func toTestUnstructured(obj interface{}) *unstructured.Unstructured {
ret, err := native.ToUnstructured(obj)
if err != nil {
klog.Error(err)
}
return ret
}
func generateTestLabelSelectorTargetResource(name, labelSelector string) util.KCCTargetResource {
return util.ToKCCTargetResource(toTestUnstructured(&v1alpha1.EvictionConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1alpha1.EvictionConfigurationSpec{
GenericConfigSpec: v1alpha1.GenericConfigSpec{
NodeLabelSelector: labelSelector,
},
},
}))
}
func generateTestNodeNamesTargetResource(name string, nodeNames []string) util.KCCTargetResource {
return util.ToKCCTargetResource(toTestUnstructured(&v1alpha1.EvictionConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Spec: v1alpha1.EvictionConfigurationSpec{
GenericConfigSpec: v1alpha1.GenericConfigSpec{
EphemeralSelector: v1alpha1.EphemeralSelector{
NodeNames: nodeNames,
},
},
},
}))
}
func TestKatalystCustomConfigTargetController_Run(t *testing.T) {
type args struct {
kccList []runtime.Object
kccTargetList []runtime.Object
kccConfig *config.Configuration
}
tests := []struct {
name string
args args
}{
{
name: "kcc and kcc target are all valid",
args: args{
kccList: []runtime.Object{
&v1alpha1.KatalystCustomConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "test-kcc",
Namespace: "default",
},
Spec: v1alpha1.KatalystCustomConfigSpec{
TargetType: metav1.GroupVersionResource{
Group: v1alpha1.SchemeGroupVersion.Group,
Version: v1alpha1.SchemeGroupVersion.Version,
Resource: v1alpha1.ResourceNameEvictionConfigurations,
},
NodeLabelSelectorKey: "aa",
},
},
},
kccTargetList: []runtime.Object{
&v1alpha1.EvictionConfiguration{
TypeMeta: metav1.TypeMeta{
Kind: "EvictionConfiguration",
APIVersion: "config.katalyst.kubewharf.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "default",
Namespace: "default",
},
Spec: v1alpha1.EvictionConfigurationSpec{
Config: v1alpha1.EvictionConfig{
EvictionPluginsConfig: v1alpha1.EvictionPluginsConfig{
ReclaimedResourcesEvictionPluginConfig: v1alpha1.ReclaimedResourcesEvictionPluginConfig{
EvictionThreshold: map[v1.ResourceName]float64{
v1.ResourceCPU: 5.0,
},
},
},
},
},
},
&v1alpha1.EvictionConfiguration{
TypeMeta: metav1.TypeMeta{
Kind: "EvictionConfiguration",
APIVersion: "config.katalyst.kubewharf.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "aa=bb",
Namespace: "default",
},
Spec: v1alpha1.EvictionConfigurationSpec{
GenericConfigSpec: v1alpha1.GenericConfigSpec{
NodeLabelSelector: "aa=bb",
},
Config: v1alpha1.EvictionConfig{
EvictionPluginsConfig: v1alpha1.EvictionPluginsConfig{
ReclaimedResourcesEvictionPluginConfig: v1alpha1.ReclaimedResourcesEvictionPluginConfig{
EvictionThreshold: map[v1.ResourceName]float64{
v1.ResourceCPU: 5.0,
},
},
},
},
},
},
&v1alpha1.EvictionConfiguration{
TypeMeta: metav1.TypeMeta{
Kind: "EvictionConfiguration",
APIVersion: "config.katalyst.kubewharf.io/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "node-1",
Namespace: "default",
CreationTimestamp: metav1.Now(),
},
Spec: v1alpha1.EvictionConfigurationSpec{
GenericConfigSpec: v1alpha1.GenericConfigSpec{
EphemeralSelector: v1alpha1.EphemeralSelector{
NodeNames: []string{
"node-1",
},
LastDuration: &metav1.Duration{
Duration: 10 * time.Second,
},
},
},
Config: v1alpha1.EvictionConfig{
EvictionPluginsConfig: v1alpha1.EvictionPluginsConfig{
ReclaimedResourcesEvictionPluginConfig: v1alpha1.ReclaimedResourcesEvictionPluginConfig{
EvictionThreshold: map[v1.ResourceName]float64{
v1.ResourceCPU: 5.0,
},
},
},
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
genericContext, err := katalyst_base.GenerateFakeGenericContext(nil, tt.args.kccList, tt.args.kccTargetList)
assert.NoError(t, err)
conf := generateTestConfiguration(t)
ctx := context.Background()
targetHandler := kcctarget.NewKatalystCustomConfigTargetHandler(
ctx,
genericContext.Client,
conf.ControllersConfiguration.KCCConfig,
genericContext.InternalInformerFactory.Config().V1alpha1().KatalystCustomConfigs(),
)
kcc, err := NewKatalystCustomConfigTargetController(
ctx,
conf.GenericConfiguration,
conf.GenericControllerConfiguration,
conf.KCCConfig,
genericContext.Client,
genericContext.InternalInformerFactory.Config().V1alpha1().KatalystCustomConfigs(),
metrics.DummyMetrics{},
targetHandler,
)
assert.NoError(t, err)
genericContext.StartInformer(ctx)
go targetHandler.Run()
go kcc.Run()
cache.WaitForCacheSync(kcc.ctx.Done(), kcc.syncedFunc...)
time.Sleep(1 * time.Second)
})
}
}
func Test_validateLabelSelectorWithOthers(t *testing.T) {
type args struct {
labelSelector string
targetResource util.KCCTargetResource
otherResources []util.KCCTargetResource
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "test-1",
args: args{
labelSelector: "aa",
targetResource: generateTestLabelSelectorTargetResource("1", "aa=bb"),
otherResources: []util.KCCTargetResource{
generateTestLabelSelectorTargetResource("2", "aa=cc"),
},
},
want: true,
},
{
name: "test-2",
args: args{
labelSelector: "aa",
targetResource: generateTestLabelSelectorTargetResource("1", "aa=bb"),
otherResources: []util.KCCTargetResource{
generateTestLabelSelectorTargetResource("2", "aa in (cc,dd)"),
},
},
want: true,
},
{
name: "test-3",
args: args{
labelSelector: "aa",
targetResource: generateTestLabelSelectorTargetResource("1", "aa=bb"),
otherResources: []util.KCCTargetResource{
generateTestLabelSelectorTargetResource("2", "aa in (bb,cc)"),
},
},
want: false,
},
{
name: "test-4",
args: args{
labelSelector: "aa",
targetResource: generateTestLabelSelectorTargetResource("1", "aa=bb"),
otherResources: []util.KCCTargetResource{
generateTestLabelSelectorTargetResource("2", "aa notin (bb,cc)"),
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, _, _, err := validateLabelSelectorOverlapped(tt.args.labelSelector, tt.args.targetResource, tt.args.otherResources)
if (err != nil) != tt.wantErr {
t.Errorf("validateLabelSelectorOverlapped() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("validateLabelSelectorOverlapped() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_validateTargetResourceNodeNamesWithOthers(t *testing.T) {
type args struct {
targetResource util.KCCTargetResource
otherResources []util.KCCTargetResource
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "test-1",
args: args{
targetResource: generateTestNodeNamesTargetResource("1", []string{"node-1"}),
otherResources: []util.KCCTargetResource{
generateTestNodeNamesTargetResource("2", []string{"node-2"}),
},
},
want: true,
},
{
name: "test-2",
args: args{
targetResource: generateTestNodeNamesTargetResource("1", []string{"node-1"}),
otherResources: []util.KCCTargetResource{
generateTestNodeNamesTargetResource("2", []string{"node-2", "node-3"}),
},
},
want: true,
},
{
name: "test-3",
args: args{
targetResource: generateTestNodeNamesTargetResource("1", []string{"node-1"}),
otherResources: []util.KCCTargetResource{
generateTestNodeNamesTargetResource("2", []string{"node-1", "node-3"}),
},
},
want: false,
},
{
name: "test-4",
args: args{
targetResource: generateTestNodeNamesTargetResource("1", []string{"node-1", "node-2"}),
otherResources: []util.KCCTargetResource{
generateTestNodeNamesTargetResource("2", []string{"node-3", "node-4"}),
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, _, _, err := validateTargetResourceNodeNamesOverlapped(tt.args.targetResource, tt.args.otherResources)
if (err != nil) != tt.wantErr {
t.Errorf("validateTargetResourceNodeNamesOverlapped() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("validateTargetResourceNodeNamesOverlapped() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_validateTargetResourceGlobalWithOthers(t *testing.T) {
type args struct {
targetResource util.KCCTargetResource
otherResources []util.KCCTargetResource
}
tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "test-1",
args: args{
targetResource: generateTestLabelSelectorTargetResource("1", ""),
otherResources: []util.KCCTargetResource{
generateTestLabelSelectorTargetResource("2", ""),
},
},
want: false,
},
{
name: "test-2",
args: args{
targetResource: generateTestLabelSelectorTargetResource("1", ""),
otherResources: []util.KCCTargetResource{
generateTestLabelSelectorTargetResource("1", ""),
generateTestLabelSelectorTargetResource("2", "aa=bb"),
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, _, _, err := validateTargetResourceGlobalOverlapped(tt.args.targetResource, tt.args.otherResources)
if (err != nil) != tt.wantErr {
t.Errorf("validateTargetResourceGlobalOverlapped() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("validateTargetResourceGlobalOverlapped() got = %v, want %v", got, tt.want)
}
})
}
}
func Test_updateTargetResourceStatus(t *testing.T) {
type args struct {
targetResource util.KCCTargetResource
isValid bool
msg string
reason string
}
tests := []struct {
name string
args args
wantResource util.KCCTargetResource
equalFunc func(util.KCCTargetResource, util.KCCTargetResource) bool
}{
{
name: "test-1",
args: args{
targetResource: util.ToKCCTargetResource(toTestUnstructured(&v1alpha1.EvictionConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "config-1",
},
Spec: v1alpha1.EvictionConfigurationSpec{
GenericConfigSpec: v1alpha1.GenericConfigSpec{
NodeLabelSelector: "aa=bb",
},
},
})),
isValid: false,
msg: "ssasfr",
reason: "dasf",
},
wantResource: util.ToKCCTargetResource(toTestUnstructured(&v1alpha1.EvictionConfiguration{
ObjectMeta: metav1.ObjectMeta{
Name: "config-1",
},
Spec: v1alpha1.EvictionConfigurationSpec{
GenericConfigSpec: v1alpha1.GenericConfigSpec{
NodeLabelSelector: "aa=bb",
},
},
Status: v1alpha1.GenericConfigStatus{
Conditions: []v1alpha1.GenericConfigCondition{
{
Type: v1alpha1.ConfigConditionTypeValid,
Status: v1.ConditionFalse,
Reason: "dasf",
Message: "ssasfr",
},
},
},
})),
equalFunc: func(t1 util.KCCTargetResource, t2 util.KCCTargetResource) bool {
status1 := t1.GetGenericStatus()
status2 := t2.GetGenericStatus()
if len(status1.Conditions) != len(status2.Conditions) {
return false
}
status1.Conditions[0].LastTransitionTime = metav1.Time{}
status2.Conditions[0].LastTransitionTime = metav1.Time{}
t1.SetGenericStatus(status1)
t2.SetGenericStatus(status2)
if !apiequality.Semantic.DeepEqual(t1, t2) {
return false
}
return true
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if updateTargetResourceStatus(tt.args.targetResource, tt.args.isValid, tt.args.msg, tt.args.reason); !tt.equalFunc(tt.args.targetResource, tt.wantResource) {
t.Errorf("updateTargetResourceStatus() = %v, want %v", tt.args.targetResource.GetGenericStatus(), tt.wantResource.GetGenericStatus())
}
})
}
}