pkg/karmadactl: unit test promote

In this commit, we unti test promoting a namespace-scoped or
cluster-scoped resource in a legacy clsuter and validating the promote
options on karmadactl command.

Signed-off-by: Mohamed Awnallah <mohamedmohey2352@gmail.com>
This commit is contained in:
Mohamed Awnallah 2024-11-28 20:20:15 +02:00
parent 1bc955a23c
commit 6af9994ca7
2 changed files with 550 additions and 26 deletions

View File

@ -18,6 +18,7 @@ package promote
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"os" "os"
@ -93,6 +94,18 @@ var (
%[1]s promote deployment nginx -n default -C cluster1 --cluster-kubeconfig=<CLUSTER_KUBECONFIG_PATH> --cluster-context=<CLUSTER_CONTEXT>`) %[1]s promote deployment nginx -n default -C cluster1 --cluster-kubeconfig=<CLUSTER_KUBECONFIG_PATH> --cluster-context=<CLUSTER_CONTEXT>`)
) )
var (
dynamicClientBuilder = func(cfg *rest.Config) dynamic.Interface {
return dynamic.NewForConfigOrDie(cfg)
}
kubeClientBuilder = func(cfg *rest.Config) kubeclientset.Interface {
return kubeclientset.NewForConfigOrDie(cfg)
}
karmadaClientBuilder = func(cfg *rest.Config) karmadaclientset.Interface {
return karmadaclientset.NewForConfigOrDie(cfg)
}
)
// NewCmdPromote defines the `promote` command that promote resources from legacy clusters // NewCmdPromote defines the `promote` command that promote resources from legacy clusters
func NewCmdPromote(f util.Factory, parentCommand string) *cobra.Command { func NewCmdPromote(f util.Factory, parentCommand string) *cobra.Command {
opts := CommandPromoteOption{} opts := CommandPromoteOption{}
@ -197,7 +210,7 @@ func (o *CommandPromoteOption) Complete(f util.Factory, args []string) error {
var err error var err error
if len(args) != 2 { if len(args) != 2 {
return fmt.Errorf("incorrect command format, please use correct command format") return errors.New("incorrect command format, please use correct command format")
} }
o.name = args[1] o.name = args[1]
@ -235,11 +248,11 @@ func (o *CommandPromoteOption) Complete(f util.Factory, args []string) error {
// Validate checks to the PromoteOptions to see if there is sufficient information run the command // Validate checks to the PromoteOptions to see if there is sufficient information run the command
func (o *CommandPromoteOption) Validate() error { func (o *CommandPromoteOption) Validate() error {
if o.Cluster == "" { if o.Cluster == "" {
return fmt.Errorf("the cluster cannot be empty") return errors.New("the cluster cannot be empty")
} }
if o.OutputFormat != "" && o.OutputFormat != "yaml" && o.OutputFormat != "json" { if o.OutputFormat != "" && o.OutputFormat != "yaml" && o.OutputFormat != "json" {
return fmt.Errorf("output format is only one of json and yaml") return errors.New("invalid output format: supported formats are json and yaml")
} }
return nil return nil
@ -294,11 +307,11 @@ func (o *CommandPromoteOption) Run(f util.Factory, args []string) error {
} }
} }
return o.promote(controlPlaneRestConfig, obj, gvr, o.Deps) return o.promote(controlPlaneRestConfig, obj, gvr)
} }
// revertPromotedDeps reverts promoted dependencies of the resource // revertPromotedDeps reverts promoted dependencies of the resource
func (o *CommandPromoteOption) revertPromotedDeps(memberClusterFactory cmdutil.Factory, dependencies []configv1alpha1.DependentObjectReference, mapper meta.RESTMapper, controlPlaneDynamicClient *dynamic.DynamicClient, index int) { func (o *CommandPromoteOption) revertPromotedDeps(memberClusterFactory cmdutil.Factory, dependencies []configv1alpha1.DependentObjectReference, mapper meta.RESTMapper, controlPlaneDynamicClient dynamic.Interface, index int) {
memberDynamicClient, err := memberClusterFactory.DynamicClient() memberDynamicClient, err := memberClusterFactory.DynamicClient()
if err != nil { if err != nil {
klog.Errorf("revertPromotedDeps failed to get dynamic client of member cluster: %v", err) klog.Errorf("revertPromotedDeps failed to get dynamic client of member cluster: %v", err)
@ -364,7 +377,7 @@ func (o *CommandPromoteOption) revertPromotedDeps(memberClusterFactory cmdutil.F
// doPromoteDeps promotes dependencies of the resource // doPromoteDeps promotes dependencies of the resource
func (o *CommandPromoteOption) doPromoteDeps(memberClusterFactory cmdutil.Factory, dependencies []configv1alpha1.DependentObjectReference, mapper meta.RESTMapper, config *rest.Config) error { func (o *CommandPromoteOption) doPromoteDeps(memberClusterFactory cmdutil.Factory, dependencies []configv1alpha1.DependentObjectReference, mapper meta.RESTMapper, config *rest.Config) error {
controlPlaneDynamicClient := dynamic.NewForConfigOrDie(config) controlPlaneDynamicClient := dynamicClientBuilder(config)
for i, dep := range dependencies { for i, dep := range dependencies {
depInfo, err := o.getObjInfo(memberClusterFactory, o.Cluster, []string{dep.Kind, dep.Name}) depInfo, err := o.getObjInfo(memberClusterFactory, o.Cluster, []string{dep.Kind, dep.Name})
if err != nil { if err != nil {
@ -441,17 +454,17 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
// create resource interpreter // create resource interpreter
stopCh := make(chan struct{}) stopCh := make(chan struct{})
defer close(stopCh) defer close(stopCh)
dynamicClientSet := dynamic.NewForConfigOrDie(config) dynamicClientSet := dynamicClientBuilder(config)
controlPlaneInformerManager := genericmanager.NewSingleClusterInformerManager(dynamicClientSet, 0, stopCh) controlPlaneInformerManager := genericmanager.NewSingleClusterInformerManager(dynamicClientSet, 0, stopCh)
controlPlaneKubeClientSet := kubeclientset.NewForConfigOrDie(config) controlPlaneKubeClientSet := kubeClientBuilder(config)
sharedFactory := informers.NewSharedInformerFactory(controlPlaneKubeClientSet, 0) sharedFactory := informers.NewSharedInformerFactory(controlPlaneKubeClientSet, 0)
serviceLister := sharedFactory.Core().V1().Services().Lister() serviceLister := sharedFactory.Core().V1().Services().Lister()
sharedFactory.Start(stopCh) sharedFactory.Start(stopCh)
sharedFactory.WaitForCacheSync(stopCh) sharedFactory.WaitForCacheSync(stopCh)
controlPlaneInformerManager.Start() controlPlaneInformerManager.Start()
if sync := controlPlaneInformerManager.WaitForCacheSync(); sync == nil { if sync := controlPlaneInformerManager.WaitForCacheSync(); sync == nil {
return fmt.Errorf("informer factory for cluster does not exist") return errors.New("informer factory for cluster does not exist")
} }
defaultInterpreter := native.NewDefaultInterpreter() defaultInterpreter := native.NewDefaultInterpreter()
@ -468,7 +481,7 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
!configurableInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) && !configurableInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) &&
!customizedInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) { !customizedInterpreter.HookEnabled(obj.GroupVersionKind(), configv1alpha1.InterpreterOperationInterpretDependency) {
// if there is no interpreter configured, abort this dependency promotion but continue to promote the resource // if there is no interpreter configured, abort this dependency promotion but continue to promote the resource
klog.Warningf("there is no interpreter configured support to interpret dependencies") klog.Warning("there is no interpreter configured support to interpret dependencies")
return nil return nil
} }
@ -478,7 +491,7 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
return fmt.Errorf("configurable interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err) return fmt.Errorf("configurable interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err)
} }
if hookEnabled { if hookEnabled {
fmt.Printf("use configurable interpreter\n") fmt.Println("use configurable interpreter")
err := o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config) err := o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config)
return err return err
} }
@ -492,7 +505,7 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
return fmt.Errorf("customized interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err) return fmt.Errorf("customized interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err)
} }
if hookEnabled { if hookEnabled {
fmt.Printf("use customized interpreter\n") fmt.Println("use customized interpreter")
err := o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config) err := o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config)
return err return err
} }
@ -500,10 +513,10 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
// thirdparty interpreter has higher priority than default interpreter // thirdparty interpreter has higher priority than default interpreter
dependencies, hookEnabled, err = thirdpartyInterpreter.GetDependencies(obj) dependencies, hookEnabled, err = thirdpartyInterpreter.GetDependencies(obj)
if err != nil { if err != nil {
return fmt.Errorf("thirdparty interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err) return fmt.Errorf("third-party interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err)
} }
if hookEnabled { if hookEnabled {
fmt.Printf("use thirdparty interpreter\n") fmt.Println("use third-party interpreter")
err := o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config) err := o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config)
return err return err
} }
@ -513,12 +526,12 @@ func (o *CommandPromoteOption) promoteDeps(memberClusterFactory cmdutil.Factory,
if err != nil { if err != nil {
return fmt.Errorf("default interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err) return fmt.Errorf("default interpreter failed to get dependencies of resource %q(%s/%s): %v", gvr, obj.GetNamespace(), obj.GetName(), err)
} }
fmt.Printf("use default interpreter\n") fmt.Println("use default interpreter")
err = o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config) err = o.doPromoteDeps(memberClusterFactory, dependencies, mapper, config)
return err return err
} }
func (o *CommandPromoteOption) promote(controlPlaneRestConfig *rest.Config, obj *unstructured.Unstructured, gvr schema.GroupVersionResource, isDep bool) error { func (o *CommandPromoteOption) promote(controlPlaneRestConfig *rest.Config, obj *unstructured.Unstructured, gvr schema.GroupVersionResource) error {
if err := preprocessResource(obj); err != nil { if err := preprocessResource(obj); err != nil {
return fmt.Errorf("failed to preprocess resource %q(%s/%s) in control plane: %v", gvr, o.Namespace, o.name, err) return fmt.Errorf("failed to preprocess resource %q(%s/%s) in control plane: %v", gvr, o.Namespace, o.name, err)
} }
@ -534,11 +547,11 @@ func (o *CommandPromoteOption) promote(controlPlaneRestConfig *rest.Config, obj
return nil return nil
} }
controlPlaneDynamicClient := dynamic.NewForConfigOrDie(controlPlaneRestConfig) controlPlaneDynamicClient := dynamicClientBuilder(controlPlaneRestConfig)
karmadaClient := karmadaClientBuilder(controlPlaneRestConfig)
karmadaClient := karmadaclientset.NewForConfigOrDie(controlPlaneRestConfig) isClusterScoped := len(obj.GetNamespace()) == 0
if isClusterScoped {
if len(obj.GetNamespace()) == 0 {
_, err := controlPlaneDynamicClient.Resource(gvr).Get(context.TODO(), o.name, metav1.GetOptions{}) _, err := controlPlaneDynamicClient.Resource(gvr).Get(context.TODO(), o.name, metav1.GetOptions{})
if err == nil { if err == nil {
klog.Warningf("Resource %q(%s) already exist in karmada control plane, you can edit PropagationPolicy and OverridePolicy to propagate it\n", klog.Warningf("Resource %q(%s) already exist in karmada control plane, you can edit PropagationPolicy and OverridePolicy to propagate it\n",
@ -557,7 +570,7 @@ func (o *CommandPromoteOption) promote(controlPlaneRestConfig *rest.Config, obj
fmt.Printf("ResourceTemplate (%s/%s) is created successfully\n", o.Namespace, o.name) fmt.Printf("ResourceTemplate (%s/%s) is created successfully\n", o.Namespace, o.name)
if o.AutoCreatePolicy { if o.AutoCreatePolicy {
policyName, err := o.createClusterPropagationPolicy(karmadaClient, gvr, isDep) policyName, err := o.createClusterPropagationPolicy(karmadaClient, gvr)
if err != nil { if err != nil {
return err return err
} }
@ -584,7 +597,7 @@ func (o *CommandPromoteOption) promote(controlPlaneRestConfig *rest.Config, obj
fmt.Printf("ResourceTemplate (%s/%s) is created successfully\n", o.Namespace, o.name) fmt.Printf("ResourceTemplate (%s/%s) is created successfully\n", o.Namespace, o.name)
if o.AutoCreatePolicy { if o.AutoCreatePolicy {
policyName, err := o.createPropagationPolicy(karmadaClient, gvr, isDep) policyName, err := o.createPropagationPolicy(karmadaClient, gvr)
if err != nil { if err != nil {
return err return err
} }
@ -663,7 +676,7 @@ func (o *CommandPromoteOption) printObjectAndPolicy(obj *unstructured.Unstructur
} }
// createPropagationPolicy create PropagationPolicy in karmada control plane // createPropagationPolicy create PropagationPolicy in karmada control plane
func (o *CommandPromoteOption) createPropagationPolicy(karmadaClient *karmadaclientset.Clientset, gvr schema.GroupVersionResource, isDep bool) (string, error) { func (o *CommandPromoteOption) createPropagationPolicy(karmadaClient karmadaclientset.Interface, gvr schema.GroupVersionResource) (string, error) {
var policyName string var policyName string
if o.PolicyName == "" { if o.PolicyName == "" {
policyName = names.GeneratePolicyName(o.Namespace, o.name, o.gvk.String()) policyName = names.GeneratePolicyName(o.Namespace, o.name, o.gvk.String())
@ -673,7 +686,7 @@ func (o *CommandPromoteOption) createPropagationPolicy(karmadaClient *karmadacli
_, err := karmadaClient.PolicyV1alpha1().PropagationPolicies(o.Namespace).Get(context.TODO(), policyName, metav1.GetOptions{}) _, err := karmadaClient.PolicyV1alpha1().PropagationPolicies(o.Namespace).Get(context.TODO(), policyName, metav1.GetOptions{})
if err != nil && apierrors.IsNotFound(err) { if err != nil && apierrors.IsNotFound(err) {
pp := buildPropagationPolicy(o.name, policyName, o.Namespace, o.Cluster, gvr, o.gvk, isDep) pp := buildPropagationPolicy(o.name, policyName, o.Namespace, o.Cluster, gvr, o.gvk, o.Deps)
_, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(o.Namespace).Create(context.TODO(), pp, metav1.CreateOptions{}) _, err = karmadaClient.PolicyV1alpha1().PropagationPolicies(o.Namespace).Create(context.TODO(), pp, metav1.CreateOptions{})
return policyName, err return policyName, err
@ -687,7 +700,7 @@ func (o *CommandPromoteOption) createPropagationPolicy(karmadaClient *karmadacli
} }
// createClusterPropagationPolicy create ClusterPropagationPolicy in karmada control plane // createClusterPropagationPolicy create ClusterPropagationPolicy in karmada control plane
func (o *CommandPromoteOption) createClusterPropagationPolicy(karmadaClient *karmadaclientset.Clientset, gvr schema.GroupVersionResource, isDep bool) (string, error) { func (o *CommandPromoteOption) createClusterPropagationPolicy(karmadaClient karmadaclientset.Interface, gvr schema.GroupVersionResource) (string, error) {
var policyName string var policyName string
if o.PolicyName == "" { if o.PolicyName == "" {
policyName = names.GeneratePolicyName("", o.name, o.gvk.String()) policyName = names.GeneratePolicyName("", o.name, o.gvk.String())
@ -697,7 +710,7 @@ func (o *CommandPromoteOption) createClusterPropagationPolicy(karmadaClient *kar
_, err := karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), policyName, metav1.GetOptions{}) _, err := karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), policyName, metav1.GetOptions{})
if err != nil && apierrors.IsNotFound(err) { if err != nil && apierrors.IsNotFound(err) {
cpp := buildClusterPropagationPolicy(o.name, policyName, o.Cluster, gvr, o.gvk, isDep) cpp := buildClusterPropagationPolicy(o.name, policyName, o.Cluster, gvr, o.gvk, o.Deps)
_, err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Create(context.TODO(), cpp, metav1.CreateOptions{}) _, err = karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Create(context.TODO(), cpp, metav1.CreateOptions{})
return policyName, err return policyName, err

View File

@ -0,0 +1,511 @@
/*
Copyright 2024 The Karmada 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 promote
import (
"context"
"errors"
"fmt"
"strings"
"testing"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/dynamic"
fakedynamic "k8s.io/client-go/dynamic/fake"
"k8s.io/client-go/rest"
coretesting "k8s.io/client-go/testing"
policyv1alpha1 "github.com/karmada-io/karmada/pkg/apis/policy/v1alpha1"
karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned"
fakekarmadaclient "github.com/karmada-io/karmada/pkg/generated/clientset/versioned/fake"
"github.com/karmada-io/karmada/pkg/generated/clientset/versioned/scheme"
"github.com/karmada-io/karmada/pkg/util/names"
)
func TestValidatePromoteOptions(t *testing.T) {
tests := []struct {
name string
promoteOpts *CommandPromoteOption
wantErr bool
errMsg string
}{
{
name: "Validate_WithoutCluster_ClusterCanNotBeEmpty",
promoteOpts: &CommandPromoteOption{Cluster: ""},
wantErr: true,
errMsg: "the cluster cannot be empty",
},
{
name: "Validate_WithXMLOutputFormat_InvalidOutputFormat",
promoteOpts: &CommandPromoteOption{
Cluster: "member1",
OutputFormat: "xml",
},
wantErr: true,
errMsg: "invalid output format",
},
{
name: "Validate_WithValidOptions_PromoteOptionsValidated",
promoteOpts: &CommandPromoteOption{
Cluster: "member1",
OutputFormat: "json",
},
wantErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
err := test.promoteOpts.Validate()
if err == nil && test.wantErr {
t.Fatal("expected an error, but got none")
}
if err != nil && !test.wantErr {
t.Errorf("unexpected error, got: %v", err)
}
if err != nil && test.wantErr && !strings.Contains(err.Error(), test.errMsg) {
t.Errorf("expected error message %s to be in %s", test.errMsg, err.Error())
}
})
}
}
func TestPromoteResourceInLegacyCluster(t *testing.T) {
tests := []struct {
name string
promoteOpts *CommandPromoteOption
controlPlaneRestConfig *rest.Config
obj *unstructured.Unstructured
controlPlaneDynamicClient dynamic.Interface
karmadaClient karmadaclientset.Interface
gvr schema.GroupVersionResource
prep func(dynamic.Interface, karmadaclientset.Interface, schema.GroupVersionResource) error
verify func(controlPlaneDynamicClient dynamic.Interface, karmadaClient karmadaclientset.Interface, gvr schema.GroupVersionResource, namespace, resourceName, policyName string) error
wantErr bool
errMsg string
}{
{
name: "Promote_PrintObjectAndPolicy_FailedToInitializeK8sPrinter",
promoteOpts: &CommandPromoteOption{
name: "demo-crd",
OutputFormat: "json",
Printer: func(*meta.RESTMapping, *bool, bool, bool) (printers.ResourcePrinterFunc, error) {
return nil, errors.New("unknown type in printer")
},
},
obj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": map[string]interface{}{
"name": "demo-crd",
},
},
},
prep: func(dynamic.Interface, karmadaclientset.Interface, schema.GroupVersionResource) error { return nil },
verify: func(dynamic.Interface, karmadaclientset.Interface, schema.GroupVersionResource, string, string, string) error {
return nil
},
wantErr: true,
errMsg: "unknown type in printer",
},
{
name: "Promote_CreateClusterScopedResourceInControlPlane_FailedToCreateResource",
promoteOpts: &CommandPromoteOption{
name: "demo-crd",
},
obj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": map[string]interface{}{
"name": "demo-crd",
},
},
},
controlPlaneRestConfig: &rest.Config{},
controlPlaneDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
gvr: schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"},
prep: func(controlPlaneDynamicClient dynamic.Interface, _ karmadaclientset.Interface, gvr schema.GroupVersionResource) error {
controlPlaneDynamicClient.(*fakedynamic.FakeDynamicClient).Fake.PrependReactor("create", gvr.Resource, func(coretesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unexpected error; encountered network issue while creating resources")
})
dynamicClientBuilder = func(*rest.Config) dynamic.Interface {
return controlPlaneDynamicClient
}
return nil
},
verify: func(dynamic.Interface, karmadaclientset.Interface, schema.GroupVersionResource, string, string, string) error {
return nil
},
wantErr: true,
errMsg: "encountered network issue while creating resources",
},
{
name: "Promote_CreateClusterScopedResourceInControlPlane_ResourceCreated",
promoteOpts: &CommandPromoteOption{
name: "demo-crd",
Cluster: "member1",
PolicyName: "demo-crd-cluster-propagationpolicy",
AutoCreatePolicy: true,
Deps: true,
},
obj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apiextensions.k8s.io/v1",
"kind": "CustomResourceDefinition",
"metadata": map[string]interface{}{
"name": "demo-crd",
},
},
},
controlPlaneRestConfig: &rest.Config{},
controlPlaneDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
karmadaClient: fakekarmadaclient.NewSimpleClientset(),
gvr: schema.GroupVersionResource{
Group: "apiextensions.k8s.io",
Version: "v1",
Resource: "customresourcedefinitions",
},
prep: func(controlPlaneDynamicClient dynamic.Interface, karmadaClient karmadaclientset.Interface, _ schema.GroupVersionResource) error {
dynamicClientBuilder = func(*rest.Config) dynamic.Interface {
return controlPlaneDynamicClient
}
karmadaClientBuilder = func(*rest.Config) karmadaclientset.Interface {
return karmadaClient
}
return nil
},
verify: func(controlPlaneDynamicClient dynamic.Interface, karmadaClient karmadaclientset.Interface, gvr schema.GroupVersionResource, _, resourceName, policyName string) error {
if _, err := controlPlaneDynamicClient.Resource(gvr).Get(context.TODO(), resourceName, metav1.GetOptions{}); err != nil {
return fmt.Errorf("failed to get resource %v, but got error: %v", resourceName, err)
}
if _, err := karmadaClient.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), policyName, metav1.GetOptions{}); err != nil {
return fmt.Errorf("failed to get cluster propagation policy %s, got error: %v", policyName, err)
}
return nil
},
wantErr: false,
},
{
name: "Promote_CreateNamespaceScopedResourceInControlPlane_ResourceCreated",
promoteOpts: &CommandPromoteOption{
name: "demo-deployment",
Cluster: "member1",
PolicyName: "demo-deployment-propagationpolicy",
Namespace: names.NamespaceDefault,
AutoCreatePolicy: true,
},
obj: &unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "apps/v1",
"kind": "Deploymnet",
"metadata": map[string]interface{}{
"name": "demo-deployment",
"namespace": names.NamespaceDefault,
},
},
},
controlPlaneRestConfig: &rest.Config{},
controlPlaneDynamicClient: fakedynamic.NewSimpleDynamicClient(scheme.Scheme),
karmadaClient: fakekarmadaclient.NewSimpleClientset(),
gvr: schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "deployments",
},
prep: func(controlPlaneDynamicClient dynamic.Interface, karmadaClient karmadaclientset.Interface, _ schema.GroupVersionResource) error {
dynamicClientBuilder = func(*rest.Config) dynamic.Interface {
return controlPlaneDynamicClient
}
karmadaClientBuilder = func(*rest.Config) karmadaclientset.Interface {
return karmadaClient
}
return nil
},
verify: func(controlPlaneDynamicClient dynamic.Interface, karmadaClient karmadaclientset.Interface, gvr schema.GroupVersionResource, namespace, resourceName, policyName string) error {
if _, err := controlPlaneDynamicClient.Resource(gvr).Namespace(namespace).Get(context.TODO(), resourceName, metav1.GetOptions{}); err != nil {
return fmt.Errorf("failed to get resource %v, but got error: %v", resourceName, err)
}
if _, err := karmadaClient.PolicyV1alpha1().PropagationPolicies(namespace).Get(context.TODO(), policyName, metav1.GetOptions{}); err != nil {
return fmt.Errorf("failed to get propagation policy %s in namespace %s, got error: %v", policyName, namespace, err)
}
return nil
},
wantErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if err := test.prep(test.controlPlaneDynamicClient, test.karmadaClient, test.gvr); err != nil {
t.Fatalf("failed to prep test environment, got error: %v", err)
}
err := test.promoteOpts.promote(test.controlPlaneRestConfig, test.obj, test.gvr)
if err == nil && test.wantErr {
t.Fatal("expected an error, but got none")
}
if err != nil && !test.wantErr {
t.Errorf("unexpected error, got: %v", err)
}
if err != nil && test.wantErr && !strings.Contains(err.Error(), test.errMsg) {
t.Errorf("expected error message %s to be in %s", test.errMsg, err.Error())
}
if err := test.verify(test.controlPlaneDynamicClient, test.karmadaClient, test.gvr, test.obj.GetNamespace(), test.obj.GetName(), test.promoteOpts.PolicyName); err != nil {
t.Errorf("failed to verify promoting the resource %s in the legacy cluster %s, got error: %v", test.gvr.Resource, test.promoteOpts.Cluster, err)
}
})
}
}
func TestCreatePropagationPolicy(t *testing.T) {
tests := []struct {
name string
promoteOpts *CommandPromoteOption
client karmadaclientset.Interface
gvr schema.GroupVersionResource
prep func(karmadaclientset.Interface, *CommandPromoteOption) error
verify func(karmadaclientset.Interface, *CommandPromoteOption) error
wantErr bool
errMsg string
}{
{
name: "CreatePropagationPolicy_PropagationPolicyAlreadyExists_ReturnTheExistingOne",
promoteOpts: &CommandPromoteOption{
Namespace: names.NamespaceDefault,
PolicyName: "webserver-propagation",
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(client karmadaclientset.Interface, promoteOpts *CommandPromoteOption) error {
pp := &policyv1alpha1.PropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: promoteOpts.PolicyName,
Namespace: promoteOpts.Namespace,
},
}
if _, err := client.PolicyV1alpha1().PropagationPolicies(promoteOpts.Namespace).Create(context.TODO(), pp, metav1.CreateOptions{}); err != nil {
return fmt.Errorf("failed to create propagation policy %s, got error: %v", promoteOpts.PolicyName, err)
}
return nil
},
verify: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
wantErr: true,
errMsg: fmt.Sprintf("PropagationPolicy(%s/%s) already exist", names.NamespaceDefault, "webserver-propagation"),
},
{
name: "CreatePropagationPolicy_GetPropagationPolicyFromK8s_GotNetworkIssue",
promoteOpts: &CommandPromoteOption{
Namespace: names.NamespaceDefault,
PolicyName: "webserver-propagation",
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(client karmadaclientset.Interface, _ *CommandPromoteOption) error {
client.(*fakekarmadaclient.Clientset).Fake.PrependReactor("get", "propagationpolicies", func(coretesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unexpected error: encountered a network issue while getting the propagationpolicies")
})
return nil
},
verify: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
wantErr: true,
errMsg: "encountered a network issue while getting the propagationpolicies",
},
{
name: "CreatePropagationPolicy_CreatePropagationPolicy_FailedToCreatePropagationPolicy",
promoteOpts: &CommandPromoteOption{
Namespace: names.NamespaceDefault,
PolicyName: "webserver-propagation",
Cluster: "member1",
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(client karmadaclientset.Interface, _ *CommandPromoteOption) error {
client.(*fakekarmadaclient.Clientset).Fake.PrependReactor("get", "propagationpolicies", func(coretesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unexpected error: encountered a network issue while creating the propagationpolicies")
})
return nil
},
verify: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
gvr: schema.GroupVersionResource{},
wantErr: true,
errMsg: "encountered a network issue while creating the propagationpolicies",
},
{
name: "CreatePropagationPolicy_CreatePropagationPolicy_PropagationPolicyCreated",
promoteOpts: &CommandPromoteOption{
name: "nginx-deployment",
Namespace: names.NamespaceDefault,
PolicyName: "webserver-propagation",
Cluster: "member1",
gvk: schema.GroupVersionKind{
Kind: "Deployment",
},
Deps: true,
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
verify: func(client karmadaclientset.Interface, promoteOpts *CommandPromoteOption) error {
if _, err := client.PolicyV1alpha1().PropagationPolicies(promoteOpts.Namespace).Get(context.TODO(), promoteOpts.PolicyName, metav1.GetOptions{}); err != nil {
return fmt.Errorf("failed to create propagation policy %s in namespace %s, got error: %v", promoteOpts.PolicyName, promoteOpts.Namespace, err)
}
return nil
},
gvr: schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "deployments",
},
wantErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if err := test.prep(test.client, test.promoteOpts); err != nil {
t.Fatalf("failed to prep test environment, got error: %v", err)
}
_, err := test.promoteOpts.createPropagationPolicy(test.client, test.gvr)
if err == nil && test.wantErr {
t.Fatal("expcted an error, but got none")
}
if err != nil && !test.wantErr {
t.Errorf("unexpected error, got: %v", err)
}
if err != nil && test.wantErr && !strings.Contains(err.Error(), test.errMsg) {
t.Errorf("expected error message %s to be in %s", test.errMsg, err.Error())
}
if err := test.verify(test.client, test.promoteOpts); err != nil {
t.Errorf("failed to verify creating propagation policy, got error: %v", err)
}
})
}
}
func TestCreateClusterPropagationPolicy(t *testing.T) {
tests := []struct {
name string
promoteOpts *CommandPromoteOption
client karmadaclientset.Interface
gvr schema.GroupVersionResource
prep func(karmadaclientset.Interface, *CommandPromoteOption) error
verify func(karmadaclientset.Interface, *CommandPromoteOption) error
wantErr bool
errMsg string
}{
{
name: "CreateClusterPropagationPolicy_ClusterPropagationPolicyAlreadyExists_ReturnTheExistingOne",
promoteOpts: &CommandPromoteOption{
PolicyName: "crd-cluster-propagation",
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(client karmadaclientset.Interface, promoteOpts *CommandPromoteOption) error {
cpp := &policyv1alpha1.ClusterPropagationPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: promoteOpts.PolicyName,
},
}
if _, err := client.PolicyV1alpha1().ClusterPropagationPolicies().Create(context.TODO(), cpp, metav1.CreateOptions{}); err != nil {
return fmt.Errorf("failed to create cluster propagation policy %s, got error: %v", promoteOpts.PolicyName, err)
}
return nil
},
verify: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
wantErr: true,
errMsg: "ClusterPropagationPolicy(crd-cluster-propagation) already exist",
},
{
name: "CreateClusterPropagationPolicy_GetClusterPropagationPolicyFromK8s_GotNetworkIssue",
promoteOpts: &CommandPromoteOption{
PolicyName: "crd-cluster-propagation",
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(client karmadaclientset.Interface, _ *CommandPromoteOption) error {
client.(*fakekarmadaclient.Clientset).Fake.PrependReactor("get", "clusterpropagationpolicies", func(coretesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unexpected error: encountered a network issue while getting the cluster propagationpolicies")
})
return nil
},
verify: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
wantErr: true,
errMsg: "encountered a network issue while getting the cluster propagationpolicies",
},
{
name: "CreateClusterPropagationPolicy_CreateClusterPropagationPolicy_FailedToCreateClusterPropagationPolicy",
promoteOpts: &CommandPromoteOption{
PolicyName: "crd-cluster-propagation",
Cluster: "member1",
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(client karmadaclientset.Interface, _ *CommandPromoteOption) error {
client.(*fakekarmadaclient.Clientset).Fake.PrependReactor("get", "clusterpropagationpolicies", func(coretesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New("unexpected error: encountered a network issue while creating the cluster propagationpolicies")
})
return nil
},
verify: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
gvr: schema.GroupVersionResource{},
wantErr: true,
errMsg: "encountered a network issue while creating the cluster propagationpolicies",
},
{
name: "CreateClusterPropagationPolicy_CreateClusterPropagationPolicy_ClusterPropagationPolicyCreated",
promoteOpts: &CommandPromoteOption{
name: "crd",
PolicyName: "crd-cluster-propagation",
Cluster: "member1",
gvk: schema.GroupVersionKind{
Kind: "CustomResourceDefinition",
},
Deps: true,
},
client: fakekarmadaclient.NewSimpleClientset(),
prep: func(karmadaclientset.Interface, *CommandPromoteOption) error { return nil },
verify: func(client karmadaclientset.Interface, promoteOpts *CommandPromoteOption) error {
if _, err := client.PolicyV1alpha1().ClusterPropagationPolicies().Get(context.TODO(), promoteOpts.PolicyName, metav1.GetOptions{}); err != nil {
return fmt.Errorf("failed to create propagation policy %s for the legacy cluster %s, got error: %v", promoteOpts.PolicyName, promoteOpts.Cluster, err)
}
return nil
},
gvr: schema.GroupVersionResource{
Group: "apiextensions.k8s.io",
Version: "v1",
Resource: "customresourcedefinitions",
},
wantErr: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if err := test.prep(test.client, test.promoteOpts); err != nil {
t.Fatalf("failed to prep test environment, got error: %v", err)
}
_, err := test.promoteOpts.createClusterPropagationPolicy(test.client, test.gvr)
if err == nil && test.wantErr {
t.Fatal("expcted an error, but got none")
}
if err != nil && !test.wantErr {
t.Errorf("unexpected error, got: %v", err)
}
if err != nil && test.wantErr && !strings.Contains(err.Error(), test.errMsg) {
t.Errorf("expected error message %s to be in %s", test.errMsg, err.Error())
}
if err := test.verify(test.client, test.promoteOpts); err != nil {
t.Errorf("failed to verify creating cluster propagation policy, got error: %v", err)
}
})
}
}