Merge pull request #3066 from XiShanYongYe-Chang/add-ut-for-strategy
Add ut for cluster registry
This commit is contained in:
commit
9fc1a124b5
|
@ -24,6 +24,7 @@ import (
|
|||
netutils "k8s.io/utils/net"
|
||||
|
||||
"github.com/karmada-io/karmada/pkg/aggregatedapiserver"
|
||||
clusterscheme "github.com/karmada-io/karmada/pkg/apis/cluster/scheme"
|
||||
clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1"
|
||||
pkgfeatures "github.com/karmada-io/karmada/pkg/features"
|
||||
clientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
|
||||
|
@ -54,7 +55,7 @@ func NewOptions() *Options {
|
|||
o := &Options{
|
||||
RecommendedOptions: genericoptions.NewRecommendedOptions(
|
||||
defaultEtcdPathPrefix,
|
||||
aggregatedapiserver.Codecs.LegacyCodec(clusterv1alpha1.SchemeGroupVersion)),
|
||||
clusterscheme.Codecs.LegacyCodec(clusterv1alpha1.SchemeGroupVersion)),
|
||||
}
|
||||
o.RecommendedOptions.Etcd.StorageConfig.EncodeVersioner = runtime.NewMultiGroupVersioner(clusterv1alpha1.SchemeGroupVersion, schema.GroupKind{Group: clusterv1alpha1.GroupName})
|
||||
return o
|
||||
|
@ -126,10 +127,10 @@ func (o *Options) Config() (*aggregatedapiserver.Config, error) {
|
|||
}
|
||||
o.RecommendedOptions.Features = &genericoptions.FeatureOptions{EnableProfiling: false}
|
||||
|
||||
serverConfig := genericapiserver.NewRecommendedConfig(aggregatedapiserver.Codecs)
|
||||
serverConfig := genericapiserver.NewRecommendedConfig(clusterscheme.Codecs)
|
||||
serverConfig.LongRunningFunc = customLongRunningRequestCheck(sets.NewString("watch", "proxy"),
|
||||
sets.NewString("attach", "exec", "proxy", "log", "portforward"))
|
||||
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapi.NewDefinitionNamer(aggregatedapiserver.Scheme))
|
||||
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapi.NewDefinitionNamer(clusterscheme.Scheme))
|
||||
serverConfig.OpenAPIConfig.Info.Title = "Karmada"
|
||||
if err := o.RecommendedOptions.ApplyTo(serverConfig); err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
package aggregatedapiserver
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
|
@ -12,38 +8,10 @@ import (
|
|||
"k8s.io/klog/v2"
|
||||
|
||||
clusterapis "github.com/karmada-io/karmada/pkg/apis/cluster"
|
||||
clusterinstall "github.com/karmada-io/karmada/pkg/apis/cluster/install"
|
||||
clusterscheme "github.com/karmada-io/karmada/pkg/apis/cluster/scheme"
|
||||
clusterstorage "github.com/karmada-io/karmada/pkg/registry/cluster/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
// Scheme defines methods for serializing and deserializing API objects.
|
||||
Scheme = runtime.NewScheme()
|
||||
// Codecs provides methods for retrieving codecs and serializers for specific
|
||||
// versions and content types.
|
||||
Codecs = serializer.NewCodecFactory(Scheme)
|
||||
// ParameterCodec handles versioning of objects that are converted to query parameters.
|
||||
ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||
)
|
||||
|
||||
func init() {
|
||||
clusterinstall.Install(Scheme)
|
||||
|
||||
// we need to add the options to empty v1
|
||||
// TODO fix the server code to avoid this
|
||||
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
|
||||
|
||||
// TODO: keep the generic API server from wanting this
|
||||
unversioned := schema.GroupVersion{Group: "", Version: "v1"}
|
||||
Scheme.AddUnversionedTypes(unversioned,
|
||||
&metav1.Status{},
|
||||
&metav1.APIVersions{},
|
||||
&metav1.APIGroupList{},
|
||||
&metav1.APIGroup{},
|
||||
&metav1.APIResourceList{},
|
||||
)
|
||||
}
|
||||
|
||||
// ExtraConfig holds custom apiserver config
|
||||
type ExtraConfig struct {
|
||||
// Add custom config if necessary.
|
||||
|
@ -95,9 +63,9 @@ func (c completedConfig) New(kubeClient kubernetes.Interface) (*APIServer, error
|
|||
GenericAPIServer: genericServer,
|
||||
}
|
||||
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(clusterapis.GroupName, Scheme, ParameterCodec, Codecs)
|
||||
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(clusterapis.GroupName, clusterscheme.Scheme, clusterscheme.ParameterCodec, clusterscheme.Codecs)
|
||||
|
||||
clusterStorage, err := clusterstorage.NewStorage(Scheme, kubeClient, c.GenericConfig.RESTOptionsGetter)
|
||||
clusterStorage, err := clusterstorage.NewStorage(clusterscheme.Scheme, kubeClient, c.GenericConfig.RESTOptionsGetter)
|
||||
if err != nil {
|
||||
klog.Errorf("Unable to create REST storage for a resource due to %v, will die", err)
|
||||
return nil, err
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package scheme
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
|
||||
clusterinstall "github.com/karmada-io/karmada/pkg/apis/cluster/install"
|
||||
)
|
||||
|
||||
var (
|
||||
// Scheme defines methods for serializing and deserializing API objects.
|
||||
Scheme = runtime.NewScheme()
|
||||
// Codecs provides methods for retrieving codecs and serializers for specific
|
||||
// versions and content types.
|
||||
Codecs = serializer.NewCodecFactory(Scheme)
|
||||
// ParameterCodec handles versioning of objects that are converted to query parameters.
|
||||
ParameterCodec = runtime.NewParameterCodec(Scheme)
|
||||
)
|
||||
|
||||
func init() {
|
||||
clusterinstall.Install(Scheme)
|
||||
|
||||
// we need to add the options to empty v1
|
||||
// TODO fix the server code to avoid this
|
||||
metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
|
||||
|
||||
// TODO: keep the generic API server from wanting this
|
||||
unversioned := schema.GroupVersion{Group: "", Version: "v1"}
|
||||
Scheme.AddUnversionedTypes(unversioned,
|
||||
&metav1.Status{},
|
||||
&metav1.APIVersions{},
|
||||
&metav1.APIGroupList{},
|
||||
&metav1.APIGroup{},
|
||||
&metav1.APIResourceList{},
|
||||
)
|
||||
}
|
|
@ -0,0 +1,323 @@
|
|||
package cluster
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
runtimeutil "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/component-base/featuregate"
|
||||
|
||||
clusterapis "github.com/karmada-io/karmada/pkg/apis/cluster"
|
||||
"github.com/karmada-io/karmada/pkg/apis/cluster/mutation"
|
||||
clusterscheme "github.com/karmada-io/karmada/pkg/apis/cluster/scheme"
|
||||
"github.com/karmada-io/karmada/pkg/features"
|
||||
_ "github.com/karmada-io/karmada/pkg/features"
|
||||
)
|
||||
|
||||
func getValidCluster(name string) *clusterapis.Cluster {
|
||||
return &clusterapis.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: clusterapis.ClusterSpec{
|
||||
ID: "abc",
|
||||
APIEndpoint: "http://0.0.0.0:80",
|
||||
SyncMode: clusterapis.Push,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setFeatureGateDuringTest(tb testing.TB, gate featuregate.FeatureGate, f featuregate.Feature, value bool) func() {
|
||||
originalValue := gate.Enabled(f)
|
||||
|
||||
if err := gate.(featuregate.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, value)); err != nil {
|
||||
tb.Errorf("error setting %s=%v: %v", f, value, err)
|
||||
}
|
||||
|
||||
return func() {
|
||||
if err := gate.(featuregate.MutableFeatureGate).Set(fmt.Sprintf("%s=%v", f, originalValue)); err != nil {
|
||||
tb.Errorf("error restoring %s=%v: %v", f, originalValue, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_NamespaceScoped(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
if clusterStrategy.NamespaceScoped() {
|
||||
t.Errorf("Cluster must be cluster scoped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_PrepareForCreate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
ctx := request.NewContext()
|
||||
|
||||
defaultResourceModelCluster := getValidCluster("m1")
|
||||
mutation.SetDefaultClusterResourceModels(defaultResourceModelCluster)
|
||||
|
||||
standardResourceModelClusterBefore := getValidCluster("m2")
|
||||
standardResourceModelClusterBefore.Spec.ResourceModels = []clusterapis.ResourceModel{
|
||||
{
|
||||
Grade: 2,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(math.MaxInt64, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Grade: 1,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(0, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
standardResourceModelClusterAfter := getValidCluster("m2")
|
||||
standardResourceModelClusterAfter.Spec.ResourceModels = []clusterapis.ResourceModel{
|
||||
{
|
||||
Grade: 1,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(0, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Grade: 2,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(math.MaxInt64, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
object runtime.Object
|
||||
expect runtime.Object
|
||||
gateFlag bool
|
||||
}{
|
||||
{
|
||||
name: "featureGate CustomizedClusterResourceModeling is false",
|
||||
object: getValidCluster("cluster"),
|
||||
expect: getValidCluster("cluster"),
|
||||
gateFlag: false,
|
||||
},
|
||||
{
|
||||
name: "featureGate CustomizedClusterResourceModeling is true and cluster.Spec.ResourceModels is nil",
|
||||
object: getValidCluster("m1"),
|
||||
expect: defaultResourceModelCluster,
|
||||
gateFlag: true,
|
||||
},
|
||||
{
|
||||
name: "featureGate CustomizedClusterResourceModeling is true and cluster.Spec.ResourceModels is not nil",
|
||||
object: standardResourceModelClusterBefore,
|
||||
expect: standardResourceModelClusterAfter,
|
||||
gateFlag: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
runtimeutil.Must(utilfeature.DefaultMutableFeatureGate.Add(features.DefaultFeatureGates))
|
||||
setFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, features.CustomizedClusterResourceModeling, tt.gateFlag)
|
||||
|
||||
clusterStrategy.PrepareForCreate(ctx, tt.object)
|
||||
if !reflect.DeepEqual(tt.expect, tt.object) {
|
||||
t.Errorf("Object mismatch! Excepted: \n%#v \ngot: \n%#v", tt.expect, tt.object)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_PrepareForUpdate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
ctx := request.NewContext()
|
||||
|
||||
standardResourceModelClusterBefore := getValidCluster("m2")
|
||||
standardResourceModelClusterBefore.Spec.ResourceModels = []clusterapis.ResourceModel{
|
||||
{
|
||||
Grade: 2,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(math.MaxInt64, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Grade: 1,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(0, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
standardResourceModelClusterAfter := getValidCluster("m2")
|
||||
standardResourceModelClusterAfter.Spec.ResourceModels = []clusterapis.ResourceModel{
|
||||
{
|
||||
Grade: 1,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(0, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Grade: 2,
|
||||
Ranges: []clusterapis.ResourceModelRange{
|
||||
{
|
||||
Name: clusterapis.ResourceCPU,
|
||||
Min: *resource.NewQuantity(2, resource.DecimalSI),
|
||||
Max: *resource.NewQuantity(math.MaxInt64, resource.DecimalSI),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
object runtime.Object
|
||||
expect runtime.Object
|
||||
gateFlag bool
|
||||
}{
|
||||
{
|
||||
name: "featureGate CustomizedClusterResourceModeling is false",
|
||||
object: getValidCluster("cluster"),
|
||||
expect: getValidCluster("cluster"),
|
||||
gateFlag: false,
|
||||
},
|
||||
{
|
||||
name: "featureGate CustomizedClusterResourceModeling is true and cluster.Spec.ResourceModels is nil",
|
||||
object: getValidCluster("m1"),
|
||||
expect: getValidCluster("m1"),
|
||||
gateFlag: true,
|
||||
},
|
||||
{
|
||||
name: "featureGate CustomizedClusterResourceModeling is true and cluster.Spec.ResourceModels is not nil",
|
||||
object: standardResourceModelClusterBefore,
|
||||
expect: standardResourceModelClusterAfter,
|
||||
gateFlag: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
runtimeutil.Must(utilfeature.DefaultMutableFeatureGate.Add(features.DefaultFeatureGates))
|
||||
setFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, features.CustomizedClusterResourceModeling, tt.gateFlag)
|
||||
|
||||
clusterStrategy.PrepareForUpdate(ctx, tt.object, nil)
|
||||
if !reflect.DeepEqual(tt.expect, tt.object) {
|
||||
t.Errorf("Object mismatch! Excepted: \n%#v \ngot: \n%#b", tt.expect, tt.object)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_Validate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
ctx := request.NewContext()
|
||||
cluster := getValidCluster("cluster")
|
||||
|
||||
errs := clusterStrategy.Validate(ctx, cluster)
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("Cluster is validate but got errs: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_WarningsOnCreate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
ctx := request.NewContext()
|
||||
cluster := getValidCluster("cluster")
|
||||
|
||||
wrs := clusterStrategy.WarningsOnCreate(ctx, cluster)
|
||||
if len(wrs) > 0 {
|
||||
t.Errorf("Cluster is validate but go warings: %v", wrs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_AllowCreateOnUpdate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
if clusterStrategy.AllowCreateOnUpdate() {
|
||||
t.Errorf("Cluster do not allow create on update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_AllowUnconditionalUpdate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
if !clusterStrategy.AllowUnconditionalUpdate() {
|
||||
t.Errorf("Cluster can be updated unconditionally on update")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_Canonicalize(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
clusterBefore := getValidCluster("cluster")
|
||||
clusterAfter := getValidCluster("cluster")
|
||||
clusterAfter.Spec.Taints = []corev1.Taint{
|
||||
{
|
||||
Key: "foo",
|
||||
Value: "abc",
|
||||
Effect: corev1.TaintEffectNoSchedule,
|
||||
},
|
||||
{
|
||||
Key: "bar",
|
||||
Effect: corev1.TaintEffectNoExecute,
|
||||
},
|
||||
}
|
||||
|
||||
clusterStrategy.Canonicalize(clusterBefore)
|
||||
if !reflect.DeepEqual(clusterAfter, clusterAfter) {
|
||||
t.Errorf("Object mismatch! Excepted: \n%#v \ngot: \n%#v", clusterAfter, clusterAfter)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_ValidateUpdate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
ctx := request.NewContext()
|
||||
clusterOld := getValidCluster("cluster")
|
||||
clusterOld.ResourceVersion = "abc"
|
||||
clusterNew := getValidCluster("cluster")
|
||||
clusterNew.ResourceVersion = "def"
|
||||
|
||||
errs := clusterStrategy.ValidateUpdate(ctx, clusterOld, clusterNew)
|
||||
if len(errs) > 0 {
|
||||
t.Errorf("Clusters old and new are both validate but got errs: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrategy_WarningsOnUpdate(t *testing.T) {
|
||||
clusterStrategy := NewStrategy(clusterscheme.Scheme)
|
||||
ctx := request.NewContext()
|
||||
cluster := getValidCluster("cluster")
|
||||
|
||||
wrs := clusterStrategy.WarningsOnUpdate(ctx, cluster, nil)
|
||||
if len(wrs) > 0 {
|
||||
t.Errorf("Cluster is validate but go warings: %v", wrs)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue