diff --git a/pkg/util/cluster.go b/pkg/util/cluster.go index fa4d0b854..57a32d7d7 100644 --- a/pkg/util/cluster.go +++ b/pkg/util/cluster.go @@ -51,9 +51,6 @@ type ClusterRegisterOption struct { // IsKubeCredentialsEnabled represents whether report secret func (r ClusterRegisterOption) IsKubeCredentialsEnabled() bool { - if len(r.ReportSecrets) == 1 && r.ReportSecrets[0] == None { - return false - } for _, sct := range r.ReportSecrets { if sct == KubeCredentials { return true @@ -64,9 +61,6 @@ func (r ClusterRegisterOption) IsKubeCredentialsEnabled() bool { // IsKubeImpersonatorEnabled represents whether report impersonator secret func (r ClusterRegisterOption) IsKubeImpersonatorEnabled() bool { - if len(r.ReportSecrets) == 1 && r.ReportSecrets[0] == None { - return false - } for _, sct := range r.ReportSecrets { if sct == KubeImpersonator { return true @@ -146,7 +140,7 @@ func GetClusterWithKarmadaClient(client karmadaclientset.Interface, name string) return nil, false, nil } - klog.Warningf("failed to retrieve cluster(%s). error: %v", cluster.Name, err) + klog.Warningf("failed to retrieve cluster(%s). error: %v", name, err) return nil, false, err } @@ -174,7 +168,7 @@ func updateCluster(controlPlaneClient karmadaclientset.Interface, cluster *clust } // ObtainClusterID returns the cluster ID property with clusterKubeClient -func ObtainClusterID(clusterKubeClient *kubernetes.Clientset) (string, error) { +func ObtainClusterID(clusterKubeClient kubernetes.Interface) (string, error) { ns, err := clusterKubeClient.CoreV1().Namespaces().Get(context.TODO(), metav1.NamespaceSystem, metav1.GetOptions{}) if err != nil { return "", err diff --git a/pkg/util/cluster_test.go b/pkg/util/cluster_test.go index f7c50fb70..4796c70e6 100644 --- a/pkg/util/cluster_test.go +++ b/pkg/util/cluster_test.go @@ -1,15 +1,21 @@ package util import ( - "context" "reflect" "testing" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" karmadaclientset "github.com/karmada-io/karmada/pkg/generated/clientset/versioned" karmadaclientsetfake "github.com/karmada-io/karmada/pkg/generated/clientset/versioned/fake" + "github.com/karmada-io/karmada/pkg/util/gclient" ) func newCluster(name string) *clusterv1alpha1.Cluster { @@ -38,12 +44,10 @@ func withID(cluster *clusterv1alpha1.Cluster, id string) *clusterv1alpha1.Cluste } func TestCreateOrUpdateClusterObject(t *testing.T) { - fakeClient := karmadaclientsetfake.NewSimpleClientset() type args struct { controlPlaneClient karmadaclientset.Interface clusterObj *clusterv1alpha1.Cluster mutate func(*clusterv1alpha1.Cluster) - aop func() func() } tests := []struct { name string @@ -54,17 +58,21 @@ func TestCreateOrUpdateClusterObject(t *testing.T) { { name: "cluster exist, and update cluster", args: args{ - controlPlaneClient: fakeClient, + controlPlaneClient: karmadaclientsetfake.NewSimpleClientset(withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443")), clusterObj: newCluster(ClusterMember1), mutate: func(cluster *clusterv1alpha1.Cluster) { cluster.Spec.SyncMode = clusterv1alpha1.Pull }, - aop: func() func() { - cluster := withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443") - _, _ = fakeClient.ClusterV1alpha1().Clusters().Create(context.TODO(), cluster, metav1.CreateOptions{}) - return func() { - _ = fakeClient.ClusterV1alpha1().Clusters().Delete(context.TODO(), cluster.Name, metav1.DeleteOptions{}) - } + }, + want: withSyncMode(withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443"), clusterv1alpha1.Pull), + }, + { + name: "cluster exist and equal, not update", + args: args{ + controlPlaneClient: karmadaclientsetfake.NewSimpleClientset(withSyncMode(withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443"), clusterv1alpha1.Pull)), + clusterObj: withSyncMode(withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443"), clusterv1alpha1.Pull), + mutate: func(cluster *clusterv1alpha1.Cluster) { + cluster.Spec.SyncMode = clusterv1alpha1.Pull }, }, want: withSyncMode(withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443"), clusterv1alpha1.Pull), @@ -72,27 +80,65 @@ func TestCreateOrUpdateClusterObject(t *testing.T) { { name: "cluster not exist, and create cluster", args: args{ - controlPlaneClient: fakeClient, + controlPlaneClient: karmadaclientsetfake.NewSimpleClientset(), clusterObj: newCluster(ClusterMember1), mutate: func(cluster *clusterv1alpha1.Cluster) { cluster.Spec.SyncMode = clusterv1alpha1.Pull }, - aop: func() func() { - cluster := withSyncMode(newCluster(ClusterMember1), clusterv1alpha1.Pull) - return func() { - _ = fakeClient.ClusterV1alpha1().Clusters().Delete(context.TODO(), cluster.Name, metav1.DeleteOptions{}) - } - }, }, want: withSyncMode(newCluster(ClusterMember1), clusterv1alpha1.Pull), }, + { + name: "get cluster error", + args: args{ + controlPlaneClient: func() karmadaclientset.Interface { + c := karmadaclientsetfake.NewSimpleClientset() + c.PrependReactor("get", "*", errorAction) + return c + }(), + clusterObj: newCluster(ClusterMember1), + mutate: func(cluster *clusterv1alpha1.Cluster) { + cluster.Spec.SyncMode = clusterv1alpha1.Pull + }, + }, + wantErr: true, + want: nil, + }, + { + name: "create cluster error", + args: args{ + controlPlaneClient: func() karmadaclientset.Interface { + c := karmadaclientsetfake.NewSimpleClientset() + c.PrependReactor("create", "*", errorAction) + return c + }(), + clusterObj: newCluster(ClusterMember1), + mutate: func(cluster *clusterv1alpha1.Cluster) { + cluster.Spec.SyncMode = clusterv1alpha1.Pull + }, + }, + wantErr: true, + want: nil, + }, + { + name: "update cluster error", + args: args{ + controlPlaneClient: func() karmadaclientset.Interface { + c := karmadaclientsetfake.NewSimpleClientset(withAPIEndPoint(newCluster(ClusterMember1), "https://127.0.0.1:6443")) + c.PrependReactor("update", "*", errorAction) + return c + }(), + clusterObj: newCluster(ClusterMember1), + mutate: func(cluster *clusterv1alpha1.Cluster) { + cluster.Spec.SyncMode = clusterv1alpha1.Pull + }, + }, + wantErr: true, + want: nil, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if tt.args.aop != nil { - cancel := tt.args.aop() - defer cancel() - } got, err := CreateOrUpdateClusterObject(tt.args.controlPlaneClient, tt.args.clusterObj, tt.args.mutate) if (err != nil) != tt.wantErr { t.Errorf("CreateOrUpdateClusterObject() error = %v, wantErr %v", err, tt.wantErr) @@ -108,35 +154,28 @@ func TestCreateOrUpdateClusterObject(t *testing.T) { func TestIsClusterIDUnique(t *testing.T) { tests := []struct { name string - existedCluster []*clusterv1alpha1.Cluster + existedCluster []runtime.Object id string want bool clustername string }{ { name: "no cluster", id: "1", want: true, - existedCluster: []*clusterv1alpha1.Cluster{}, + existedCluster: []runtime.Object{}, }, { name: "existed id", id: "1", want: false, clustername: "cluster-1", - existedCluster: []*clusterv1alpha1.Cluster{withID(newCluster("cluster-1"), "1")}, + existedCluster: []runtime.Object{withID(newCluster("cluster-1"), "1")}, }, { name: "unique id", id: "2", want: true, - existedCluster: []*clusterv1alpha1.Cluster{withID(newCluster("cluster-1"), "1")}, + existedCluster: []runtime.Object{withID(newCluster("cluster-1"), "1")}, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - fakeClient := karmadaclientsetfake.NewSimpleClientset() - - for _, cluster := range tc.existedCluster { - _, err := fakeClient.ClusterV1alpha1().Clusters().Create(context.TODO(), cluster, metav1.CreateOptions{}) - if err != nil { - t.Fatal(err) - } - } + fakeClient := karmadaclientsetfake.NewSimpleClientset(tc.existedCluster...) ok, name, err := IsClusterIdentifyUnique(fakeClient, tc.id) if err != nil { @@ -153,3 +192,277 @@ func TestIsClusterIDUnique(t *testing.T) { }) } } + +func TestClusterRegisterOption_IsKubeCredentialsEnabled(t *testing.T) { + type fields struct { + ReportSecrets []string + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "secrets empty", + fields: fields{ + ReportSecrets: nil, + }, + want: false, + }, + { + name: "secrets are [None]", + fields: fields{ + ReportSecrets: []string{None}, + }, + want: false, + }, + { + name: "secrets are [KubeCredentials]", + fields: fields{ + ReportSecrets: []string{KubeCredentials}, + }, + want: true, + }, + { + name: "secrets are [None,KubeCredentials]", + fields: fields{ + ReportSecrets: []string{None, KubeCredentials}, + }, + want: true, + }, + { + name: "secrets are [None,None]", + fields: fields{ + ReportSecrets: []string{None, None}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := ClusterRegisterOption{ + ReportSecrets: tt.fields.ReportSecrets, + } + if got := r.IsKubeCredentialsEnabled(); got != tt.want { + t.Errorf("IsKubeCredentialsEnabled() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestClusterRegisterOption_IsKubeImpersonatorEnabled(t *testing.T) { + type fields struct { + ReportSecrets []string + } + tests := []struct { + name string + fields fields + want bool + }{ + { + name: "secrets empty", + fields: fields{ + ReportSecrets: nil, + }, + want: false, + }, + { + name: "secrets are [None]", + fields: fields{ + ReportSecrets: []string{None}, + }, + want: false, + }, + { + name: "secrets are [KubeImpersonator]", + fields: fields{ + ReportSecrets: []string{KubeImpersonator}, + }, + want: true, + }, + { + name: "secrets are [None,KubeImpersonator]", + fields: fields{ + ReportSecrets: []string{None, KubeImpersonator}, + }, + want: true, + }, + { + name: "secrets are [None,None]", + fields: fields{ + ReportSecrets: []string{None, None}, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := ClusterRegisterOption{ + ReportSecrets: tt.fields.ReportSecrets, + } + if got := r.IsKubeImpersonatorEnabled(); got != tt.want { + t.Errorf("IsKubeImpersonatorEnabled() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestCreateClusterObject(t *testing.T) { + type args struct { + controlPlaneClient karmadaclientset.Interface + clusterObj *clusterv1alpha1.Cluster + } + tests := []struct { + name string + args args + want *clusterv1alpha1.Cluster + wantErr bool + }{ + { + name: "cluster not exit, and create it", + args: args{ + controlPlaneClient: karmadaclientsetfake.NewSimpleClientset(), + clusterObj: newCluster("test"), + }, + want: newCluster("test"), + wantErr: false, + }, + { + name: "cluster exits, and return error", + args: args{ + controlPlaneClient: karmadaclientsetfake.NewSimpleClientset(newCluster("test")), + clusterObj: newCluster("test"), + }, + want: newCluster("test"), + wantErr: true, + }, + { + name: "get cluster error", + args: args{ + controlPlaneClient: func() karmadaclientset.Interface { + c := karmadaclientsetfake.NewSimpleClientset() + c.PrependReactor("get", "*", errorAction) + return c + }(), + clusterObj: newCluster("test"), + }, + want: nil, + wantErr: true, + }, + { + name: "create cluster error", + args: args{ + controlPlaneClient: func() karmadaclientset.Interface { + c := karmadaclientsetfake.NewSimpleClientset() + c.PrependReactor("create", "*", errorAction) + return c + }(), + clusterObj: newCluster("test"), + }, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := CreateClusterObject(tt.args.controlPlaneClient, tt.args.clusterObj) + if (err != nil) != tt.wantErr { + t.Errorf("CreateClusterObject() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("CreateClusterObject() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetCluster(t *testing.T) { + type args struct { + hostClient client.Client + clusterName string + } + tests := []struct { + name string + args args + want *clusterv1alpha1.Cluster + wantErr bool + }{ + { + name: "cluster not found", + args: args{ + hostClient: fakeclient.NewClientBuilder().Build(), + clusterName: "test", + }, + want: nil, + wantErr: true, + }, + { + name: "cluster get success", + args: args{ + hostClient: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects(newCluster("test")).Build(), + clusterName: "test", + }, + want: newCluster("test"), + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := GetCluster(tt.args.hostClient, tt.args.clusterName) + if (err != nil) != tt.wantErr { + t.Errorf("GetCluster() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != nil { + // remove fields injected by fake client + got.TypeMeta = metav1.TypeMeta{} + got.ResourceVersion = "" + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetCluster() got = %v, want %v", got, tt.want) + } + }) + } +} + +func TestObtainClusterID(t *testing.T) { + type args struct { + clusterKubeClient kubernetes.Interface + } + tests := []struct { + name string + args args + want string + wantErr bool + }{ + { + name: "namespace not exist", + args: args{ + clusterKubeClient: fake.NewSimpleClientset(), + }, + want: "", + wantErr: true, + }, + { + name: "namespace exists", + args: args{ + clusterKubeClient: fake.NewSimpleClientset(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: metav1.NamespaceSystem, UID: "123"}}), + }, + want: "123", + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ObtainClusterID(tt.args.clusterKubeClient) + if (err != nil) != tt.wantErr { + t.Errorf("ObtainClusterID() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("ObtainClusterID() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/util/clusterlease_test.go b/pkg/util/clusterlease_test.go new file mode 100644 index 000000000..990354246 --- /dev/null +++ b/pkg/util/clusterlease_test.go @@ -0,0 +1,83 @@ +package util + +import ( + "reflect" + "testing" + + coordinationv1 "k8s.io/api/coordination/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/karmada-io/karmada/pkg/util/gclient" +) + +func TestSetLeaseOwnerFunc(t *testing.T) { + type args struct { + c client.Client + clusterName string + lease *coordinationv1.Lease + } + tests := []struct { + name string + args args + wantErr bool + want *coordinationv1.Lease + }{ + { + name: "lease has no owner", + args: args{ + c: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + newCluster("test"), + ).Build(), + clusterName: "test", + lease: &coordinationv1.Lease{}, + }, + wantErr: false, + want: &coordinationv1.Lease{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{ + {APIVersion: "cluster.karmada.io/v1alpha1", Kind: "Cluster", Name: "test"}}}}, + }, + { + name: "cluster not found", + args: args{ + c: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects().Build(), + clusterName: "test", + lease: &coordinationv1.Lease{}, + }, + wantErr: true, + want: &coordinationv1.Lease{}, + }, + { + name: "lease has owner", + args: args{ + c: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects().Build(), + clusterName: "test", + lease: &coordinationv1.Lease{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{ + {APIVersion: "cluster.karmada.io/v1alpha1", Kind: "Cluster", Name: "foo", UID: "456"}}}}, + }, + wantErr: false, + want: &coordinationv1.Lease{ObjectMeta: metav1.ObjectMeta{OwnerReferences: []metav1.OwnerReference{ + {APIVersion: "cluster.karmada.io/v1alpha1", Kind: "Cluster", Name: "foo", UID: "456"}}}}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := SetLeaseOwnerFunc(tt.args.c, tt.args.clusterName) + if f == nil { + t.Errorf("SetLeaseOwnerFunc() returns nil") + return + } + + err := f(tt.args.lease) + if (err != nil) != tt.wantErr { + t.Errorf("got error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !reflect.DeepEqual(tt.want, tt.args.lease) { + t.Errorf("got = %v, want %v", tt.args.lease, tt.want) + return + } + }) + } +} diff --git a/pkg/util/membercluster_client_test.go b/pkg/util/membercluster_client_test.go new file mode 100644 index 000000000..7fd4f342e --- /dev/null +++ b/pkg/util/membercluster_client_test.go @@ -0,0 +1,512 @@ +package util + +import ( + "context" + "io" + "net/http" + "net/http/httptest" + "reflect" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + + clusterv1alpha1 "github.com/karmada-io/karmada/pkg/apis/cluster/v1alpha1" + "github.com/karmada-io/karmada/pkg/util/gclient" +) + +// copy from go/src/net/http/internal/testcert/testcert.go +var testCA = []byte(`-----BEGIN CERTIFICATE----- +MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS +MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw +MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A +MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r +bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U +aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P +YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk +POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu +h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE +AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud +DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv +bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI +5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv +cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2 ++tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B +grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK +5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/ +WkBKOclmOV2xlTVuPw== +-----END CERTIFICATE-----`) + +func TestNewClusterClientSet(t *testing.T) { + type args struct { + clusterName string + client client.Client + clientOption *ClientOption + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "cluster not found", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(), + clientOption: nil, + }, + wantErr: true, + }, + { + name: "APIEndpoint is empty", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()). + WithObjects(newCluster("test")).Build(), + clientOption: nil, + }, + wantErr: true, + }, + { + name: "SecretRef is empty", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()). + WithObjects(withAPIEndPoint(newCluster("test"), "https://127.0.0.1")).Build(), + clientOption: nil, + }, + wantErr: true, + }, + { + name: "Secret not found", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "default", Name: "secret1"}, + }, + }).Build(), + clientOption: nil, + }, + wantErr: true, + }, + { + name: "token not found", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + }).Build(), + clientOption: nil, + }, + wantErr: true, + }, + { + name: "CA data is set", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, + }).Build(), + clientOption: &ClientOption{QPS: 100, Burst: 200}, + }, + wantErr: false, + }, + { + name: "skip TLS verification", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")}, + }).Build(), + clientOption: &ClientOption{QPS: 100, Burst: 200}, + }, + wantErr: false, + }, + { + name: "ProxyURL is error", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + ProxyURL: "://", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")}, + }).Build(), + clientOption: &ClientOption{QPS: 100, Burst: 200}, + }, + wantErr: true, + }, + { + name: "ProxyURL is set", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + ProxyURL: "http://1.1.1.1", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")}, + }).Build(), + clientOption: &ClientOption{QPS: 100, Burst: 200}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewClusterClientSet(tt.args.clusterName, tt.args.client, tt.args.clientOption) + if (err != nil) != tt.wantErr { + t.Errorf("NewClusterClientSet() error = %v, wantErr %v", err, tt.wantErr) + return + } + if err != nil { + return + } + + if got == nil { + t.Error("NewClusterClientSet() got nil") + return + } + if got.ClusterName != tt.args.clusterName { + t.Errorf("NewClusterClientSet() got.ClusterName = %v, want %v", got.ClusterName, tt.args.clusterName) + return + } + if got.KubeClient == nil { + t.Error("NewClusterClientSet() got.KubeClient got nil") + return + } + }) + } +} + +func TestNewClusterClientSet_ClientWorks(t *testing.T) { + s := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Add("Content-Type", "application/json") + _, _ = io.WriteString(rw, ` +{ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "foo" + } +}`) + })) + defer s.Close() + + const clusterName = "test" + hostClient := fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: clusterName}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: s.URL, + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{ + clusterv1alpha1.SecretTokenKey: []byte("token"), + }, + }).Build() + + clusterClient, err := NewClusterClientSet(clusterName, hostClient, nil) + if err != nil { + t.Error(err) + return + } + got, err := clusterClient.KubeClient.CoreV1().Nodes().Get(context.TODO(), "foo", metav1.GetOptions{}) + if err != nil { + t.Error(err) + return + } + + want := &corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("got = %#v, want %#v", got, want) + } +} + +func TestNewClusterDynamicClientSet(t *testing.T) { + type args struct { + clusterName string + client client.Client + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "cluster not found", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).Build(), + }, + wantErr: true, + }, + { + name: "APIEndpoint is empty", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()). + WithObjects(newCluster("test")).Build(), + }, + wantErr: true, + }, + { + name: "SecretRef is empty", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()). + WithObjects(withAPIEndPoint(newCluster("test"), "https://127.0.0.1")).Build(), + }, + wantErr: true, + }, + { + name: "Secret not found", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "default", Name: "secret1"}, + }, + }).Build(), + }, + wantErr: true, + }, + { + name: "token not found", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + }).Build(), + }, + wantErr: true, + }, + { + name: "CA data is set", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, + }).Build(), + }, + wantErr: false, + }, + { + name: "skip TLS verification", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")}, + }).Build(), + }, + wantErr: false, + }, + { + name: "ProxyURL is error", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + ProxyURL: "://", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")}, + }).Build(), + }, + wantErr: true, + }, + { + name: "ProxyURL is set", + args: args{ + clusterName: "test", + client: fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: "https://127.0.0.1", + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + ProxyURL: "http://1.1.1.1", + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token")}, + }).Build(), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewClusterDynamicClientSet(tt.args.clusterName, tt.args.client) + if (err != nil) != tt.wantErr { + t.Errorf("NewClusterClientSet() error = %v, wantErr %v", err, tt.wantErr) + return + } + if err != nil { + return + } + + if got == nil { + t.Error("NewClusterClientSet() got nil") + return + } + if got.ClusterName != tt.args.clusterName { + t.Errorf("NewClusterClientSet() got ClusterName = %v, want %v", got.ClusterName, tt.args.clusterName) + return + } + if got.DynamicClientSet == nil { + t.Error("NewClusterClientSet() got DynamicClientSet nil") + return + } + }) + } +} + +func TestNewClusterDynamicClientSet_ClientWorks(t *testing.T) { + s := httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Add("Content-Type", "application/json") + _, _ = io.WriteString(rw, ` +{ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "foo" + } +}`) + })) + defer s.Close() + + const clusterName = "test" + hostClient := fakeclient.NewClientBuilder().WithScheme(gclient.NewSchema()).WithObjects( + &clusterv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: clusterName}, + Spec: clusterv1alpha1.ClusterSpec{ + APIEndpoint: s.URL, + SecretRef: &clusterv1alpha1.LocalSecretReference{Namespace: "ns1", Name: "secret1"}, + InsecureSkipTLSVerification: true, + }, + }, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, + Data: map[string][]byte{ + clusterv1alpha1.SecretTokenKey: []byte("token"), + }, + }).Build() + + clusterClient, err := NewClusterDynamicClientSet(clusterName, hostClient) + if err != nil { + t.Error(err) + return + } + + nodeGVR := corev1.SchemeGroupVersion.WithResource("nodes") + got, err := clusterClient.DynamicClientSet.Resource(nodeGVR).Get(context.TODO(), "foo", metav1.GetOptions{}) + if err != nil { + t.Error(err) + return + } + + want := &unstructured.Unstructured{} + want.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Node")) + want.SetName("foo") + + if !reflect.DeepEqual(got, want) { + t.Errorf("got = %#v, want %#v", got, want) + } +}