Skip to content

Commit

Permalink
Merge pull request #2988 from jwcesign/get-ns-first-before-create
Browse files Browse the repository at this point in the history
Check if the resource exists before creating it
  • Loading branch information
karmada-bot authored Dec 28, 2022
2 parents 51edc93 + c28bac0 commit eda7814
Show file tree
Hide file tree
Showing 4 changed files with 207 additions and 10 deletions.
24 changes: 24 additions & 0 deletions pkg/util/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ import (
kubeclient "k8s.io/client-go/kubernetes"
)

// IsNamespaceExist tells if the namespace already exists.
func IsNamespaceExist(client kubeclient.Interface, namespace string) (bool, error) {
_, err := client.CoreV1().Namespaces().Get(context.Background(), namespace, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}

return false, err
}

return true, nil
}

// CreateNamespace just try to create the namespace.
func CreateNamespace(client kubeclient.Interface, namespaceObj *corev1.Namespace) (*corev1.Namespace, error) {
_, err := client.CoreV1().Namespaces().Create(context.TODO(), namespaceObj, metav1.CreateOptions{})
Expand Down Expand Up @@ -43,6 +57,16 @@ func EnsureNamespaceExist(client kubeclient.Interface, namespace string, dryRun
return namespaceObj, nil
}

// It's necessary to check if a namespace exists before creating it.
// Sometimes the namespace is created in advance, to give less privilege to Karmada.
exist, err := IsNamespaceExist(client, namespace)
if err != nil {
return nil, fmt.Errorf("failed to check if namespace exist. namespace: %s, error: %v", namespace, err)
}
if exist {
return namespaceObj, nil
}

createdObj, err := CreateNamespace(client, namespaceObj)
if err != nil {
return nil, fmt.Errorf("ensure namespace failed due to create failed. namespace: %s, error: %v", namespace, err)
Expand Down
82 changes: 81 additions & 1 deletion pkg/util/namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,70 @@ import (
coretesting "k8s.io/client-go/testing"
)

func TestIsNamespaceExist(t *testing.T) {
type args struct {
client *fake.Clientset
namespace string
reactor coretesting.ReactionFunc
}

tests := []struct {
name string
args args
want bool
wantErr bool
}{
{
name: "query namespace error",
args: args{
client: fake.NewSimpleClientset(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "default"}}),
namespace: metav1.NamespaceDefault,
reactor: func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, &corev1.Namespace{}, errors.New("failed to get namespace")
},
},
want: false,
wantErr: true,
},
{
name: "namespace not exists",
args: args{
client: fake.NewSimpleClientset(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "default-1"}}),
namespace: metav1.NamespaceDefault,
reactor: nil,
},
want: false,
wantErr: false,
},
{
name: "namespace already exists",
args: args{
client: fake.NewSimpleClientset(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "default"}}),
namespace: metav1.NamespaceDefault,
reactor: nil,
},
want: true,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.reactor != nil {
tt.args.client.PrependReactor("get", "namespaces", tt.args.reactor)
}

got, err := IsNamespaceExist(tt.args.client, tt.args.namespace)
if (err == nil && tt.wantErr == true) || (err != nil && tt.wantErr == false) {
t.Errorf("IsNamespaceExist() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("IsNamespaceExist() got = %v, want %v", got, tt.want)
}
})
}
}

func TestCreateNamespace(t *testing.T) {
type args struct {
client *fake.Clientset
Expand Down Expand Up @@ -135,6 +199,7 @@ func TestEnsureNamespaceExist(t *testing.T) {
client *fake.Clientset
namespace string
dryRun bool
reactorGet coretesting.ReactionFunc
reactorCreate coretesting.ReactionFunc
}
tests := []struct {
Expand Down Expand Up @@ -162,11 +227,22 @@ func TestEnsureNamespaceExist(t *testing.T) {
},
wantErr: false,
},
{
name: "check namespace exists error",
args: args{
client: fake.NewSimpleClientset(&corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "default"}}),
namespace: metav1.NamespaceDefault,
reactorGet: func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("failed to get namespace")
},
},
wantErr: true,
},
{
name: "create namespace error",
args: args{
client: fake.NewSimpleClientset(),
namespace: "default",
namespace: metav1.NamespaceDefault,
reactorCreate: func(action coretesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("failed to create namespace")
},
Expand All @@ -176,6 +252,10 @@ func TestEnsureNamespaceExist(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.reactorGet != nil {
tt.args.client.PrependReactor("get", "namespaces", tt.args.reactorGet)
}

if tt.args.reactorCreate != nil {
tt.args.client.PrependReactor("create", "namespaces", tt.args.reactorCreate)
}
Expand Down
22 changes: 22 additions & 0 deletions pkg/util/serviceaccount.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ func CreateServiceAccount(client kubeclient.Interface, saObj *corev1.ServiceAcco
return saObj, nil
}

// IsServiceAccountExist tells if specific service account already exists.
func IsServiceAccountExist(client kubeclient.Interface, namespace string, name string) (bool, error) {
_, err := client.CoreV1().ServiceAccounts(namespace).Get(context.Background(), name, metav1.GetOptions{})
if err != nil {
if apierrors.IsNotFound(err) {
return false, nil
}

return false, err
}

return true, nil
}

// DeleteServiceAccount just try to delete the ServiceAccount.
func DeleteServiceAccount(client kubeclient.Interface, namespace, name string) error {
err := client.CoreV1().ServiceAccounts(namespace).Delete(context.Background(), name, metav1.DeleteOptions{})
Expand All @@ -42,6 +56,14 @@ func EnsureServiceAccountExist(client kubeclient.Interface, serviceAccountObj *c
return serviceAccountObj, nil
}

exist, err := IsServiceAccountExist(client, serviceAccountObj.Namespace, serviceAccountObj.Name)
if err != nil {
return nil, fmt.Errorf("failed to check if service account exist. service account: %s/%s, error: %v", serviceAccountObj.Namespace, serviceAccountObj.Name, err)
}
if exist {
return serviceAccountObj, nil
}

createdObj, err := CreateServiceAccount(client, serviceAccountObj)
if err != nil {
return nil, fmt.Errorf("ensure service account failed due to create failed. service account: %s/%s, error: %v", serviceAccountObj.Namespace, serviceAccountObj.Name, err)
Expand Down
89 changes: 80 additions & 9 deletions pkg/util/serviceaccount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestCreateServiceAccount(t *testing.T) {
wantErr bool
}{
{
name: "already exist",
name: "service account already exist",
args: args{
client: fake.NewSimpleClientset(makeServiceAccount("test")),
sa: makeServiceAccount("test"),
Expand All @@ -36,7 +36,7 @@ func TestCreateServiceAccount(t *testing.T) {
wantErr: false,
},
{
name: "create error",
name: "service account create error",
args: args{
client: alwaysErrorKubeClient,
sa: makeServiceAccount("test"),
Expand All @@ -45,7 +45,7 @@ func TestCreateServiceAccount(t *testing.T) {
wantErr: true,
},
{
name: "create success",
name: "service account create success",
args: args{
client: fake.NewSimpleClientset(),
sa: makeServiceAccount("test"),
Expand Down Expand Up @@ -80,7 +80,7 @@ func TestDeleteServiceAccount(t *testing.T) {
wantErr bool
}{
{
name: "not found",
name: "service account not found",
args: args{
client: fake.NewSimpleClientset(),
namespace: metav1.NamespaceDefault,
Expand All @@ -89,7 +89,7 @@ func TestDeleteServiceAccount(t *testing.T) {
wantErr: false,
},
{
name: "error",
name: "service account delete failed",
args: args{
client: alwaysErrorKubeClient,
namespace: metav1.NamespaceDefault,
Expand All @@ -98,7 +98,7 @@ func TestDeleteServiceAccount(t *testing.T) {
wantErr: true,
},
{
name: "success",
name: "service account delete success",
args: args{
client: fake.NewSimpleClientset(makeServiceAccount("test")),
namespace: metav1.NamespaceDefault,
Expand Down Expand Up @@ -139,7 +139,7 @@ func TestEnsureServiceAccountExist(t *testing.T) {
wantErr: false,
},
{
name: "exist",
name: "service account already exist",
args: args{
client: fake.NewSimpleClientset(makeServiceAccount("test")),
serviceAccountObj: makeServiceAccount("test"),
Expand All @@ -149,7 +149,7 @@ func TestEnsureServiceAccountExist(t *testing.T) {
wantErr: false,
},
{
name: "not exist",
name: "service account not exists",
args: args{
client: fake.NewSimpleClientset(),
serviceAccountObj: makeServiceAccount("test"),
Expand All @@ -159,7 +159,21 @@ func TestEnsureServiceAccountExist(t *testing.T) {
wantErr: false,
},
{
name: "create error",
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()
Expand Down Expand Up @@ -187,6 +201,63 @@ func TestEnsureServiceAccountExist(t *testing.T) {
}
}

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
Expand Down

0 comments on commit eda7814

Please sign in to comment.