package controller import ( "context" "testing" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client/fake" bindv1alpha1 "git.unkin.net/unkin/bind-operator/api/v1alpha1" ) func TestConfigHashStableAndSensitive(t *testing.T) { scheme := runtime.NewScheme() if err := clientgoscheme.AddToScheme(scheme); err != nil { t.Fatal(err) } if err := bindv1alpha1.AddToScheme(scheme); err != nil { t.Fatal(err) } cluster := &bindv1alpha1.BindCluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: "ns"}} cm := &corev1.ConfigMap{ ObjectMeta: metav1.ObjectMeta{Name: configMapName("c"), Namespace: "ns"}, Data: map[string]string{"named.conf.secondary": "options { recursion yes; };"}, } keys := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: keysSecretName("c"), Namespace: "ns"}, Data: map[string][]byte{"keys.conf": []byte("key x {};")}, } c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(cm, keys).Build() r := &BindClusterReconciler{Client: c, Scheme: scheme} ctx := context.Background() h1 := r.configHash(ctx, cluster) if h1 == "" { t.Fatal("hash should not be empty when config exists") } // Stable across calls when nothing changes. if h2 := r.configHash(ctx, cluster); h2 != h1 { t.Fatalf("hash not stable: %s != %s", h1, h2) } // A config change flips the hash (this is what rolls the StatefulSet). cm.Data["named.conf.secondary"] = "options { recursion yes; validate-except { unkin.net; }; };" if err := c.Update(ctx, cm); err != nil { t.Fatal(err) } if h3 := r.configHash(ctx, cluster); h3 == h1 { t.Fatal("hash must change when the ConfigMap changes") } // A TSIG key (keys.conf) change also flips it. afterCM := r.configHash(ctx, cluster) keys.Data["keys.conf"] = []byte("key x { algorithm hmac-sha256; };") if err := c.Update(ctx, keys); err != nil { t.Fatal(err) } if h4 := r.configHash(ctx, cluster); h4 == afterCM { t.Fatal("hash must change when keys.conf changes") } }