|
@@ -0,0 +1,112 @@
|
|
|
+package controllers
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "reflect"
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/golang/mock/gomock"
|
|
|
+ appv1 "github.com/kakao/bluegreen/api/v1"
|
|
|
+ "github.com/kakao/bluegreen/controllers/bluegreen"
|
|
|
+ "github.com/stretchr/testify/assert"
|
|
|
+ v1 "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/reconcile"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ testScheme = runtime.NewScheme()
|
|
|
+)
|
|
|
+
|
|
|
+func init() {
|
|
|
+ utilruntime.Must(clientgoscheme.AddToScheme(testScheme))
|
|
|
+ utilruntime.Must(appv1.AddToScheme(testScheme))
|
|
|
+}
|
|
|
+
|
|
|
+func TestBlueGreenResconciler(t *testing.T) {
|
|
|
+ ctrl := gomock.NewController(t)
|
|
|
+ defer ctrl.Finish()
|
|
|
+ c := bluegreen.NewMockClient(ctrl)
|
|
|
+ r := &BlueGreenReconciler{
|
|
|
+ Client: c,
|
|
|
+ Scheme: testScheme,
|
|
|
+ }
|
|
|
+
|
|
|
+ req := reconcile.Request{NamespacedName: types.NamespacedName{
|
|
|
+ Namespace: "test",
|
|
|
+ Name: "bluegreen",
|
|
|
+ }}
|
|
|
+ bg := appv1.BlueGreen{
|
|
|
+ ObjectMeta: metav1.ObjectMeta{
|
|
|
+ Namespace: req.Namespace,
|
|
|
+ Name: req.Name,
|
|
|
+ },
|
|
|
+ Spec: appv1.BlueGreenSpec{
|
|
|
+ RouteTo: appv1.Blue,
|
|
|
+ BlueSpec: &v1.PodSpec{
|
|
|
+ Containers: []v1.Container{{Name: "test"}},
|
|
|
+ },
|
|
|
+ GreenSpec: nil,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ blugreenMatcher := &GenericMatcher[*appv1.BlueGreen]{func(in *appv1.BlueGreen) bool { return reflect.DeepEqual(bg, *in) }}
|
|
|
+
|
|
|
+ // EXPECTING
|
|
|
+ c.EXPECT().Get(gomock.Any(), gomock.Eq(req.NamespacedName), gomock.Any()).
|
|
|
+ SetArg(2, bg).
|
|
|
+ Return(nil)
|
|
|
+ c.EXPECT().CreateOrUpdateService(gomock.Any(), blugreenMatcher).
|
|
|
+ Return(nil)
|
|
|
+ c.EXPECT().CreateOrUpdateDeployment(gomock.Any(), blugreenMatcher, gomock.Eq(appv1.Blue), gomock.Eq(*bg.Spec.BlueSpec)).
|
|
|
+ Return(nil)
|
|
|
+
|
|
|
+ c.EXPECT().UpdateStatus(gomock.Any(),
|
|
|
+ &GenericMatcher[*appv1.BlueGreen]{func(bg *appv1.BlueGreen) bool { return *bg.Status.RouteTo == appv1.Blue }},
|
|
|
+ ).Return(nil)
|
|
|
+
|
|
|
+ // Reconciling
|
|
|
+ result, err := r.Reconcile(ctx, req)
|
|
|
+
|
|
|
+ // Validation
|
|
|
+ assert.NoError(t, err)
|
|
|
+ assert.Equal(t, reconcile.Result{Requeue: false}, result)
|
|
|
+}
|
|
|
+
|
|
|
+var _ gomock.Matcher = &GenericMatcher[interface{}]{}
|
|
|
+
|
|
|
+type GenericMatcher[T interface{}] struct {
|
|
|
+ F func(T) bool
|
|
|
+}
|
|
|
+
|
|
|
+func (m *GenericMatcher[T]) Matches(x interface{}) bool {
|
|
|
+ if v, ok := x.(T); ok {
|
|
|
+ return m.F(v)
|
|
|
+ } else {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (m *GenericMatcher[T]) String() string {
|
|
|
+ return fmt.Sprintf("matching %v", reflect.TypeOf(new(T)))
|
|
|
+}
|
|
|
+
|
|
|
+var _ gomock.Matcher = &RuntimeObjectMatcher{}
|
|
|
+
|
|
|
+type RuntimeObjectMatcher struct {
|
|
|
+ object runtime.Object
|
|
|
+}
|
|
|
+
|
|
|
+// Matches implements gomock.Matcher
|
|
|
+func (r *RuntimeObjectMatcher) Matches(x interface{}) bool {
|
|
|
+ return reflect.DeepEqual(r.object, x)
|
|
|
+}
|
|
|
+
|
|
|
+// String implements gomock.Matcher
|
|
|
+func (r *RuntimeObjectMatcher) String() string {
|
|
|
+ gok := r.object.GetObjectKind().GroupVersionKind()
|
|
|
+ return fmt.Sprintf("should match %s/%s/%s", gok.Group, gok.Version, gok.Kind)
|
|
|
+}
|