diff --git a/pkg/karmadactl/unregister/unregister.go b/pkg/karmadactl/unregister/unregister.go index 37c5a84c6b44..f3361539c262 100644 --- a/pkg/karmadactl/unregister/unregister.go +++ b/pkg/karmadactl/unregister/unregister.go @@ -128,10 +128,10 @@ type CommandUnregisterOption struct { DryRun bool // ControlPlaneClient control plane client set - ControlPlaneClient *karmadaclientset.Clientset + ControlPlaneClient karmadaclientset.Interface // MemberClusterClient member cluster client set - MemberClusterClient *kubeclient.Clientset + MemberClusterClient kubeclient.Interface } // AddFlags adds flags to the specified FlagSet. @@ -282,6 +282,10 @@ func (j *CommandUnregisterOption) getKarmadaAgentConfig(agent *appsv1.Deployment if err != nil { return nil, fmt.Errorf("failed to get the secret which stores the karmada agent config") } + if len(agentConfigSecret.Data[fileName]) == 0 { + return nil, fmt.Errorf("empty data, secretName: %s, keyName: %s", agentConfigSecretName, fileName) + } + return clientcmd.Load(agentConfigSecret.Data[fileName]) } diff --git a/pkg/karmadactl/unregister/unregister_test.go b/pkg/karmadactl/unregister/unregister_test.go new file mode 100644 index 000000000000..87c681f51e59 --- /dev/null +++ b/pkg/karmadactl/unregister/unregister_test.go @@ -0,0 +1,234 @@ +/* +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 unregister + +import ( + "testing" + "time" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/tools/clientcmd" + + clusterV1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" + fakekarmadaclient "github.com/karmada-io/karmada/pkg/generated/clientset/versioned/fake" + "github.com/karmada-io/karmada/pkg/karmadactl/register" +) + +const ( + clusterNamespace = "default" + namespace = "default" + clusterName = "member_test" + agentConfigSecretName = "karmada-agent-config" + agentConfigKeyName = "karmada.config" + agentSecretVolumeName = "karmada-config" +) + +func TestCommandUnregisterOption_Complete_Validate(t *testing.T) { + tests := []struct { + name string + args []string + clusterKubeConfig string + wait time.Duration + wantCompleteErr bool + wantValidateErr bool + }{ + { + name: "args more than one", + args: []string{"member1", "member2"}, + wantCompleteErr: false, + wantValidateErr: true, + }, + { + name: "invalid cluster name", + args: []string{"member.1"}, + wantCompleteErr: false, + wantValidateErr: true, + }, + { + name: "empty clusterKubeConfig", + args: []string{"member1"}, + clusterKubeConfig: "", + wantCompleteErr: false, + wantValidateErr: true, + }, + { + name: "negative wait time", + args: []string{"member1"}, + clusterKubeConfig: "./kube/config", + wait: -1, + wantCompleteErr: false, + wantValidateErr: true, + }, + { + name: "normal case", + args: []string{"member1"}, + clusterKubeConfig: "./kube/config", + wait: 60 * time.Second, + wantCompleteErr: false, + wantValidateErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + j := &CommandUnregisterOption{ + ClusterKubeConfig: tt.clusterKubeConfig, + Wait: tt.wait, + } + + err := j.Complete(tt.args) + if (err == nil && tt.wantCompleteErr) || (err != nil && !tt.wantCompleteErr) { + t.Errorf("Complete() error = %v, wantCompleteErr %v", err, tt.wantCompleteErr) + } + + err = j.Validate(tt.args) + if (err == nil && tt.wantValidateErr) || (err != nil && !tt.wantValidateErr) { + t.Errorf("Validate() error = %v, wantValidateErr %v", err, tt.wantValidateErr) + } + }) + } +} + +func TestCommandUnregisterOption_getKarmadaAgentConfig(t *testing.T) { + agentConfig := register.CreateBasic("http://127.0.0.1:5443", clusterName, "test", nil) + agentConfigBytes, _ := clientcmd.Write(*agentConfig) + agentConfigSecret := createSecret(agentConfigSecretName, namespace, agentConfigKeyName, agentConfigBytes) + + tests := []struct { + name string + mountPath string + karmadaConfigPath string + clusterResources []runtime.Object + wantErr bool + }{ + { + name: "common case", + mountPath: "/etc/karmada/config", + karmadaConfigPath: "/etc/karmada/config/karmada.config", + clusterResources: []runtime.Object{agentConfigSecret}, + wantErr: false, + }, + { + name: "mount path end up with a extra / symbol", + mountPath: "/etc/karmada/config/", + karmadaConfigPath: "/etc/karmada/config/karmada.config", + clusterResources: []runtime.Object{agentConfigSecret}, + wantErr: false, + }, + { + name: "agent config secret not found", + mountPath: "/etc/karmada/config", + karmadaConfigPath: "/etc/karmada/config/karmada.config", + wantErr: true, + }, + { + name: "agent config secret exist but has a invalid key name", + mountPath: "/etc/karmada/config", + karmadaConfigPath: "/etc/karmada/config/karmada-config", + clusterResources: []runtime.Object{agentConfigSecret}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + j := &CommandUnregisterOption{ + Namespace: namespace, + MemberClusterClient: fake.NewSimpleClientset(tt.clusterResources...), + } + agent := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{Name: register.KarmadaAgentName, Namespace: namespace}, + Spec: appsv1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Command: []string{ + "/bin/karmada-agent", + "--karmada-kubeconfig=" + tt.karmadaConfigPath, + }, + VolumeMounts: []corev1.VolumeMount{{ + Name: agentSecretVolumeName, + MountPath: tt.mountPath, + }}, + }}, + Volumes: []corev1.Volume{{ + Name: agentSecretVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{SecretName: agentConfigSecretName}, + }, + }}, + }, + }, + }, + } + _, err := j.getKarmadaAgentConfig(agent) + if (err == nil && tt.wantErr) || (err != nil && !tt.wantErr) { + t.Errorf("getSpecifiedKarmadaContext() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestCommandUnregisterOption_RunUnregisterCluster(t *testing.T) { + tests := []struct { + name string + clusterObject []runtime.Object + clusterResources []runtime.Object + wantErr bool + }{ + { + name: "cluster object not exist", + clusterObject: []runtime.Object{}, + clusterResources: []runtime.Object{}, + wantErr: true, + }, + { + name: "cluster exist, but cluster resources not found", + clusterObject: []runtime.Object{&clusterV1alpha1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: clusterName}}}, + clusterResources: []runtime.Object{}, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + j := &CommandUnregisterOption{ + ClusterName: clusterName, + Namespace: namespace, + ClusterNamespace: clusterNamespace, + Wait: 60 * time.Second, + } + j.ControlPlaneClient = fakekarmadaclient.NewSimpleClientset(tt.clusterObject...) + j.MemberClusterClient = fake.NewSimpleClientset(tt.clusterResources...) + err := j.RunUnregisterCluster() + if (err == nil && tt.wantErr) || (err != nil && !tt.wantErr) { + t.Errorf("RunUnregisterCluster() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func createSecret(secretName, secretNamespace, keyName string, value []byte) *corev1.Secret { + return &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: secretNamespace}, + Data: map[string][]byte{ + keyName: value, + }, + } +} diff --git a/pkg/karmadactl/util/cluster.go b/pkg/karmadactl/util/cluster.go index d7b9a6ca17f9..ff578c63211c 100644 --- a/pkg/karmadactl/util/cluster.go +++ b/pkg/karmadactl/util/cluster.go @@ -30,7 +30,7 @@ import ( ) // DeleteClusterObject deletes the cluster object from the Karmada control plane. -func DeleteClusterObject(controlPlaneKarmadaClient *karmadaclientset.Clientset, clusterName string, +func DeleteClusterObject(controlPlaneKarmadaClient karmadaclientset.Interface, clusterName string, timeout time.Duration, dryRun bool) error { if dryRun { return nil