scotty.scott 2 лет назад
Родитель
Сommit
00e62b494b

+ 91 - 0
controllers/bluegreen/client.go

@@ -0,0 +1,91 @@
+package bluegreen
+
+import (
+	"context"
+	"fmt"
+	"strings"
+
+	_ "github.com/golang/mock/mockgen/model"
+	v1 "github.com/kakao/bluegreen/api/v1"
+	appsv1 "k8s.io/api/apps/v1"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+//go:generate go run github.com/golang/mock/mockgen@v1.6.0 -package bluegreen -destination mock.gen.go . Client
+type Client interface {
+	client.Reader
+
+	CreateOrUpdateService(ctx context.Context, owner *v1.BlueGreen) error
+	CreateOrUpdateDeployment(ctx context.Context, owner *v1.BlueGreen, phase v1.BlueOrGreen, podSpec corev1.PodSpec) error
+
+	UpdateStatus(ctx context.Context, req ctrl.Request, status v1.BlueGreenStatus) error
+}
+
+var _ Client = &BlueGreenClient{}
+
+type BlueGreenClient struct {
+	client.Client
+}
+
+func (r *BlueGreenClient) UpdateStatus(ctx context.Context, req reconcile.Request, status v1.BlueGreenStatus) error {
+	return r.Status().Update(ctx, &v1.BlueGreen{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      req.Name,
+			Namespace: req.Namespace,
+		},
+		Status: status,
+	})
+}
+
+func (r *BlueGreenClient) CreateOrUpdateDeployment(ctx context.Context, owner *v1.BlueGreen, phase v1.BlueOrGreen, podSpec corev1.PodSpec) error {
+	deploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: owner.Namespace, Name: deploymentName(owner.Name, phase)}}
+	if _, err := ctrl.CreateOrUpdate(ctx, r.Client, deploy, func() error {
+		if err := ctrl.SetControllerReference(owner, deploy, r.Scheme()); err != nil {
+			return err
+		}
+		label := map[string]string{
+			"app.kubernetes.io/managed-by": "app.demo.kakao.com",
+			"app.kubernetes.io/name":       owner.Name,
+			"app.kubernetes.io/phase":      string(phase),
+		}
+		deploy.Spec.Selector = &metav1.LabelSelector{MatchLabels: label}
+		deploy.Spec.Template = corev1.PodTemplateSpec{
+			ObjectMeta: metav1.ObjectMeta{Labels: label},
+			Spec:       podSpec,
+		}
+		return nil
+	}); err != nil {
+		return err
+	}
+	return nil
+}
+func (r *BlueGreenClient) CreateOrUpdateService(ctx context.Context, owner *v1.BlueGreen) error {
+	svc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: owner.Namespace, Name: owner.Name}}
+	if _, err := ctrl.CreateOrUpdate(ctx, r.Client, svc,
+		func() error {
+			if err := ctrl.SetControllerReference(owner, svc, r.Scheme()); err != nil {
+				return err
+			}
+			svc.Spec.Ports = []corev1.ServicePort{
+				{Name: "http", Protocol: corev1.ProtocolTCP, Port: 80},
+			}
+			svc.Spec.Selector = map[string]string{
+				"app.kubernetes.io/managed-by": "app.demo.kakao.com",
+				"app.kubernetes.io/name":       owner.Name,
+				"app.kubernetes.io/phase":      string(owner.Spec.RouteTo),
+			}
+			return nil
+		},
+	); err != nil {
+		return err
+	}
+	return nil
+}
+
+func deploymentName(name string, phase v1.BlueOrGreen) string {
+	return fmt.Sprintf("%s-%s", name, strings.ToLower(string(phase)))
+}

+ 54 - 0
controllers/bluegreen/client_test.go

@@ -0,0 +1,54 @@
+package bluegreen
+
+import (
+	"context"
+	"testing"
+
+	appv1 "github.com/kakao/bluegreen/api/v1"
+	"github.com/stretchr/testify/assert"
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/types"
+	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+)
+
+var (
+	testScheme = runtime.NewScheme()
+)
+
+func init() {
+	utilruntime.Must(clientgoscheme.AddToScheme(testScheme))
+	utilruntime.Must(appv1.AddToScheme(testScheme))
+}
+
+func TestBlueGreenReconciler_CreateOrUpdateService(t *testing.T) {
+	t.Run("Should Create a New Service", func(t *testing.T) {
+		c := fake.NewClientBuilder().WithScheme(testScheme).Build()
+		r := &BlueGreenClient{Client: c}
+		NN := types.NamespacedName{
+			Namespace: "test-namespace",
+			Name:      "test-name",
+		}
+
+		svc := new(corev1.Service)
+		assert.Error(t, c.Get(context.TODO(), NN, svc))
+
+		err := r.CreateOrUpdateService(
+			context.TODO(),
+			&appv1.BlueGreen{
+				ObjectMeta: metav1.ObjectMeta{Namespace: NN.Namespace, Name: NN.Name},
+				Spec:       appv1.BlueGreenSpec{RouteTo: appv1.Blue},
+			})
+
+		assert.NoError(t, err)
+		assert.NoError(t, c.Get(context.TODO(), NN, svc))
+		assert.Equal(t, 1, len(svc.ObjectMeta.OwnerReferences))
+		assert.Equal(t, "app.demo.kakao.com", svc.Spec.Selector["app.kubernetes.io/managed-by"])
+		assert.Equal(t, "Blue", svc.Spec.Selector["app.kubernetes.io/phase"])
+		assert.Equal(t, NN.Name, svc.Spec.Selector["app.kubernetes.io/name"])
+		/* more validation here */
+	})
+}

+ 115 - 0
controllers/bluegreen/mock.gen.go

@@ -0,0 +1,115 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: github.com/kakao/bluegreen/controllers/bluegreen (interfaces: Client)
+
+// Package bluegreen is a generated GoMock package.
+package bluegreen
+
+import (
+	context "context"
+	reflect "reflect"
+
+	gomock "github.com/golang/mock/gomock"
+	v1 "github.com/kakao/bluegreen/api/v1"
+	v10 "k8s.io/api/core/v1"
+	types "k8s.io/apimachinery/pkg/types"
+	client "sigs.k8s.io/controller-runtime/pkg/client"
+	reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile"
+)
+
+// MockClient is a mock of Client interface.
+type MockClient struct {
+	ctrl     *gomock.Controller
+	recorder *MockClientMockRecorder
+}
+
+// MockClientMockRecorder is the mock recorder for MockClient.
+type MockClientMockRecorder struct {
+	mock *MockClient
+}
+
+// NewMockClient creates a new mock instance.
+func NewMockClient(ctrl *gomock.Controller) *MockClient {
+	mock := &MockClient{ctrl: ctrl}
+	mock.recorder = &MockClientMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockClient) EXPECT() *MockClientMockRecorder {
+	return m.recorder
+}
+
+// CreateOrUpdateDeployment mocks base method.
+func (m *MockClient) CreateOrUpdateDeployment(arg0 context.Context, arg1 *v1.BlueGreen, arg2 v1.BlueOrGreen, arg3 v10.PodSpec) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CreateOrUpdateDeployment", arg0, arg1, arg2, arg3)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// CreateOrUpdateDeployment indicates an expected call of CreateOrUpdateDeployment.
+func (mr *MockClientMockRecorder) CreateOrUpdateDeployment(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdateDeployment", reflect.TypeOf((*MockClient)(nil).CreateOrUpdateDeployment), arg0, arg1, arg2, arg3)
+}
+
+// CreateOrUpdateService mocks base method.
+func (m *MockClient) CreateOrUpdateService(arg0 context.Context, arg1 *v1.BlueGreen) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "CreateOrUpdateService", arg0, arg1)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// CreateOrUpdateService indicates an expected call of CreateOrUpdateService.
+func (mr *MockClientMockRecorder) CreateOrUpdateService(arg0, arg1 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdateService", reflect.TypeOf((*MockClient)(nil).CreateOrUpdateService), arg0, arg1)
+}
+
+// Get mocks base method.
+func (m *MockClient) Get(arg0 context.Context, arg1 types.NamespacedName, arg2 client.Object) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// Get indicates an expected call of Get.
+func (mr *MockClientMockRecorder) Get(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockClient)(nil).Get), arg0, arg1, arg2)
+}
+
+// List mocks base method.
+func (m *MockClient) List(arg0 context.Context, arg1 client.ObjectList, arg2 ...client.ListOption) error {
+	m.ctrl.T.Helper()
+	varargs := []interface{}{arg0, arg1}
+	for _, a := range arg2 {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "List", varargs...)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// List indicates an expected call of List.
+func (mr *MockClientMockRecorder) List(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	varargs := append([]interface{}{arg0, arg1}, arg2...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockClient)(nil).List), varargs...)
+}
+
+// UpdateStatus mocks base method.
+func (m *MockClient) UpdateStatus(arg0 context.Context, arg1 reconcile.Request, arg2 v1.BlueGreenStatus) error {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "UpdateStatus", arg0, arg1, arg2)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// UpdateStatus indicates an expected call of UpdateStatus.
+func (mr *MockClientMockRecorder) UpdateStatus(arg0, arg1, arg2 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateStatus", reflect.TypeOf((*MockClient)(nil).UpdateStatus), arg0, arg1, arg2)
+}

+ 3 - 56
controllers/bluegreen_controller.go

@@ -18,13 +18,9 @@ package controllers
 
 import (
 	"context"
-	"fmt"
-	"strings"
 
 	v1 "github.com/kakao/bluegreen/api/v1"
-	appsv1 "k8s.io/api/apps/v1"
-	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"github.com/kakao/bluegreen/controllers/bluegreen"
 	"k8s.io/apimachinery/pkg/runtime"
 	ctrl "sigs.k8s.io/controller-runtime"
 	"sigs.k8s.io/controller-runtime/pkg/client"
@@ -33,7 +29,7 @@ import (
 
 // BlueGreenReconciler reconciles a BlueGreen object
 type BlueGreenReconciler struct {
-	client.Client
+	bluegreen.Client
 	Scheme *runtime.Scheme
 }
 
@@ -76,7 +72,7 @@ func (r *BlueGreenReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
 	bluegreen.Status = v1.BlueGreenStatus{
 		RouteTo: &bluegreen.Spec.RouteTo,
 	}
-	return ctrl.Result{}, r.Client.Status().Update(ctx, bluegreen)
+	return ctrl.Result{}, r.Client.UpdateStatus(ctx, req, bluegreen.Status)
 }
 
 // SetupWithManager sets up the controller with the Manager.
@@ -85,52 +81,3 @@ func (r *BlueGreenReconciler) SetupWithManager(mgr ctrl.Manager) error {
 		For(&v1.BlueGreen{}).
 		Complete(r)
 }
-
-func (r *BlueGreenReconciler) CreateOrUpdateDeployment(ctx context.Context, owner *v1.BlueGreen, phase v1.BlueOrGreen, podSpec corev1.PodSpec) error {
-	deploy := &appsv1.Deployment{ObjectMeta: metav1.ObjectMeta{Namespace: owner.Namespace, Name: deploymentName(owner.Name, phase)}}
-	if _, err := ctrl.CreateOrUpdate(ctx, r.Client, deploy, func() error {
-		if err := ctrl.SetControllerReference(owner, deploy, r.Scheme); err != nil {
-			return err
-		}
-		label := map[string]string{
-			"app.kubernetes.io/managed-by": "app.demo.kakao.com",
-			"app.kubernetes.io/name":       owner.Name,
-			"app.kubernetes.io/phase":      string(phase),
-		}
-		deploy.Spec.Selector = &metav1.LabelSelector{MatchLabels: label}
-		deploy.Spec.Template = corev1.PodTemplateSpec{
-			ObjectMeta: metav1.ObjectMeta{Labels: label},
-			Spec:       podSpec,
-		}
-		return nil
-	}); err != nil {
-		return err
-	}
-	return nil
-}
-func (r *BlueGreenReconciler) CreateOrUpdateService(ctx context.Context, owner *v1.BlueGreen) error {
-	svc := &corev1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: owner.Namespace, Name: owner.Name}}
-	if _, err := ctrl.CreateOrUpdate(ctx, r.Client, svc,
-		func() error {
-			if err := ctrl.SetControllerReference(owner, svc, r.Scheme); err != nil {
-				return err
-			}
-			svc.Spec.Ports = []corev1.ServicePort{
-				{Name: "http", Protocol: corev1.ProtocolTCP, Port: 80},
-			}
-			svc.Spec.Selector = map[string]string{
-				"app.kubernetes.io/managed-by": "app.demo.kakao.com",
-				"app.kubernetes.io/name":       owner.Name,
-				"app.kubernetes.io/phase":      string(owner.Spec.RouteTo),
-			}
-			return nil
-		},
-	); err != nil {
-		return err
-	}
-	return nil
-}
-
-func deploymentName(name string, phase v1.BlueOrGreen) string {
-	return fmt.Sprintf("%s-%s", name, strings.ToLower(string(phase)))
-}

+ 44 - 60
controllers/bluegreen_controller_test.go

@@ -1,70 +1,54 @@
-/*
-Copyright 2022.
-
-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 controllers
 
 import (
-	"context"
-	"testing"
+	"time"
 
-	appv1 "github.com/kakao/bluegreen/api/v1"
-	"github.com/stretchr/testify/assert"
+	v1 "github.com/kakao/bluegreen/api/v1"
+	. "github.com/onsi/ginkgo"
+	. "github.com/onsi/gomega"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/runtime"
-	"k8s.io/apimachinery/pkg/types"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
-	"sigs.k8s.io/controller-runtime/pkg/client/fake"
-)
-
-var (
-	testScheme = runtime.NewScheme()
 )
 
-func init() {
-	utilruntime.Must(clientgoscheme.AddToScheme(testScheme))
-	utilruntime.Must(appv1.AddToScheme(testScheme))
-}
-
-func TestBlueGreenReconciler_CreateOrUpdateService(t *testing.T) {
-	t.Run("Should Create a New Service", func(t *testing.T) {
-		c := fake.NewClientBuilder().WithScheme(testScheme).Build()
-		r := &BlueGreenReconciler{Client: c, Scheme: c.Scheme()}
-		NN := types.NamespacedName{
-			Namespace: "test-namespace",
-			Name:      "test-name",
-		}
-
-		svc := new(corev1.Service)
-		assert.Error(t, c.Get(context.TODO(), NN, svc))
-
-		err := r.CreateOrUpdateService(
-			context.TODO(),
-			&appv1.BlueGreen{
-				ObjectMeta: metav1.ObjectMeta{Namespace: NN.Namespace, Name: NN.Name},
-				Spec:       appv1.BlueGreenSpec{RouteTo: appv1.Blue},
+var _ = Describe("BlueGreen controller", func() {
+	const (
+		timeout  = time.Second * 10
+		duration = time.Second * 10
+		interval = time.Millisecond * 250
+	)
+	Context("When creating a New BlueGreen Resource", func() {
+		Context("When defining Blue Spec only", func() {
+			It("Should Create a One Service and a One Deployment", func() {
+				Expect(
+					k8sClient.Create(ctx,
+						&v1.BlueGreen{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "bluegreen-test",
+								Namespace: "default",
+							},
+							Spec: v1.BlueGreenSpec{
+								RouteTo: v1.Blue,
+								BlueSpec: &corev1.PodSpec{
+									Containers: []corev1.Container{
+										{Name: "blue", Image: "nginx"},
+									},
+								},
+							},
+						},
+					),
+				).Should(Succeed())
+
+				svcList := &corev1.ServiceList{}
+				Eventually(func() bool {
+					if err := k8sClient.List(ctx, svcList); err != nil {
+						return false
+					}
+					return len(svcList.Items) != 0
+				}, timeout, interval).Should(BeTrue())
+				Expect(len(svcList.Items)).To(Equal(1))
+				/* More Validation Logics here */
 			})
-
-		assert.NoError(t, err)
-		assert.NoError(t, c.Get(context.TODO(), NN, svc))
-		assert.Equal(t, 1, len(svc.ObjectMeta.OwnerReferences))
-		assert.Equal(t, "app.demo.kakao.com", svc.Spec.Selector["app.kubernetes.io/managed-by"])
-		assert.Equal(t, "Blue", svc.Spec.Selector["app.kubernetes.io/phase"])
-		assert.Equal(t, NN.Name, svc.Spec.Selector["app.kubernetes.io/name"])
-		/* more validation here */
+		})
+		/* More Test Scenarios here */
 	})
-}
+})

+ 27 - 3
controllers/suite_test.go

@@ -17,19 +17,24 @@ limitations under the License.
 package controllers
 
 import (
+	"context"
 	"path/filepath"
+	"testing"
 
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	ctrl "sigs.k8s.io/controller-runtime"
 
 	"k8s.io/client-go/kubernetes/scheme"
 	"k8s.io/client-go/rest"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/envtest"
+	"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
 	logf "sigs.k8s.io/controller-runtime/pkg/log"
 	"sigs.k8s.io/controller-runtime/pkg/log/zap"
 
 	appv1 "github.com/kakao/bluegreen/api/v1"
+	"github.com/kakao/bluegreen/controllers/bluegreen"
 	//+kubebuilder:scaffold:imports
 )
 
@@ -39,8 +44,9 @@ import (
 var cfg *rest.Config
 var k8sClient client.Client
 var testEnv *envtest.Environment
+var ctx context.Context
+var cancel context.CancelFunc
 
-/*
 func TestAPIs(t *testing.T) {
 	RegisterFailHandler(Fail)
 
@@ -48,11 +54,10 @@ func TestAPIs(t *testing.T) {
 		"Controller Suite",
 		[]Reporter{printer.NewlineReporter{}})
 }
-*/
 
 var _ = BeforeSuite(func() {
 	logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
-
+	ctx, cancel = context.WithCancel(context.TODO())
 	By("bootstrapping test environment")
 	testEnv = &envtest.Environment{
 		CRDDirectoryPaths:     []string{filepath.Join("..", "config", "crd", "bases")},
@@ -74,9 +79,28 @@ var _ = BeforeSuite(func() {
 	Expect(err).NotTo(HaveOccurred())
 	Expect(k8sClient).NotTo(BeNil())
 
+	k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
+		Scheme: scheme.Scheme,
+	})
+	Expect(err).ToNot(HaveOccurred())
+
+	err = (&BlueGreenReconciler{
+		Client: &bluegreen.BlueGreenClient{
+			Client: k8sManager.GetClient(),
+		},
+		Scheme: k8sManager.GetScheme(),
+	}).SetupWithManager(k8sManager)
+	Expect(err).ToNot(HaveOccurred())
+
+	go func() {
+		defer GinkgoRecover()
+		err = k8sManager.Start(ctx)
+		Expect(err).ToNot(HaveOccurred(), "failed to run manager")
+	}()
 }, 60)
 
 var _ = AfterSuite(func() {
+	cancel()
 	By("tearing down the test environment")
 	err := testEnv.Stop()
 	Expect(err).NotTo(HaveOccurred())

+ 5 - 3
go.mod

@@ -3,8 +3,9 @@ module github.com/kakao/bluegreen
 go 1.18
 
 require (
+	github.com/golang/mock v1.6.0
 	github.com/onsi/ginkgo v1.16.5
-	github.com/onsi/gomega v1.18.1
+	github.com/onsi/gomega v1.19.0
 	github.com/stretchr/testify v1.7.0
 	k8s.io/api v0.24.2
 	k8s.io/apimachinery v0.24.2
@@ -50,6 +51,7 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
 	github.com/nxadm/tail v1.4.8 // indirect
+	github.com/onsi/ginkgo/v2 v2.1.4 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_golang v1.12.1 // indirect
@@ -61,9 +63,9 @@ require (
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.19.1 // indirect
 	golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
-	golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
+	golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
 	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
-	golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect
+	golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 // indirect
 	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect

+ 11 - 0
go.sum

@@ -195,6 +195,8 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
 github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
 github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
 github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
+github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -371,12 +373,16 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
 github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
 github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
+github.com/onsi/ginkgo/v2 v2.1.4 h1:GNapqRSid3zijZ9H77KrgVG4/8KqiyRsxcSxe+7ApXY=
+github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
 github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
+github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
 github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
@@ -609,6 +615,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -701,6 +709,8 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158 h1:rm+CHSpPEEW2IsXUib1ThaHIjuBVZjxNgSKmBLFfD4c=
 golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
+golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -776,6 +786,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.10-0.20220218145154-897bd77cd717/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=

+ 4 - 1
main.go

@@ -33,6 +33,7 @@ import (
 
 	appv1 "github.com/kakao/bluegreen/api/v1"
 	"github.com/kakao/bluegreen/controllers"
+	"github.com/kakao/bluegreen/controllers/bluegreen"
 	//+kubebuilder:scaffold:imports
 )
 
@@ -90,7 +91,9 @@ func main() {
 	}
 
 	if err = (&controllers.BlueGreenReconciler{
-		Client: mgr.GetClient(),
+		Client: &bluegreen.BlueGreenClient{
+			Client: mgr.GetClient(),
+		},
 		Scheme: mgr.GetScheme(),
 	}).SetupWithManager(mgr); err != nil {
 		setupLog.Error(err, "unable to create controller", "controller", "BlueGreen")