/* Copyright 2022 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 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"}, ProxyURL: "://", }, }, &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: 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"}, ProxyURL: "http://1.1.1.1", }, }, &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, }, } 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, _ *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"}, }, }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, }).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"}, }, }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, }).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"}, ProxyURL: "://", }, }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, }).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"}, ProxyURL: "http://1.1.1.1", }, }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, }).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, _ *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"}, }, }, &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "secret1"}, Data: map[string][]byte{clusterv1alpha1.SecretTokenKey: []byte("token"), clusterv1alpha1.SecretCADataKey: testCA}, }).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) } }