mirror of
https://github.com/stakater/Reloader.git
synced 2026-08-22 13:36:54 +00:00
296 lines
7.0 KiB
Go
296 lines
7.0 KiB
Go
package controller_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/go-logr/logr/testr"
|
|
"github.com/stakater/Reloader/internal/pkg/config"
|
|
"github.com/stakater/Reloader/internal/pkg/controller"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/labels"
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
ctrl "sigs.k8s.io/controller-runtime"
|
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
|
)
|
|
|
|
func TestNamespaceCache_Basic(t *testing.T) {
|
|
cache := controller.NewNamespaceCache(true)
|
|
|
|
// Test Add and Contains
|
|
cache.Add("namespace-1")
|
|
if !cache.Contains("namespace-1") {
|
|
t.Error("Cache should contain namespace-1")
|
|
}
|
|
if cache.Contains("namespace-2") {
|
|
t.Error("Cache should not contain namespace-2")
|
|
}
|
|
|
|
// Test Remove
|
|
cache.Remove("namespace-1")
|
|
if cache.Contains("namespace-1") {
|
|
t.Error("Cache should not contain namespace-1 after removal")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceCache_Disabled(t *testing.T) {
|
|
cache := controller.NewNamespaceCache(false)
|
|
|
|
// When disabled, Contains should always return true
|
|
if !cache.Contains("any-namespace") {
|
|
t.Error("Disabled cache should return true for any namespace")
|
|
}
|
|
if !cache.Contains("other-namespace") {
|
|
t.Error("Disabled cache should return true for any namespace")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceCache_List(t *testing.T) {
|
|
cache := controller.NewNamespaceCache(true)
|
|
cache.Add("ns-1")
|
|
cache.Add("ns-2")
|
|
cache.Add("ns-3")
|
|
|
|
list := cache.List()
|
|
if len(list) != 3 {
|
|
t.Errorf("Expected 3 namespaces, got %d", len(list))
|
|
}
|
|
|
|
// Check all namespaces are in the list
|
|
found := make(map[string]bool)
|
|
for _, ns := range list {
|
|
found[ns] = true
|
|
}
|
|
for _, expected := range []string{"ns-1", "ns-2", "ns-3"} {
|
|
if !found[expected] {
|
|
t.Errorf("Expected %s in list", expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNamespaceCache_IsEnabled(t *testing.T) {
|
|
enabledCache := controller.NewNamespaceCache(true)
|
|
disabledCache := controller.NewNamespaceCache(false)
|
|
|
|
if !enabledCache.IsEnabled() {
|
|
t.Error("EnabledCache.IsEnabled() should return true")
|
|
}
|
|
if disabledCache.IsEnabled() {
|
|
t.Error("DisabledCache.IsEnabled() should return false")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceReconciler_Add(t *testing.T) {
|
|
scheme := runtime.NewScheme()
|
|
_ = corev1.AddToScheme(scheme)
|
|
|
|
ns := &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-ns",
|
|
Labels: map[string]string{"env": "production"},
|
|
},
|
|
}
|
|
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithObjects(ns).
|
|
Build()
|
|
|
|
cfg := config.NewDefault()
|
|
selector, _ := labels.Parse("env=production")
|
|
cfg.NamespaceSelectors = []labels.Selector{selector}
|
|
|
|
cache := controller.NewNamespaceCache(true)
|
|
reconciler := &controller.NamespaceReconciler{
|
|
Client: fakeClient,
|
|
Log: testr.New(t),
|
|
Config: cfg,
|
|
Cache: cache,
|
|
}
|
|
|
|
req := ctrl.Request{
|
|
NamespacedName: types.NamespacedName{Name: "test-ns"},
|
|
}
|
|
|
|
_, err := reconciler.Reconcile(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Reconcile failed: %v", err)
|
|
}
|
|
|
|
if !cache.Contains("test-ns") {
|
|
t.Error("Cache should contain test-ns after reconcile")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceReconciler_Remove_LabelChange(t *testing.T) {
|
|
scheme := runtime.NewScheme()
|
|
_ = corev1.AddToScheme(scheme)
|
|
|
|
// Namespace with non-matching labels
|
|
ns := &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-ns",
|
|
Labels: map[string]string{"env": "staging"},
|
|
},
|
|
}
|
|
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithObjects(ns).
|
|
Build()
|
|
|
|
cfg := config.NewDefault()
|
|
selector, _ := labels.Parse("env=production")
|
|
cfg.NamespaceSelectors = []labels.Selector{selector}
|
|
|
|
cache := controller.NewNamespaceCache(true)
|
|
// Pre-populate cache
|
|
cache.Add("test-ns")
|
|
|
|
reconciler := &controller.NamespaceReconciler{
|
|
Client: fakeClient,
|
|
Log: testr.New(t),
|
|
Config: cfg,
|
|
Cache: cache,
|
|
}
|
|
|
|
req := ctrl.Request{
|
|
NamespacedName: types.NamespacedName{Name: "test-ns"},
|
|
}
|
|
|
|
_, err := reconciler.Reconcile(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Reconcile failed: %v", err)
|
|
}
|
|
|
|
if cache.Contains("test-ns") {
|
|
t.Error("Cache should not contain test-ns after reconcile (labels no longer match)")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceReconciler_Remove_Delete(t *testing.T) {
|
|
scheme := runtime.NewScheme()
|
|
_ = corev1.AddToScheme(scheme)
|
|
|
|
// No namespace in cluster (simulates delete)
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
Build()
|
|
|
|
cfg := config.NewDefault()
|
|
selector, _ := labels.Parse("env=production")
|
|
cfg.NamespaceSelectors = []labels.Selector{selector}
|
|
|
|
cache := controller.NewNamespaceCache(true)
|
|
// Pre-populate cache
|
|
cache.Add("deleted-ns")
|
|
|
|
reconciler := &controller.NamespaceReconciler{
|
|
Client: fakeClient,
|
|
Log: testr.New(t),
|
|
Config: cfg,
|
|
Cache: cache,
|
|
}
|
|
|
|
req := ctrl.Request{
|
|
NamespacedName: types.NamespacedName{Name: "deleted-ns"},
|
|
}
|
|
|
|
_, err := reconciler.Reconcile(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Reconcile failed: %v", err)
|
|
}
|
|
|
|
if cache.Contains("deleted-ns") {
|
|
t.Error("Cache should not contain deleted-ns after reconcile")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceReconciler_MultipleSelectors(t *testing.T) {
|
|
scheme := runtime.NewScheme()
|
|
_ = corev1.AddToScheme(scheme)
|
|
|
|
ns := &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-ns",
|
|
Labels: map[string]string{"team": "platform"},
|
|
},
|
|
}
|
|
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithObjects(ns).
|
|
Build()
|
|
|
|
cfg := config.NewDefault()
|
|
selector1, _ := labels.Parse("env=production")
|
|
selector2, _ := labels.Parse("team=platform")
|
|
cfg.NamespaceSelectors = []labels.Selector{selector1, selector2}
|
|
|
|
cache := controller.NewNamespaceCache(true)
|
|
reconciler := &controller.NamespaceReconciler{
|
|
Client: fakeClient,
|
|
Log: testr.New(t),
|
|
Config: cfg,
|
|
Cache: cache,
|
|
}
|
|
|
|
req := ctrl.Request{
|
|
NamespacedName: types.NamespacedName{Name: "test-ns"},
|
|
}
|
|
|
|
_, err := reconciler.Reconcile(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Reconcile failed: %v", err)
|
|
}
|
|
|
|
// Should be added because it matches second selector (team=platform)
|
|
if !cache.Contains("test-ns") {
|
|
t.Error("Cache should contain test-ns (matches second selector)")
|
|
}
|
|
}
|
|
|
|
func TestNamespaceReconciler_NoLabels(t *testing.T) {
|
|
scheme := runtime.NewScheme()
|
|
_ = corev1.AddToScheme(scheme)
|
|
|
|
// Namespace with no labels
|
|
ns := &corev1.Namespace{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-ns",
|
|
},
|
|
}
|
|
|
|
fakeClient := fake.NewClientBuilder().
|
|
WithScheme(scheme).
|
|
WithObjects(ns).
|
|
Build()
|
|
|
|
cfg := config.NewDefault()
|
|
selector, _ := labels.Parse("env=production")
|
|
cfg.NamespaceSelectors = []labels.Selector{selector}
|
|
|
|
cache := controller.NewNamespaceCache(true)
|
|
reconciler := &controller.NamespaceReconciler{
|
|
Client: fakeClient,
|
|
Log: testr.New(t),
|
|
Config: cfg,
|
|
Cache: cache,
|
|
}
|
|
|
|
req := ctrl.Request{
|
|
NamespacedName: types.NamespacedName{Name: "test-ns"},
|
|
}
|
|
|
|
_, err := reconciler.Reconcile(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatalf("Reconcile failed: %v", err)
|
|
}
|
|
|
|
if cache.Contains("test-ns") {
|
|
t.Error("Cache should not contain test-ns (no labels)")
|
|
}
|
|
}
|