364 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
/*
 | 
						|
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 (
 | 
						|
	"fmt"
 | 
						|
	"reflect"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	corev1 "k8s.io/api/core/v1"
 | 
						|
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
						|
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime"
 | 
						|
	"k8s.io/apimachinery/pkg/runtime/schema"
 | 
						|
	"k8s.io/client-go/kubernetes"
 | 
						|
	"k8s.io/client-go/kubernetes/fake"
 | 
						|
	kubetesting "k8s.io/client-go/testing"
 | 
						|
)
 | 
						|
 | 
						|
func TestCreateServiceAccount(t *testing.T) {
 | 
						|
	type args struct {
 | 
						|
		client kubernetes.Interface
 | 
						|
		sa     *corev1.ServiceAccount
 | 
						|
	}
 | 
						|
	tests := []struct {
 | 
						|
		name    string
 | 
						|
		args    args
 | 
						|
		want    *corev1.ServiceAccount
 | 
						|
		wantErr bool
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "service account already exist",
 | 
						|
			args: args{
 | 
						|
				client: fake.NewSimpleClientset(makeServiceAccount("test")),
 | 
						|
				sa:     makeServiceAccount("test"),
 | 
						|
			},
 | 
						|
			want:    makeServiceAccount("test"),
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account create error",
 | 
						|
			args: args{
 | 
						|
				client: alwaysErrorKubeClient,
 | 
						|
				sa:     makeServiceAccount("test"),
 | 
						|
			},
 | 
						|
			want:    nil,
 | 
						|
			wantErr: true,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account create success",
 | 
						|
			args: args{
 | 
						|
				client: fake.NewSimpleClientset(),
 | 
						|
				sa:     makeServiceAccount("test"),
 | 
						|
			},
 | 
						|
			want:    makeServiceAccount("test"),
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			got, err := CreateServiceAccount(tt.args.client, tt.args.sa)
 | 
						|
			if (err != nil) != tt.wantErr {
 | 
						|
				t.Errorf("CreateServiceAccount() error = %v, wantErr %v", err, tt.wantErr)
 | 
						|
				return
 | 
						|
			}
 | 
						|
			if !reflect.DeepEqual(got, tt.want) {
 | 
						|
				t.Errorf("CreateServiceAccount() got = %v, want %v", got, tt.want)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestDeleteServiceAccount(t *testing.T) {
 | 
						|
	type args struct {
 | 
						|
		client    kubernetes.Interface
 | 
						|
		namespace string
 | 
						|
		name      string
 | 
						|
	}
 | 
						|
	tests := []struct {
 | 
						|
		name    string
 | 
						|
		args    args
 | 
						|
		wantErr bool
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "service account not found",
 | 
						|
			args: args{
 | 
						|
				client:    fake.NewSimpleClientset(),
 | 
						|
				namespace: metav1.NamespaceDefault,
 | 
						|
				name:      "test",
 | 
						|
			},
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account delete failed",
 | 
						|
			args: args{
 | 
						|
				client:    alwaysErrorKubeClient,
 | 
						|
				namespace: metav1.NamespaceDefault,
 | 
						|
				name:      "test",
 | 
						|
			},
 | 
						|
			wantErr: true,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account delete success",
 | 
						|
			args: args{
 | 
						|
				client:    fake.NewSimpleClientset(makeServiceAccount("test")),
 | 
						|
				namespace: metav1.NamespaceDefault,
 | 
						|
				name:      "test",
 | 
						|
			},
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			if err := DeleteServiceAccount(tt.args.client, tt.args.namespace, tt.args.name); (err != nil) != tt.wantErr {
 | 
						|
				t.Errorf("DeleteServiceAccount() error = %v, wantErr %v", err, tt.wantErr)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestEnsureServiceAccountExist(t *testing.T) {
 | 
						|
	type args struct {
 | 
						|
		client            kubernetes.Interface
 | 
						|
		serviceAccountObj *corev1.ServiceAccount
 | 
						|
		dryRun            bool
 | 
						|
	}
 | 
						|
	tests := []struct {
 | 
						|
		name    string
 | 
						|
		args    args
 | 
						|
		want    *corev1.ServiceAccount
 | 
						|
		wantErr bool
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "dry run",
 | 
						|
			args: args{
 | 
						|
				client:            fake.NewSimpleClientset(),
 | 
						|
				serviceAccountObj: makeServiceAccount("test"),
 | 
						|
				dryRun:            true,
 | 
						|
			},
 | 
						|
			want:    makeServiceAccount("test"),
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account already exist",
 | 
						|
			args: args{
 | 
						|
				client:            fake.NewSimpleClientset(makeServiceAccount("test")),
 | 
						|
				serviceAccountObj: makeServiceAccount("test"),
 | 
						|
				dryRun:            false,
 | 
						|
			},
 | 
						|
			want:    makeServiceAccount("test"),
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account not exists",
 | 
						|
			args: args{
 | 
						|
				client:            fake.NewSimpleClientset(),
 | 
						|
				serviceAccountObj: makeServiceAccount("test"),
 | 
						|
				dryRun:            false,
 | 
						|
			},
 | 
						|
			want:    makeServiceAccount("test"),
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "get service account error",
 | 
						|
			args: args{
 | 
						|
				client: func() kubernetes.Interface {
 | 
						|
					c := fake.NewSimpleClientset()
 | 
						|
					c.PrependReactor("get", "*", errorAction)
 | 
						|
					return c
 | 
						|
				}(),
 | 
						|
				serviceAccountObj: makeServiceAccount("test"),
 | 
						|
				dryRun:            false,
 | 
						|
			},
 | 
						|
			want:    nil,
 | 
						|
			wantErr: true,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "create service account error",
 | 
						|
			args: args{
 | 
						|
				client: func() kubernetes.Interface {
 | 
						|
					c := fake.NewSimpleClientset()
 | 
						|
					c.PrependReactor("create", "*", errorAction)
 | 
						|
					return c
 | 
						|
				}(),
 | 
						|
				serviceAccountObj: makeServiceAccount("test"),
 | 
						|
				dryRun:            false,
 | 
						|
			},
 | 
						|
			want:    nil,
 | 
						|
			wantErr: true,
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			got, err := EnsureServiceAccountExist(tt.args.client, tt.args.serviceAccountObj, tt.args.dryRun)
 | 
						|
			if (err != nil) != tt.wantErr {
 | 
						|
				t.Errorf("EnsureServiceAccountExist() error = %v, wantErr %v", err, tt.wantErr)
 | 
						|
				return
 | 
						|
			}
 | 
						|
			if !reflect.DeepEqual(got, tt.want) {
 | 
						|
				t.Errorf("EnsureServiceAccountExist() got = %v, want %v", got, tt.want)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestIsServiceAccountExist(t *testing.T) {
 | 
						|
	type args struct {
 | 
						|
		client    kubernetes.Interface
 | 
						|
		namespace string
 | 
						|
		name      string
 | 
						|
	}
 | 
						|
	tests := []struct {
 | 
						|
		name    string
 | 
						|
		args    args
 | 
						|
		want    bool
 | 
						|
		wantErr bool
 | 
						|
	}{
 | 
						|
		{
 | 
						|
			name: "service account not found",
 | 
						|
			args: args{
 | 
						|
				client:    fake.NewSimpleClientset(),
 | 
						|
				namespace: metav1.NamespaceDefault,
 | 
						|
				name:      "test",
 | 
						|
			},
 | 
						|
			want:    false,
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "kube client return error",
 | 
						|
			args: args{
 | 
						|
				client:    alwaysErrorKubeClient,
 | 
						|
				namespace: metav1.NamespaceDefault,
 | 
						|
				name:      "test",
 | 
						|
			},
 | 
						|
			want:    false,
 | 
						|
			wantErr: true,
 | 
						|
		},
 | 
						|
		{
 | 
						|
			name: "service account already exist",
 | 
						|
			args: args{
 | 
						|
				client:    fake.NewSimpleClientset(makeServiceAccount("test")),
 | 
						|
				namespace: metav1.NamespaceDefault,
 | 
						|
				name:      "test",
 | 
						|
			},
 | 
						|
			want:    true,
 | 
						|
			wantErr: false,
 | 
						|
		},
 | 
						|
	}
 | 
						|
	for _, tt := range tests {
 | 
						|
		t.Run(tt.name, func(t *testing.T) {
 | 
						|
			got, err := IsServiceAccountExist(tt.args.client, tt.args.namespace, tt.args.name)
 | 
						|
			if (err != nil) != tt.wantErr {
 | 
						|
				t.Errorf("IsServiceAccountExist() error = %v, wantErr %v", err, tt.wantErr)
 | 
						|
				return
 | 
						|
			}
 | 
						|
			if got != tt.want {
 | 
						|
				t.Errorf("IsServiceAccountExist() got = %v, want %v", got, tt.want)
 | 
						|
			}
 | 
						|
		})
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestWaitForServiceAccountSecretCreation(t *testing.T) {
 | 
						|
	sa := makeServiceAccount("test")
 | 
						|
	saVisitCount, secretVisitCount := 0, 0
 | 
						|
 | 
						|
	// ROUND1: get serviceAccount returns not found
 | 
						|
	// ROUND2: get secret not found
 | 
						|
	//         create secret
 | 
						|
	//         token is empty
 | 
						|
	// ROUND3: token exist
 | 
						|
	injectSecretToken := func(client *fake.Clientset, secret *corev1.Secret) error {
 | 
						|
		secret = secret.DeepCopy()
 | 
						|
		secret.Data = map[string][]byte{
 | 
						|
			"token": []byte("test token"),
 | 
						|
		}
 | 
						|
		return client.Tracker().Update(schema.GroupVersionResource{Version: "v1", Resource: "secrets"}, secret, metav1.NamespaceDefault)
 | 
						|
	}
 | 
						|
 | 
						|
	client := fake.NewSimpleClientset(makeServiceAccount("test"))
 | 
						|
	client.PrependReactor("get", "serviceaccounts", func(action kubetesting.Action) (bool, runtime.Object, error) {
 | 
						|
		saVisitCount++
 | 
						|
		if saVisitCount == 1 {
 | 
						|
			return true, nil, apierrors.NewNotFound(action.GetResource().GroupResource(), "test")
 | 
						|
		}
 | 
						|
		return false, nil, nil
 | 
						|
	})
 | 
						|
	client.PrependReactor("get", "secrets", func(action kubetesting.Action) (bool, runtime.Object, error) {
 | 
						|
		secretVisitCount++
 | 
						|
		if secretVisitCount == 2 {
 | 
						|
			s, err := client.Tracker().Get(action.GetResource(), action.GetNamespace(), "test")
 | 
						|
			if err != nil {
 | 
						|
				return true, nil, fmt.Errorf("secret shall be found: %v", err)
 | 
						|
			}
 | 
						|
 | 
						|
			// We inject token here, so that next round will see the token
 | 
						|
			if err := injectSecretToken(client, s.(*corev1.Secret)); err != nil {
 | 
						|
				return true, nil, err
 | 
						|
			}
 | 
						|
			return true, s, nil
 | 
						|
		}
 | 
						|
		return false, nil, nil
 | 
						|
	})
 | 
						|
 | 
						|
	got, err := WaitForServiceAccountSecretCreation(client, sa)
 | 
						|
	if err != nil {
 | 
						|
		t.Error(err)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	want := &corev1.Secret{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			Namespace: sa.Namespace,
 | 
						|
			Name:      sa.Name,
 | 
						|
			Annotations: map[string]string{
 | 
						|
				corev1.ServiceAccountNameKey: sa.Name,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		Type: corev1.SecretTypeServiceAccountToken,
 | 
						|
		Data: map[string][]byte{
 | 
						|
			"token": []byte("test token"),
 | 
						|
		},
 | 
						|
	}
 | 
						|
	if !reflect.DeepEqual(got, want) {
 | 
						|
		t.Errorf("WaitForServiceAccountSecretCreation() got = %v, want %v", got, want)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	alwaysErrorKubeClient kubernetes.Interface
 | 
						|
	errorAction           = func(kubetesting.Action) (handled bool, ret runtime.Object, err error) {
 | 
						|
		return true, nil, fmt.Errorf("always error")
 | 
						|
	}
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	c := fake.NewSimpleClientset()
 | 
						|
	c.PrependReactor("*", "*", errorAction)
 | 
						|
	alwaysErrorKubeClient = c
 | 
						|
}
 | 
						|
 | 
						|
func makeServiceAccount(name string) *corev1.ServiceAccount {
 | 
						|
	return &corev1.ServiceAccount{
 | 
						|
		ObjectMeta: metav1.ObjectMeta{
 | 
						|
			Name:      name,
 | 
						|
			Namespace: metav1.NamespaceDefault,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 |