Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions pkg/controller/configmap_watcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package controller

import (
"context"
"fmt"
"strings"
"time"

apicorev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/cache"
)

type ConfigMapWatcher struct {
clientset kubernetes.Interface
ch chan struct{} // TODO - TBD configmap struct
}

var configMapName = "deployment-validation-operator-config"
var configMapNamespace = "deployment-validation-operator"

// NewConfigMapWatcher returns a watcher that can be used both:
// basic: with GetStaticDisabledChecks method, it returns an existent ConfigMap data's disabled check
// dynamic: with StartInformer it sets an Informer that will be triggered on ConfigMap update
func NewConfigMapWatcher(cfg *rest.Config) (ConfigMapWatcher, error) {
clientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return ConfigMapWatcher{}, fmt.Errorf("initializing clientset: %w", err)
}

return ConfigMapWatcher{
clientset: clientset,
}, nil
}

// GetStaticDisabledChecks returns an existent ConfigMap data's disabled checks, if they exist
func (cmw *ConfigMapWatcher) GetStaticDisabledChecks(ctx context.Context) ([]string, error) {
cm, err := cmw.clientset.CoreV1().
ConfigMaps(configMapNamespace).Get(ctx, configMapName, v1.GetOptions{})
if err != nil {
return []string{}, fmt.Errorf("gathering starting configmap: %w", err)
}

// TODO - Fix dummy return based on data being check1,check2,check3...
return strings.Split(cm.Data["disabled-checks"], ","), nil
}

// StartInformer will update the channel structure with new configuration data from ConfigMap update event
func (cmw *ConfigMapWatcher) StartInformer(ctx context.Context) error {
factory := informers.NewSharedInformerFactoryWithOptions(
cmw.clientset, time.Second*30, informers.WithNamespace(configMapNamespace),
)
informer := factory.Core().V1().ConfigMaps().Informer()

informer.AddEventHandler(cache.ResourceEventHandlerFuncs{ // nolint:errcheck
UpdateFunc: func(oldObj, newObj interface{}) {
oldCm := oldObj.(*apicorev1.ConfigMap)
newCm := newObj.(*apicorev1.ConfigMap)

fmt.Printf("oldCm: %v\n", oldCm)
fmt.Printf("ConfigMap updated: %s/%s\n", newCm.Namespace, newCm.Name)

// TODO - Validate new configmap
cmw.ch <- struct{}{}
},
})

factory.Start(ctx.Done())

return nil
}

// ConfigChanged receives push notifications when the configuration is updated
func (cmw *ConfigMapWatcher) ConfigChanged() <-chan struct{} {
return cmw.ch
}
31 changes: 31 additions & 0 deletions pkg/controller/configmap_watcher_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package controller

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
kubefake "k8s.io/client-go/kubernetes/fake"
)

func TestStaticConfigMapWatcher(t *testing.T) {
// Given
cm := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{Namespace: configMapNamespace, Name: configMapName},
Data: map[string]string{"disabled-checks": "check,check2"},
}
client := kubefake.NewSimpleClientset([]runtime.Object{cm}...)
mock := ConfigMapWatcher{
clientset: client,
}

// When
test, err := mock.GetStaticDisabledChecks(context.Background())

// Assert
assert.NoError(t, err)
assert.Equal(t, []string{"check", "check2"}, test)
}