Skip to content

Commit

Permalink
fetchConfigMapDirectly (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
jmnote authored Aug 17, 2024
1 parent 619a17a commit 8fd3d34
Show file tree
Hide file tree
Showing 20 changed files with 427 additions and 680 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
30 changes: 22 additions & 8 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down Expand Up @@ -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)
Expand All @@ -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
}
49 changes: 45 additions & 4 deletions cmd/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
})
}
}
4 changes: 0 additions & 4 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@ rules:
resources:
- configmaps
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- ""
Expand Down
49 changes: 29 additions & 20 deletions controller/configmap_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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/[email protected]/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
}
Loading

0 comments on commit 8fd3d34

Please sign in to comment.