From 8fd3d3416d4bccc99fbe48c54ae7aca27b4f988c Mon Sep 17 00:00:00 2001 From: Jmnote Date: Sun, 18 Aug 2024 03:17:41 +0900 Subject: [PATCH] fetchConfigMapDirectly (#19) --- .devcontainer/devcontainer.json | 3 +- Dockerfile | 1 + cmd/main.go | 30 +- cmd/main_test.go | 49 +- config/rbac/role.yaml | 4 - controller/configmap_controller.go | 49 +- controller/configmap_controller_test.go | 198 +++----- controller/ingress_controller.go | 10 +- controller/ingress_controller_test.go | 9 +- controller/rulesstore/rulesstore_test.go | 465 ------------------ {controller => pkg}/matcher/matcher.go | 0 {controller => pkg}/matcher/matcher_test.go | 0 {controller => pkg}/model/model.go | 0 {controller => pkg}/rulesstore/rulesstore.go | 28 +- pkg/rulesstore/rulesstore_test.go | 209 ++++++++ .../testutil}/fakeclient/fakeclient.go | 23 +- .../testutil}/fakeclient/fakeclient_test.go | 0 pkg/testutil/fakereconciler/fakereconciler.go | 14 + .../mockrulesstore/mockrulesstore.go | 7 +- .../mockrulesstore/mockrulesstore_test.go | 8 +- 20 files changed, 427 insertions(+), 680 deletions(-) delete mode 100644 controller/rulesstore/rulesstore_test.go rename {controller => pkg}/matcher/matcher.go (100%) rename {controller => pkg}/matcher/matcher_test.go (100%) rename {controller => pkg}/model/model.go (100%) rename {controller => pkg}/rulesstore/rulesstore.go (72%) create mode 100644 pkg/rulesstore/rulesstore_test.go rename {controller => pkg/testutil}/fakeclient/fakeclient.go (70%) rename {controller => pkg/testutil}/fakeclient/fakeclient_test.go (100%) create mode 100644 pkg/testutil/fakereconciler/fakereconciler.go rename {controller/rulesstore => pkg/testutil}/mockrulesstore/mockrulesstore.go (66%) rename {controller/rulesstore => pkg/testutil}/mockrulesstore/mockrulesstore_test.go (83%) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2e6d681..a68ba0b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/go:1-1.22-bookworm", "features": { - "ghcr.io/devcontainers/features/docker-in-docker:2": {} + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers-contrib/features/kind:1": {} } // Features to add to the dev container. More info: https://containers.dev/features. diff --git a/Dockerfile b/Dockerfile index 52ff732..535bcbe 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ RUN go mod download # Copy the go source COPY cmd/main.go cmd/main.go COPY controller/ controller/ +COPY pkg/ pkg/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command diff --git a/cmd/main.go b/cmd/main.go index d3ad09e..45a3dd9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -24,20 +24,22 @@ import ( "fmt" "os" - "github.com/kuoss/ingress-annotator/controller" - "github.com/kuoss/ingress-annotator/controller/rulesstore" - _ "k8s.io/client-go/plugin/pkg/client/auth" - + corev1 "k8s.io/api/core/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" + _ "k8s.io/client-go/plugin/pkg/client/auth" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" "sigs.k8s.io/controller-runtime/pkg/webhook" + + "github.com/kuoss/ingress-annotator/controller" + "github.com/kuoss/ingress-annotator/pkg/rulesstore" // +kubebuilder:scaffold:imports ) @@ -135,22 +137,25 @@ func run(mgr ctrl.Manager) error { Namespace: ns, Name: configMapName, } - rulesStore, err := rulesstore.New(mgr.GetClient(), nn) + + cm, err := fetchConfigMapDirectly(mgr.GetAPIReader(), nn) + if err != nil { + return err + } + rulesStore, err := rulesstore.New(cm) if err != nil { return fmt.Errorf("unable to start rules store: %w", err) } if err = (&controller.ConfigMapReconciler{ Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - ConfigNN: nn, + NN: nn, RulesStore: rulesStore, }).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create ConfigMapReconciler: %w", err) } if err = (&controller.IngressReconciler{ Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), RulesStore: rulesStore, }).SetupWithManager(mgr); err != nil { return fmt.Errorf("unable to create IngressReconciler: %w", err) @@ -171,3 +176,12 @@ func run(mgr ctrl.Manager) error { return nil } + +func fetchConfigMapDirectly(reader client.Reader, nn types.NamespacedName) (*corev1.ConfigMap, error) { + cm := &corev1.ConfigMap{} + err := reader.Get(context.Background(), nn, cm) + if err != nil { + return nil, fmt.Errorf("failed to fetch ConfigMap: %w", err) + } + return cm, nil +} diff --git a/cmd/main_test.go b/cmd/main_test.go index d1eae0f..542d9de 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -5,18 +5,20 @@ import ( "flag" "testing" + "github.com/jmnote/tester" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/config" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/webhook" "github.com/kuoss/ingress-annotator/cmd/mocks" - "github.com/kuoss/ingress-annotator/controller/fakeclient" - "github.com/kuoss/ingress-annotator/controller/model" - "github.com/kuoss/ingress-annotator/controller/rulesstore/mockrulesstore" + "github.com/kuoss/ingress-annotator/pkg/model" + "github.com/kuoss/ingress-annotator/pkg/testutil/fakeclient" + "github.com/kuoss/ingress-annotator/pkg/testutil/mockrulesstore" ) func TestGetManagerOptions(t *testing.T) { @@ -73,7 +75,7 @@ func setupMockManager(mockCtrl *gomock.Controller) (*mocks.MockManager, *mocks.M mockManager.EXPECT().AddHealthzCheck(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockManager.EXPECT().AddReadyzCheck(gomock.Any(), gomock.Any()).Return(nil).AnyTimes() mockManager.EXPECT().GetLogger().Return(zap.New(zap.WriteTo(nil))).AnyTimes() - + mockManager.EXPECT().GetAPIReader().Return(fakeClient).AnyTimes() return mockManager, mockCache } @@ -132,3 +134,42 @@ func TestRun_SuccessfulRun(t *testing.T) { err := run(mockManager) assert.NoError(t, err) } + +func TestFetchConfigMapDirectly(t *testing.T) { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "my-configmap"}, + Data: map[string]string{"key": "value"}, + } + testCases := []struct { + name string + clientOpts *fakeclient.ClientOpts + nn types.NamespacedName + want *corev1.ConfigMap + wantError string + }{ + { + name: "ConfigMap fetched successfully", + nn: types.NamespacedName{Namespace: "default", Name: "my-configmap"}, + want: cm, + }, + { + name: "Client Get error", + clientOpts: &fakeclient.ClientOpts{GetError: true}, + nn: types.NamespacedName{Namespace: "default", Name: "error-configmap"}, + wantError: "failed to fetch ConfigMap: mocked Get error", + }, + } + + for i, tc := range testCases { + t.Run(tester.Name(i, tc.name), func(t *testing.T) { + client := fakeclient.NewClient(tc.clientOpts, cm) + got, err := fetchConfigMapDirectly(client, tc.nn) + if tc.wantError == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.wantError) + } + assert.Equal(t, tc.want, got) + }) + } +} diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index f9df5e3..81a5760 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -9,12 +9,8 @@ rules: resources: - configmaps verbs: - - create - - delete - get - list - - patch - - update - watch - apiGroups: - "" diff --git a/controller/configmap_controller.go b/controller/configmap_controller.go index a4fabd3..e08a87f 100644 --- a/controller/configmap_controller.go +++ b/controller/configmap_controller.go @@ -19,26 +19,26 @@ package controller import ( "context" "fmt" + "time" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/kuoss/ingress-annotator/controller/rulesstore" + "github.com/kuoss/ingress-annotator/pkg/rulesstore" ) // ConfigMapReconciler reconciles a ConfigMap object type ConfigMapReconciler struct { client.Client - Scheme *runtime.Scheme - ConfigNN types.NamespacedName + NN types.NamespacedName RulesStore rulesstore.IRulesStore } -// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch // +kubebuilder:rbac:groups=core,resources=configmaps/status,verbs=get;update;patch // +kubebuilder:rbac:groups=core,resources=configmaps/finalizers,verbs=update @@ -49,24 +49,33 @@ func (r *ConfigMapReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the ConfigMap object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.18.4/pkg/reconcile func (r *ConfigMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - if req.Namespace == r.ConfigNN.Namespace && req.Name == r.ConfigNN.Name { - logger := log.FromContext(ctx).WithValues("kind", "configmap", "namespace", req.Namespace, "name", req.Name) + // Only proceed if the request is for the ConfigMap we're watching + if req.Namespace != r.NN.Namespace || req.Name != r.NN.Name { + return ctrl.Result{}, nil + } + + logger := log.FromContext(ctx).WithValues("kind", "ConfigMap", "namespace", req.Namespace, "name", req.Name) + logger.Info("Reconciling ConfigMap") - logger.Info("Reconciling ConfigMap") - if err := r.RulesStore.UpdateRules(); err != nil { - return ctrl.Result{}, fmt.Errorf("failed to update rules in rules store: %w", err) + // Fetch the ConfigMap resource + var cm corev1.ConfigMap + if err := r.Get(ctx, r.NN, &cm); err != nil { + if errors.IsNotFound(err) { + logger.Error(err, "ConfigMap not found, will retry after delay") + // Retry after a delay if the ConfigMap is not found + return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } - logger.Info("Successfully reconciled ConfigMap") + // Return the error with context if the Get operation fails + return ctrl.Result{}, fmt.Errorf("failed to get ConfigMap: %w", err) } + + // Update rules in the RulesStore + if err := r.RulesStore.UpdateRules(&cm); err != nil { + // Return the error with context if the update fails + return ctrl.Result{}, fmt.Errorf("failed to update rules in rules store: %w", err) + } + + logger.Info("Successfully reconciled ConfigMap") return ctrl.Result{}, nil } diff --git a/controller/configmap_controller_test.go b/controller/configmap_controller_test.go index 1b1faf0..4ad6e7c 100644 --- a/controller/configmap_controller_test.go +++ b/controller/configmap_controller_test.go @@ -7,64 +7,36 @@ import ( "github.com/jmnote/tester" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/kuoss/ingress-annotator/controller/fakeclient" - "github.com/kuoss/ingress-annotator/controller/rulesstore" + "github.com/kuoss/ingress-annotator/pkg/rulesstore" + "github.com/kuoss/ingress-annotator/pkg/testutil/fakeclient" ) func TestConfigMapReconciler_SetupWithManager(t *testing.T) { testCases := []struct { - name string - namespaceEnv string - objects []client.Object - wantError string + name string + mgr ctrl.Manager + wantError string }{ { - name: "successful setup 1", - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: ctrl.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - }, - }, - wantError: "", + name: "nil Manager should return error", + mgr: nil, + wantError: "must provide a non-nil Manager", }, { - name: "successful setup 2", - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: ctrl.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": "annotations:\n key1: value1\nnamespace: test-namespace\ningress: test-ingress", - }, - }, - }, + name: "valid Manager should not return error", + mgr: fakeclient.NewManager(), wantError: "", }, } for i, tc := range testCases { t.Run(tester.Name(i, tc.name), func(t *testing.T) { - nn := types.NamespacedName{Namespace: "default", Name: "ingress-annotator"} - client := fakeclient.NewClient(nil, tc.objects...) - store, err := rulesstore.New(client, nn) - assert.NoError(t, err) - reconciler := &ConfigMapReconciler{ - Client: client, - Scheme: fakeclient.NewScheme(), - RulesStore: store, - } - err = reconciler.SetupWithManager(fakeclient.NewManager()) + reconciler := &ConfigMapReconciler{} + err := reconciler.SetupWithManager(tc.mgr) if tc.wantError == "" { assert.NoError(t, err) } else { @@ -73,124 +45,80 @@ func TestConfigMapReconciler_SetupWithManager(t *testing.T) { }) } } + func TestConfigMapReconciler_Reconcile(t *testing.T) { + // Helper function to create a ConfigMap + createConfigMap := func(namespace, name, data string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + ObjectMeta: ctrl.ObjectMeta{Namespace: namespace, Name: name}, + Data: map[string]string{"rule1": data}, + } + } + testCases := []struct { - name string - configNN types.NamespacedName - objects []client.Object - requestMeta types.NamespacedName - wantResult reconcile.Result - wantError string + name string + clientOpts *fakeclient.ClientOpts + cm *corev1.ConfigMap + newCM *corev1.ConfigMap + requestNN types.NamespacedName + want ctrl.Result + wantError string }{ { - name: "successful reconciliation", - configNN: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: ctrl.ObjectMeta{Namespace: "default", Name: "ingress-annotator"}, - Data: map[string]string{"rule1": "annotations:\n key: value\nnamespace: default\ningress: my-ingress"}, - }, - }, - requestMeta: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - wantResult: reconcile.Result{}, - wantError: "", + name: "client Get error should return an error", + clientOpts: &fakeclient.ClientOpts{GetError: true}, + cm: createConfigMap("default", "ingress-annotator", "annotations:\n key: value\nnamespace: default\ningress: my-ingress"), + requestNN: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, + wantError: "failed to get ConfigMap: mocked Get error", + }, + { + name: "invalid ConfigMap data should return an error", + cm: createConfigMap("default", "ingress-annotator", "annotations:\n key: value\nnamespace: default\ningress: my-ingress"), + newCM: createConfigMap("default", "ingress-annotator", "invalid data"), + requestNN: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, + wantError: "failed to update rules in rules store: invalid data in ConfigMap key rule1: failed to unmarshal YAML: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `invalid...` into model.Rule", }, { - name: "successful reconciliation for other cm", - configNN: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: ctrl.ObjectMeta{Namespace: "default", Name: "ingress-annotator"}, - Data: map[string]string{"rule1": "annotations:\n key: value\nnamespace: default\ningress: my-ingress"}, - }, - }, - requestMeta: types.NamespacedName{Namespace: "default", Name: "xxx"}, - wantResult: reconcile.Result{}, - wantError: "", + name: "valid ConfigMap should not return an error", + cm: createConfigMap("default", "ingress-annotator", "annotations:\n key: value\nnamespace: default\ningress: my-ingress"), + requestNN: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, + want: reconcile.Result{}, + }, + { + name: "ConfigMap with non-matching name should not return an error", + cm: createConfigMap("default", "ingress-annotator", "annotations:\n key: value\nnamespace: default\ningress: my-ingress"), + requestNN: types.NamespacedName{Namespace: "default", Name: "xxx"}, + want: reconcile.Result{Requeue: false, RequeueAfter: 10000000000}, }, } for i, tc := range testCases { t.Run(tester.Name(i, tc.name), func(t *testing.T) { ctx := context.Background() - nn := types.NamespacedName{Namespace: "default", Name: "ingress-annotator"} - client := fakeclient.NewClient(nil, tc.objects...) - store, err := rulesstore.New(client, nn) + + client := fakeclient.NewClient(tc.clientOpts, tc.cm) + store, err := rulesstore.New(tc.cm) assert.NoError(t, err) + reconciler := &ConfigMapReconciler{ + NN: tc.requestNN, Client: client, - Scheme: runtime.NewScheme(), - ConfigNN: tc.configNN, RulesStore: store, } - req := ctrl.Request{NamespacedName: tc.requestMeta} - result, err := reconciler.Reconcile(ctx, req) + if tc.newCM != nil { + err := client.Update(ctx, tc.newCM) + assert.NoError(t, err) + } + + got, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: tc.requestNN}) if tc.wantError == "" { assert.NoError(t, err) } else { assert.EqualError(t, err, tc.wantError) } - assert.Equal(t, tc.wantResult, result) + + assert.Equal(t, tc.want, got) }) } } - -// func TestConfigMapReconciler_updateRulesWithConfigMap(t *testing.T) { -// testCases := []struct { -// name string -// configMap *corev1.ConfigMap -// expectedError bool -// }{ -// { -// name: "valid config map", -// configMap: &corev1.ConfigMap{ -// ObjectMeta: ctrl.ObjectMeta{ -// Name: "ingress-annotator", -// Namespace: "default", -// }, -// Data: map[string]string{ -// "rule1": "annotations:\n key: value\nnamespace: default\ningress: my-ingress", -// }, -// }, -// expectedError: false, -// }, -// { -// name: "invalid config map data", -// configMap: &corev1.ConfigMap{ -// ObjectMeta: ctrl.ObjectMeta{ -// Name: "ingress-annotator", -// Namespace: "default", -// }, -// Data: map[string]string{ -// "rule1": "invalid yaml", -// }, -// }, -// expectedError: true, -// }, -// } - -// for i, tc := range testCases { -// t.Run(tester.Name(i, tc.name), func(t *testing.T) { -// client := newFakeClient(nil, tc.configMap) -// store, err := rulesstore.New() -// assert.NoError(t, err) -// reconciler := &ConfigMapReconciler{ -// Client: client, -// Scheme: runtime.NewScheme(), -// ConfigMeta: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, -// RulesStore: store, -// } - -// err = reconciler.updateRulesWithConfigMap(context.Background()) -// if tc.expectedError { -// assert.Error(t, err) -// } else { -// assert.NoError(t, err) -// rules := store.GetRules() -// assert.NotNil(t, rules) -// assert.Contains(t, *rules, "rule1") -// } -// }) -// } -// } diff --git a/controller/ingress_controller.go b/controller/ingress_controller.go index c87d5cf..500deba 100644 --- a/controller/ingress_controller.go +++ b/controller/ingress_controller.go @@ -21,16 +21,15 @@ import ( "encoding/json" "fmt" + "github.com/go-logr/logr" networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" - "github.com/go-logr/logr" - "github.com/kuoss/ingress-annotator/controller/matcher" - "github.com/kuoss/ingress-annotator/controller/model" - "github.com/kuoss/ingress-annotator/controller/rulesstore" + "github.com/kuoss/ingress-annotator/pkg/matcher" + "github.com/kuoss/ingress-annotator/pkg/model" + "github.com/kuoss/ingress-annotator/pkg/rulesstore" ) const ( @@ -47,7 +46,6 @@ type IngressContext struct { // IngressReconciler reconciles a Ingress object type IngressReconciler struct { client.Client - Scheme *runtime.Scheme RulesStore rulesstore.IRulesStore } diff --git a/controller/ingress_controller_test.go b/controller/ingress_controller_test.go index 677439e..18894d1 100644 --- a/controller/ingress_controller_test.go +++ b/controller/ingress_controller_test.go @@ -17,9 +17,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "github.com/kuoss/ingress-annotator/controller/fakeclient" - "github.com/kuoss/ingress-annotator/controller/model" - "github.com/kuoss/ingress-annotator/controller/rulesstore/mockrulesstore" + "github.com/kuoss/ingress-annotator/pkg/model" + "github.com/kuoss/ingress-annotator/pkg/testutil/fakeclient" + "github.com/kuoss/ingress-annotator/pkg/testutil/mockrulesstore" ) // TestSetupWithManager tests the SetupWithManager method of IngressReconciler. @@ -39,7 +39,6 @@ func TestSetupWithManager(t *testing.T) { client := fakeclient.NewClient(nil) reconciler := &IngressReconciler{ Client: client, - Scheme: fakeclient.NewScheme(), RulesStore: mockRulesStore, } @@ -204,7 +203,6 @@ func TestReconcile(t *testing.T) { reconciler := &IngressReconciler{ Client: client, - Scheme: fakeclient.NewScheme(), RulesStore: store, } @@ -343,7 +341,6 @@ func TestGetManagedAnnotations(t *testing.T) { r := &IngressReconciler{ Client: fakeclient.NewClient(nil, &tc.ingress), - Scheme: fakeclient.NewScheme(), } got := r.getNewManagedAnnotations(ctx) diff --git a/controller/rulesstore/rulesstore_test.go b/controller/rulesstore/rulesstore_test.go deleted file mode 100644 index 02ca71c..0000000 --- a/controller/rulesstore/rulesstore_test.go +++ /dev/null @@ -1,465 +0,0 @@ -package rulesstore - -import ( - "testing" - - "github.com/jmnote/tester" - "github.com/kuoss/ingress-annotator/controller/fakeclient" - "github.com/kuoss/ingress-annotator/controller/model" - "github.com/stretchr/testify/assert" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func TestNew(t *testing.T) { - testCases := []struct { - objects []client.Object - clientOpts *fakeclient.ClientOpts - want *RulesStore - wantError string - }{ - { - objects: nil, - wantError: "store.UpdateRules err: ConfigMap default/ingress-annotator not found", - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - }, - }, - want: &RulesStore{ - nn: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - Rules: &model.Rules{}, - }, - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - }, - }, - clientOpts: &fakeclient.ClientOpts{GetError: true}, - wantError: "store.UpdateRules err: mocked Get error", - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": "", - }, - }, - }, - want: &RulesStore{ - nn: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - Rules: &model.Rules{}, - }, - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": `annotations: - key1: value1 -namespace: test-namespace -ingress: test-ingress`, - }, - }, - }, - want: &RulesStore{ - nn: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - Rules: &model.Rules{ - "rule1": model.Rule{ - Annotations: model.Annotations{"key1": "value1"}, - Namespace: "test-namespace", - Ingress: "test-ingress", - }}, - }, - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": `annotations: - key1: value1 -namespace: test-namespace -ingress: test-ingress`, - }, - }, - }, - want: &RulesStore{ - nn: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - Rules: &model.Rules{ - "rule1": model.Rule{ - Annotations: model.Annotations{"key1": "value1"}, - Namespace: "test-namespace", - Ingress: "test-ingress", - }}, - }, - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": `invalid_data`, - }, - }, - }, - wantError: "store.UpdateRules err: invalid data in ConfigMap key rule1: failed to unmarshal YAML: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `invalid...` into model.Rule", - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": `annotations: - key1: value1 -namespace: test-namespace -ingress: test-ingress`, - }, - }, - }, - want: &RulesStore{ - nn: types.NamespacedName{Namespace: "default", Name: "ingress-annotator"}, - Rules: &model.Rules{ - "rule1": model.Rule{ - Annotations: model.Annotations{"key1": "value1"}, - Namespace: "test-namespace", - Ingress: "test-ingress", - }}, - }, - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": `invalid_data`, - }, - }, - }, - wantError: "store.UpdateRules err: invalid data in ConfigMap key rule1: failed to unmarshal YAML: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `invalid...` into model.Rule", - }, - { - objects: []client.Object{ - &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ingress-annotator", - Namespace: "default", - }, - Data: map[string]string{ - "rule1": `namespace: invalid_namespace!`, - }, - }, - }, - wantError: "store.UpdateRules err: validateRule err: invalid namespace pattern: invalid_namespace!", - }, - } - for i, tc := range testCases { - t.Run(tester.Name(i, tc), func(t *testing.T) { - nn := types.NamespacedName{Namespace: "default", Name: "ingress-annotator"} - client := fakeclient.NewClient(tc.clientOpts, tc.objects...) - got, err := New(client, nn) - if tc.wantError == "" { - assert.NoError(t, err) - tc.want.client = got.client - tc.want.rulesMutex = got.rulesMutex - assert.Equal(t, tc.want, got) - assert.Equal(t, tc.want.Rules, got.GetRules()) - } else { - assert.EqualError(t, err, tc.wantError) - assert.Equal(t, got, tc.want) - } - }) - } -} - -// func TestRulesStore(t *testing.T) { -// mockData := map[string]string{ -// "rule1": ` -// annotations: -// key1: value1 -// namespace: test-namespace -// ingress: test-ingress`, -// "rule2": ` -// annotations: -// key2: value2 -// namespace: another-namespace`, -// } -// cm := &corev1.ConfigMap{ -// Data: mockData, -// } - -// store := New() -// err := store.UpdateRules(cm) -// assert.NoError(t, err) - -// want := &model.Rules{ -// "rule1": { -// Annotations: model.Annotations{"key1": "value1"}, -// Namespace: "test-namespace", -// Ingress: "test-ingress", -// }, -// "rule2": { -// Annotations: model.Annotations{"key2": "value2"}, -// Namespace: "another-namespace", -// Ingress: "", -// }, -// } -// got := store.GetRules() -// assert.Equal(t, want, got) -// } - -// func TestGetRuleValueFromText(t *testing.T) { -// yamlText := ` -// annotations: -// key1: value1 -// namespace: test-namespace -// ingress: test-ingress` -// want := &model.Rule{ -// Annotations: model.Annotations{"key1": "value1"}, -// Namespace: "test-namespace", -// Ingress: "test-ingress", -// } - -// got, err := getRuleValueFromText(yamlText) -// assert.NoError(t, err) -// assert.Equal(t, want, got) -// } - -// func TestConcurrency(t *testing.T) { -// store := New() -// store.Rules = &model.Rules{ -// "initialRule": { -// Annotations: model.Annotations{"initialKey": "initialValue"}, -// Namespace: "initial-namespace", -// Ingress: "initial-ingress", -// }, -// } - -// var wg sync.WaitGroup - -// // Test concurrent access to GetRules and UpdateRules -// for i := 0; i < 100; i++ { -// wg.Add(1) -// go func() { -// defer wg.Done() -// store.GetRules() -// }() - -// wg.Add(1) -// go func(_ int) { -// defer wg.Done() -// mockData := map[string]string{ -// "rule": ` -// annotations: -// key: value -// namespace: namespace`, -// } -// cm := &corev1.ConfigMap{ -// Data: mockData, -// } -// _ = store.UpdateRules(cm) -// }(i) -// } - -// wg.Wait() -// } - -// func TestUpdateRules(t *testing.T) { -// testCases := []struct { -// name string -// configMapData map[string]string -// wantError string -// }{ -// { -// name: "Valid rule", -// configMapData: map[string]string{ -// "rule1": "namespace: test-namespace\ningress: test-ingress", -// }, -// wantError: "", -// }, -// { -// name: "Invalid namespace pattern", -// configMapData: map[string]string{ -// "rule1": "namespace: invalid_namespace!\ningress: test-ingress", -// }, -// wantError: "store.UpdateRules err: validateRule err: invalid namespace pattern: invalid_namespace!", -// }, -// { -// name: "Invalid ingress pattern", -// configMapData: map[string]string{ -// "rule1": "namespace: test-namespace\ningress: invalid_ingress!", -// }, -// wantError: "", -// }, -// { -// name: "Invalid YAML", -// configMapData: map[string]string{ -// "rule1": "namespace: test-namespace\ningress", -// }, -// wantError: "", -// }, -// { -// name: "Empty ConfigMap", -// configMapData: map[string]string{}, -// wantError: "", -// }, -// } - -// for i, tc := range testCases { -// t.Run(tester.Name(i, tc.name), func(t *testing.T) { -// t.Setenv("POD_NAMESPACE", "default") -// cm := &corev1.ConfigMap{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "ingress-annotator", -// Namespace: "default", -// }, -// Data: tc.configMapData, -// } -// client := testutil.NewFakeClient(nil, cm) -// store, err := New(client) -// assert.NoError(t, err) - -// err = store.UpdateRules() -// if tc.wantError == "" { -// assert.NoError(t, err) -// } else { -// assert.EqualError(t, err, tc.wantError) -// } -// }) -// } -// } - -func TestValidateRule(t *testing.T) { - testCases := []struct { - name string - rule model.Rule - wantError string - }{ - { - name: "Valid Rule with Namespace and Ingress", - rule: model.Rule{ - Namespace: "namespace-1", - Ingress: "ingress-1", - }, - wantError: "", - }, - { - name: "Valid Rule with Namespace and empty Ingress", - rule: model.Rule{ - Namespace: "namespace-1", - Ingress: "", - }, - wantError: "", - }, - { - name: "Invalid Namespace pattern", - rule: model.Rule{ - Namespace: "Invalid_Namespace", - Ingress: "ingress-1", - }, - wantError: "invalid namespace pattern: Invalid_Namespace", - }, - { - name: "Invalid Ingress pattern", - rule: model.Rule{ - Namespace: "namespace-1", - Ingress: "Invalid,Ingress", - }, - wantError: "invalid ingress pattern: Invalid,Ingress", - }, - { - name: "Valid Namespace with wildcard and negation", - rule: model.Rule{ - Namespace: "namespace-*", - Ingress: "!ingress-*", - }, - wantError: "", - }, - { - name: "Invalid pattern with special characters", - rule: model.Rule{ - Namespace: "namespace-!", - Ingress: "ingress-1", - }, - wantError: "invalid namespace pattern: namespace-!", - }, - } - - for i, tc := range testCases { - t.Run(tester.Name(i, tc.name), func(t *testing.T) { - err := validateRule(&tc.rule) - if tc.wantError == "" { - assert.NoError(t, err) - } else { - assert.EqualError(t, err, tc.wantError) - } - }) - } -} - -func TestValidate(t *testing.T) { - testCases := []struct { - pattern string - want bool - }{ - // true - {"", true}, // Empty string - {"abc", true}, // Single string - {"abc,def", true}, // Comma-separated strings - {"!abc", true}, // Pattern with an exclamation mark - {"!abc,def", true}, // Pattern with exclamation mark and comma-separated strings - {"abc-def", true}, // String with a hyphen - {"abc*", true}, // String with an asterisk - {"abc,def,ghi", true}, // Multiple comma-separated strings - {"!abc,def-ghi", true}, // Pattern with exclamation mark and hyphen - {"abc,def-ghi,jkl*", true}, // Combination of comma, hyphen, and asterisk - // false - {"abc,", false}, // Invalid pattern ending with a comma - {"abc,def!", false}, // Invalid pattern with an exclamation mark in the wrong place - {"!abc,", false}, // Invalid pattern ending with a comma after exclamation mark - {"abc,def*", true}, // Pattern with an asterisk - {"!abc,def*", true}, // Pattern with exclamation mark and asterisk - {"abc@def", false}, // Invalid pattern with an incorrect special character - {"abc,def,", false}, // Invalid comma placement - {"!abc,def,ghi!", false}, // Exclamation mark in the wrong place - } - - for i, tc := range testCases { - t.Run(tester.Name(i, tc.pattern), func(t *testing.T) { - got := validate(tc.pattern) - assert.Equal(t, tc.want, got) - }) - } -} diff --git a/controller/matcher/matcher.go b/pkg/matcher/matcher.go similarity index 100% rename from controller/matcher/matcher.go rename to pkg/matcher/matcher.go diff --git a/controller/matcher/matcher_test.go b/pkg/matcher/matcher_test.go similarity index 100% rename from controller/matcher/matcher_test.go rename to pkg/matcher/matcher_test.go diff --git a/controller/model/model.go b/pkg/model/model.go similarity index 100% rename from controller/model/model.go rename to pkg/model/model.go diff --git a/controller/rulesstore/rulesstore.go b/pkg/rulesstore/rulesstore.go similarity index 72% rename from controller/rulesstore/rulesstore.go rename to pkg/rulesstore/rulesstore.go index fa4cd99..b3fa8d3 100644 --- a/controller/rulesstore/rulesstore.go +++ b/pkg/rulesstore/rulesstore.go @@ -1,7 +1,7 @@ package rulesstore import ( - "context" + "errors" "fmt" "regexp" "strings" @@ -9,32 +9,25 @@ import ( "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/kuoss/ingress-annotator/controller/model" + "github.com/kuoss/ingress-annotator/pkg/model" ) type IRulesStore interface { GetRules() *model.Rules - UpdateRules() error + UpdateRules(cm *corev1.ConfigMap) error } type RulesStore struct { - client client.Client - nn types.NamespacedName Rules *model.Rules rulesMutex *sync.Mutex } -func New(c client.Client, nn types.NamespacedName) (*RulesStore, error) { +func New(cm *corev1.ConfigMap) (*RulesStore, error) { store := &RulesStore{ - client: c, - nn: nn, rulesMutex: &sync.Mutex{}, } - if err := store.UpdateRules(); err != nil { + if err := store.UpdateRules(cm); err != nil { return nil, fmt.Errorf("store.UpdateRules err: %w", err) } return store, nil @@ -47,14 +40,9 @@ func (s *RulesStore) GetRules() *model.Rules { return s.Rules } -func (s *RulesStore) UpdateRules() error { - cm := &corev1.ConfigMap{} - err := s.client.Get(context.Background(), s.nn, cm) - if err != nil { - if errors.IsNotFound(err) { - return fmt.Errorf("ConfigMap %s not found", s.nn) - } - return err +func (s *RulesStore) UpdateRules(cm *corev1.ConfigMap) error { + if cm == nil { + return errors.New("configMap is nil") } newRules := model.Rules{} for key, text := range cm.Data { diff --git a/pkg/rulesstore/rulesstore_test.go b/pkg/rulesstore/rulesstore_test.go new file mode 100644 index 0000000..070a555 --- /dev/null +++ b/pkg/rulesstore/rulesstore_test.go @@ -0,0 +1,209 @@ +package rulesstore + +import ( + "testing" + + "github.com/jmnote/tester" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + + "github.com/kuoss/ingress-annotator/pkg/model" +) + +func TestNew(t *testing.T) { + testCases := []struct { + cm *corev1.ConfigMap + want *RulesStore + wantError string + }{ + { + cm: nil, + wantError: "store.UpdateRules err: configMap is nil", + }, + { + cm: &corev1.ConfigMap{}, + want: &RulesStore{ + Rules: &model.Rules{}, + }, + }, + { + cm: &corev1.ConfigMap{ + Data: map[string]string{ + "rule1": "", + }, + }, + want: &RulesStore{ + Rules: &model.Rules{}, + }, + }, + { + cm: &corev1.ConfigMap{ + Data: map[string]string{ + "rule1": `annotations: + key1: value1 +namespace: test-namespace +ingress: test-ingress`, + }, + }, + want: &RulesStore{ + Rules: &model.Rules{ + "rule1": model.Rule{ + Annotations: model.Annotations{"key1": "value1"}, + Namespace: "test-namespace", + Ingress: "test-ingress", + }}, + }, + }, + { + cm: &corev1.ConfigMap{ + Data: map[string]string{ + "rule1": `annotations: + key1: value1 +namespace: test-namespace +ingress: test-ingress`, + }, + }, + want: &RulesStore{ + Rules: &model.Rules{ + "rule1": model.Rule{ + Annotations: model.Annotations{"key1": "value1"}, + Namespace: "test-namespace", + Ingress: "test-ingress", + }}, + }, + }, + { + cm: &corev1.ConfigMap{ + Data: map[string]string{ + "rule1": `invalid_data`, + }, + }, + wantError: "store.UpdateRules err: invalid data in ConfigMap key rule1: failed to unmarshal YAML: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `invalid...` into model.Rule", + }, + { + cm: &corev1.ConfigMap{ + Data: map[string]string{ + "rule1": `namespace: invalid_namespace!`, + }, + }, + wantError: "store.UpdateRules err: validateRule err: invalid namespace pattern: invalid_namespace!", + }, + } + for i, tc := range testCases { + t.Run(tester.Name(i, tc), func(t *testing.T) { + got, err := New(tc.cm) + if tc.wantError == "" { + assert.NoError(t, err) + tc.want.rulesMutex = got.rulesMutex + assert.Equal(t, tc.want, got) + assert.Equal(t, tc.want.Rules, got.GetRules()) + } else { + assert.EqualError(t, err, tc.wantError) + assert.Equal(t, got, tc.want) + } + }) + } +} + +func TestValidateRule(t *testing.T) { + testCases := []struct { + name string + rule model.Rule + wantError string + }{ + { + name: "Valid Rule with Namespace and Ingress", + rule: model.Rule{ + Namespace: "namespace-1", + Ingress: "ingress-1", + }, + wantError: "", + }, + { + name: "Valid Rule with Namespace and empty Ingress", + rule: model.Rule{ + Namespace: "namespace-1", + Ingress: "", + }, + wantError: "", + }, + { + name: "Invalid Namespace pattern", + rule: model.Rule{ + Namespace: "Invalid_Namespace", + Ingress: "ingress-1", + }, + wantError: "invalid namespace pattern: Invalid_Namespace", + }, + { + name: "Invalid Ingress pattern", + rule: model.Rule{ + Namespace: "namespace-1", + Ingress: "Invalid,Ingress", + }, + wantError: "invalid ingress pattern: Invalid,Ingress", + }, + { + name: "Valid Namespace with wildcard and negation", + rule: model.Rule{ + Namespace: "namespace-*", + Ingress: "!ingress-*", + }, + wantError: "", + }, + { + name: "Invalid pattern with special characters", + rule: model.Rule{ + Namespace: "namespace-!", + Ingress: "ingress-1", + }, + wantError: "invalid namespace pattern: namespace-!", + }, + } + + for i, tc := range testCases { + t.Run(tester.Name(i, tc.name), func(t *testing.T) { + err := validateRule(&tc.rule) + if tc.wantError == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.wantError) + } + }) + } +} + +func TestValidate(t *testing.T) { + testCases := []struct { + pattern string + want bool + }{ + // true + {"", true}, // Empty string + {"abc", true}, // Single string + {"abc,def", true}, // Comma-separated strings + {"!abc", true}, // Pattern with an exclamation mark + {"!abc,def", true}, // Pattern with exclamation mark and comma-separated strings + {"abc-def", true}, // String with a hyphen + {"abc*", true}, // String with an asterisk + {"abc,def,ghi", true}, // Multiple comma-separated strings + {"!abc,def-ghi", true}, // Pattern with exclamation mark and hyphen + {"abc,def-ghi,jkl*", true}, // Combination of comma, hyphen, and asterisk + // false + {"abc,", false}, // Invalid pattern ending with a comma + {"abc,def!", false}, // Invalid pattern with an exclamation mark in the wrong place + {"!abc,", false}, // Invalid pattern ending with a comma after exclamation mark + {"abc,def*", true}, // Pattern with an asterisk + {"!abc,def*", true}, // Pattern with exclamation mark and asterisk + {"abc@def", false}, // Invalid pattern with an incorrect special character + {"abc,def,", false}, // Invalid comma placement + {"!abc,def,ghi!", false}, // Exclamation mark in the wrong place + } + + for i, tc := range testCases { + t.Run(tester.Name(i, tc.pattern), func(t *testing.T) { + got := validate(tc.pattern) + assert.Equal(t, tc.want, got) + }) + } +} diff --git a/controller/fakeclient/fakeclient.go b/pkg/testutil/fakeclient/fakeclient.go similarity index 70% rename from controller/fakeclient/fakeclient.go rename to pkg/testutil/fakeclient/fakeclient.go index c0c73db..5e21996 100644 --- a/controller/fakeclient/fakeclient.go +++ b/pkg/testutil/fakeclient/fakeclient.go @@ -18,8 +18,12 @@ import ( func NewScheme() *runtime.Scheme { scheme := runtime.NewScheme() - _ = corev1.AddToScheme(scheme) - _ = networkingv1.AddToScheme(scheme) + if err := corev1.AddToScheme(scheme); err != nil { + panic(err) // test unreachable + } + if err := networkingv1.AddToScheme(scheme); err != nil { + panic(err) // test unreachable + } return scheme } @@ -35,12 +39,23 @@ func NewClient(opts *ClientOpts, objs ...client.Object) client.Client { interceptorFuncs := interceptor.Funcs{} if opts.GetError { - interceptorFuncs.Get = func(ctx context.Context, client client.WithWatch, key types.NamespacedName, obj client.Object, opts ...client.GetOption) error { + interceptorFuncs.Get = func( + ctx context.Context, + client client.WithWatch, + key types.NamespacedName, + obj client.Object, + opts ...client.GetOption, + ) error { return errors.New("mocked Get error") } } if opts.UpdateError { - interceptorFuncs.Update = func(ctx context.Context, client client.WithWatch, obj client.Object, opts ...client.UpdateOption) error { + interceptorFuncs.Update = func( + ctx context.Context, + client client.WithWatch, + obj client.Object, + opts ...client.UpdateOption, + ) error { return errors.New("mocked Update error") } } diff --git a/controller/fakeclient/fakeclient_test.go b/pkg/testutil/fakeclient/fakeclient_test.go similarity index 100% rename from controller/fakeclient/fakeclient_test.go rename to pkg/testutil/fakeclient/fakeclient_test.go diff --git a/pkg/testutil/fakereconciler/fakereconciler.go b/pkg/testutil/fakereconciler/fakereconciler.go new file mode 100644 index 0000000..ec6d96e --- /dev/null +++ b/pkg/testutil/fakereconciler/fakereconciler.go @@ -0,0 +1,14 @@ +package fakereconciler + +import ( + "context" + + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +type FakeReconciler struct { +} + +func (r *FakeReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) { + return reconcile.Result{}, nil +} diff --git a/controller/rulesstore/mockrulesstore/mockrulesstore.go b/pkg/testutil/mockrulesstore/mockrulesstore.go similarity index 66% rename from controller/rulesstore/mockrulesstore/mockrulesstore.go rename to pkg/testutil/mockrulesstore/mockrulesstore.go index 4e4abc1..a2275b8 100644 --- a/controller/rulesstore/mockrulesstore/mockrulesstore.go +++ b/pkg/testutil/mockrulesstore/mockrulesstore.go @@ -2,9 +2,10 @@ package mockrulesstore import ( "github.com/stretchr/testify/mock" + corev1 "k8s.io/api/core/v1" - "github.com/kuoss/ingress-annotator/controller/model" - "github.com/kuoss/ingress-annotator/controller/rulesstore" + "github.com/kuoss/ingress-annotator/pkg/model" + "github.com/kuoss/ingress-annotator/pkg/rulesstore" ) type RulesStore struct { @@ -17,7 +18,7 @@ func (m *RulesStore) GetRules() *model.Rules { return args.Get(0).(*model.Rules) } -func (m *RulesStore) UpdateRules() error { +func (m *RulesStore) UpdateRules(cm *corev1.ConfigMap) error { args := m.Called() if args.Get(0) == nil { return nil diff --git a/controller/rulesstore/mockrulesstore/mockrulesstore_test.go b/pkg/testutil/mockrulesstore/mockrulesstore_test.go similarity index 83% rename from controller/rulesstore/mockrulesstore/mockrulesstore_test.go rename to pkg/testutil/mockrulesstore/mockrulesstore_test.go index c8c3f3b..b9b7b9b 100644 --- a/controller/rulesstore/mockrulesstore/mockrulesstore_test.go +++ b/pkg/testutil/mockrulesstore/mockrulesstore_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/kuoss/ingress-annotator/controller/model" - "github.com/kuoss/ingress-annotator/controller/rulesstore/mockrulesstore" + "github.com/kuoss/ingress-annotator/pkg/model" + "github.com/kuoss/ingress-annotator/pkg/testutil/mockrulesstore" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -23,7 +23,7 @@ func TestUpdateRulesSuccess(t *testing.T) { mockStore := new(mockrulesstore.RulesStore) mockStore.On("UpdateRules").Return(nil) - err := mockStore.UpdateRules() + err := mockStore.UpdateRules(nil) assert.NoError(t, err) } @@ -32,7 +32,7 @@ func TestUpdateRulesError(t *testing.T) { expectedError := errors.New("update failed") mockStore.On("UpdateRules").Return(expectedError) - err := mockStore.UpdateRules() + err := mockStore.UpdateRules(nil) assert.EqualError(t, err, "update failed") mockStore.AssertExpectations(t)