diff --git a/go.mod b/go.mod index 4c8e4169..a6d752df 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,23 @@ go 1.26.3 require ( github.com/argoproj/argo-rollouts v1.9.0 + github.com/go-logr/logr v1.4.3 + github.com/go-logr/zerologr v1.2.3 github.com/onsi/ginkgo/v2 v2.27.4 github.com/onsi/gomega v1.39.0 github.com/openshift/api v0.0.0-20260402111718-ad9eb11110b6 github.com/openshift/client-go v0.0.0-20260330134249-7e1499aaacd7 - github.com/parnurzeal/gorequest v0.3.0 github.com/prometheus/client_golang v1.23.2 - github.com/sirupsen/logrus v1.9.4 + github.com/prometheus/client_model v0.6.2 + github.com/rs/zerolog v1.35.1 github.com/spf13/cobra v1.10.2 - github.com/stretchr/testify v1.11.1 - k8s.io/api v0.35.3 - k8s.io/apimachinery v0.35.3 - k8s.io/client-go v0.35.3 - k8s.io/kubectl v0.35.3 + github.com/spf13/pflag v1.0.10 + github.com/spf13/viper v1.12.0 + k8s.io/api v0.36.0 + k8s.io/apimachinery v0.36.0 + k8s.io/client-go v0.36.0 k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 + sigs.k8s.io/controller-runtime v0.24.1 sigs.k8s.io/secrets-store-csi-driver v1.5.5 ) @@ -52,6 +55,7 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.3 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect github.com/bombsimon/wsl/v4 v4.7.0 // indirect github.com/bombsimon/wsl/v5 v5.3.0 // indirect @@ -75,18 +79,18 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect - github.com/elazarl/goproxy v0.0.0-20240726154733-8b0c20506380 // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/ettle/strcase v0.2.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/firefart/nonamedreturns v1.0.6 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/ghostiam/protogetter v0.3.18 // indirect github.com/go-critic/go-critic v0.14.3 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-errors/errors v1.4.2 // indirect github.com/go-openapi/jsonpointer v0.22.5 // indirect github.com/go-openapi/jsonreference v0.21.5 // indirect github.com/go-openapi/swag v0.25.5 // indirect @@ -174,23 +178,20 @@ require ( github.com/mgechev/revive v1.13.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/spdystream v0.5.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/moricho/tparallel v0.3.2 // indirect - github.com/moul/http2curl v1.0.0 // indirect github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect github.com/nunnatsa/ginkgolinter v0.21.2 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/quasilyte/go-ruleguard v0.4.5 // indirect @@ -208,18 +209,18 @@ require ( github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.29.0 // indirect github.com/securego/gosec/v2 v2.22.11 // indirect + github.com/sergi/go-diff v1.2.0 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect github.com/sivchari/containedctx v1.0.3 // indirect - github.com/smartystreets/goconvey v1.7.2 // indirect github.com/sonatard/noctx v0.4.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.10 // indirect - github.com/spf13/viper v1.12.0 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect github.com/stbenjam/no-sprintf-host-port v0.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect github.com/subosito/gotenv v1.4.1 // indirect github.com/tetafro/godot v1.5.4 // indirect github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect @@ -232,6 +233,7 @@ require ( github.com/uudashr/iface v1.4.1 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xen0n/gosmopolitan v1.3.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect @@ -243,7 +245,7 @@ require ( go.augendre.info/fatcontext v0.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect @@ -257,18 +259,25 @@ require ( golang.org/x/text v0.37.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.44.0 // indirect - google.golang.org/protobuf v1.36.11 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.6.1 // indirect - k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/apiextensions-apiserver v0.36.0 // indirect + k8s.io/klog/v2 v2.140.0 // indirect k8s.io/kube-openapi v0.0.0-20260330154417-16be699c7b31 // indirect + k8s.io/streaming v0.36.0 // indirect mvdan.cc/gofumpt v0.9.2 // indirect mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/kustomize/api v0.20.1 // indirect + sigs.k8s.io/kustomize/cmd/config v0.20.1 // indirect + sigs.k8s.io/kustomize/kustomize/v5 v5.7.1 // indirect + sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect sigs.k8s.io/yaml v1.6.0 // indirect diff --git a/internal/pkg/handler/create_test.go b/internal/pkg/handler/create_test.go deleted file mode 100644 index ef21f06b..00000000 --- a/internal/pkg/handler/create_test.go +++ /dev/null @@ -1,353 +0,0 @@ -package handler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/metrics" -) - -func TestResourceCreatedHandler_GetConfig(t *testing.T) { - tests := []struct { - name string - resource interface{} - expectedName string - expectedNS string - expectedType string - expectSHANotEmpty bool - expectOldSHAEmpty bool - }{ - { - name: "ConfigMap with data", - resource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-configmap", - Namespace: "test-ns", - }, - Data: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - expectedName: "my-configmap", - expectedNS: "test-ns", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "ConfigMap with empty data", - resource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "empty-configmap", - Namespace: "default", - }, - Data: map[string]string{}, - }, - expectedName: "empty-configmap", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "ConfigMap with binary data", - resource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "binary-configmap", - Namespace: "default", - }, - BinaryData: map[string][]byte{ - "binary-key": []byte("binary-value"), - }, - }, - expectedName: "binary-configmap", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "ConfigMap with annotations", - resource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "annotated-configmap", - Namespace: "default", - Annotations: map[string]string{ - "reloader.stakater.com/match": "true", - }, - }, - Data: map[string]string{"key": "value"}, - }, - expectedName: "annotated-configmap", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "Secret with data", - resource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", - Namespace: "secret-ns", - }, - Data: map[string][]byte{ - "password": []byte("secret-password"), - }, - }, - expectedName: "my-secret", - expectedNS: "secret-ns", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "Secret with empty data", - resource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "empty-secret", - Namespace: "default", - }, - Data: map[string][]byte{}, - }, - expectedName: "empty-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "Secret with StringData", - resource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "stringdata-secret", - Namespace: "default", - }, - StringData: map[string]string{ - "username": "admin", - }, - }, - expectedName: "stringdata-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "Secret with labels", - resource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "labeled-secret", - Namespace: "default", - Labels: map[string]string{ - "app": "test", - }, - }, - Data: map[string][]byte{"key": []byte("value")}, - }, - expectedName: "labeled-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectOldSHAEmpty: true, - }, - { - name: "Invalid resource type - string", - resource: "invalid-string", - expectedName: "", - expectedNS: "", - expectedType: "", - expectSHANotEmpty: false, - expectOldSHAEmpty: true, - }, - { - name: "Invalid resource type - int", - resource: 123, - expectedName: "", - expectedNS: "", - expectedType: "", - expectSHANotEmpty: false, - expectOldSHAEmpty: true, - }, - { - name: "Invalid resource type - struct", - resource: struct{ Name string }{Name: "test"}, - expectedName: "", - expectedNS: "", - expectedType: "", - expectSHANotEmpty: false, - expectOldSHAEmpty: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := ResourceCreatedHandler{ - Resource: tt.resource, - Collectors: metrics.NewCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, tt.expectedName, config.ResourceName) - assert.Equal(t, tt.expectedNS, config.Namespace) - assert.Equal(t, tt.expectedType, config.Type) - - if tt.expectSHANotEmpty { - assert.NotEmpty(t, config.SHAValue, "SHA should not be empty") - } - - if tt.expectOldSHAEmpty { - assert.Empty(t, oldSHA, "oldSHA should always be empty for create handler") - } - }) - } -} - -func TestResourceCreatedHandler_GetConfig_Annotations(t *testing.T) { - cm := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "annotated-cm", - Namespace: "default", - Annotations: map[string]string{ - "reloader.stakater.com/match": "true", - "reloader.stakater.com/search": "true", - }, - }, - Data: map[string]string{"key": "value"}, - } - - handler := ResourceCreatedHandler{ - Resource: cm, - Collectors: metrics.NewCollectors(), - } - - config, _ := handler.GetConfig() - - assert.NotNil(t, config.ResourceAnnotations) - assert.Equal(t, "true", config.ResourceAnnotations["reloader.stakater.com/match"]) - assert.Equal(t, "true", config.ResourceAnnotations["reloader.stakater.com/search"]) -} - -func TestResourceCreatedHandler_GetConfig_Labels(t *testing.T) { - secret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "labeled-secret", - Namespace: "default", - Labels: map[string]string{ - "app": "myapp", - "version": "v1", - }, - }, - Data: map[string][]byte{"key": []byte("value")}, - } - - handler := ResourceCreatedHandler{ - Resource: secret, - Collectors: metrics.NewCollectors(), - } - - config, _ := handler.GetConfig() - - assert.NotNil(t, config.Labels) - assert.Equal(t, "myapp", config.Labels["app"]) - assert.Equal(t, "v1", config.Labels["version"]) -} - -func TestResourceCreatedHandler_Handle(t *testing.T) { - tests := []struct { - name string - resource interface{} - expectError bool - }{ - { - name: "Nil resource", - resource: nil, - expectError: false, - }, - { - name: "Valid ConfigMap - no workloads to update", - resource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-cm", - Namespace: "default", - }, - Data: map[string]string{"key": "value"}, - }, - expectError: false, - }, - { - name: "Valid Secret - no workloads to update", - resource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret", - Namespace: "default", - }, - Data: map[string][]byte{"key": []byte("value")}, - }, - expectError: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := ResourceCreatedHandler{ - Resource: tt.resource, - Collectors: metrics.NewCollectors(), - } - - err := handler.Handle() - - if tt.expectError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestResourceCreatedHandler_SHAConsistency(t *testing.T) { - data := map[string]string{"key": "value"} - - cm1 := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm1", Namespace: "default"}, - Data: data, - } - cm2 := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm2", Namespace: "default"}, - Data: data, - } - - handler1 := ResourceCreatedHandler{Resource: cm1, Collectors: metrics.NewCollectors()} - handler2 := ResourceCreatedHandler{Resource: cm2, Collectors: metrics.NewCollectors()} - - config1, _ := handler1.GetConfig() - config2, _ := handler2.GetConfig() - - assert.Equal(t, config1.SHAValue, config2.SHAValue) -} - -func TestResourceCreatedHandler_SHADifference(t *testing.T) { - cm1 := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "value1"}, - } - cm2 := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "value2"}, - } - - handler1 := ResourceCreatedHandler{Resource: cm1, Collectors: metrics.NewCollectors()} - handler2 := ResourceCreatedHandler{Resource: cm2, Collectors: metrics.NewCollectors()} - - config1, _ := handler1.GetConfig() - config2, _ := handler2.GetConfig() - - assert.NotEqual(t, config1.SHAValue, config2.SHAValue) -} diff --git a/internal/pkg/handler/delete_test.go b/internal/pkg/handler/delete_test.go deleted file mode 100644 index 812b0d18..00000000 --- a/internal/pkg/handler/delete_test.go +++ /dev/null @@ -1,353 +0,0 @@ -package handler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - appsv1 "k8s.io/api/apps/v1" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/stakater/Reloader/internal/pkg/callbacks" - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/options" - "github.com/stakater/Reloader/pkg/common" -) - -// mockDeploymentForDelete creates a deployment with containers for testing delete strategies -func mockDeploymentForDelete(name, namespace string, containers []v1.Container, volumes []v1.Volume) *appsv1.Deployment { - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: appsv1.DeploymentSpec{ - Template: v1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{}, - }, - Spec: v1.PodSpec{ - Containers: containers, - Volumes: volumes, - }, - }, - }, - } -} - -// Mock funcs for testing -func mockContainersFunc(item runtime.Object) []v1.Container { - deployment, ok := item.(*appsv1.Deployment) - if !ok { - return nil - } - return deployment.Spec.Template.Spec.Containers -} - -func mockInitContainersFunc(item runtime.Object) []v1.Container { - deployment, ok := item.(*appsv1.Deployment) - if !ok { - return nil - } - return deployment.Spec.Template.Spec.InitContainers -} - -func mockVolumesFunc(item runtime.Object) []v1.Volume { - deployment, ok := item.(*appsv1.Deployment) - if !ok { - return nil - } - return deployment.Spec.Template.Spec.Volumes -} - -func mockPodAnnotationsFunc(item runtime.Object) map[string]string { - deployment, ok := item.(*appsv1.Deployment) - if !ok { - return nil - } - return deployment.Spec.Template.Annotations -} - -func mockPatchTemplatesFunc() callbacks.PatchTemplates { - return callbacks.PatchTemplates{ - AnnotationTemplate: `{"spec":{"template":{"metadata":{"annotations":{"%s":"%s"}}}}}`, - EnvVarTemplate: `{"spec":{"template":{"spec":{"containers":[{"name":"%s","env":[{"name":"%s","value":"%s"}]}]}}}}`, - DeleteEnvVarTemplate: `[{"op":"remove","path":"/spec/template/spec/containers/%d/env/%d"}]`, - } -} - -func TestRemoveContainerEnvVars(t *testing.T) { - tests := []struct { - name string - containers []v1.Container - volumes []v1.Volume - config common.Config - autoReload bool - expected constants.Result - envVarRemoved bool - }{ - { - name: "Remove existing env var - configmap envFrom", - containers: []v1.Container{ - { - Name: "app", - EnvFrom: []v1.EnvFromSource{ - { - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "my-configmap", - }, - }, - }, - }, - Env: []v1.EnvVar{ - {Name: "STAKATER_MY_CONFIGMAP_CONFIGMAP", Value: "sha-value"}, - }, - }, - }, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-configmap", - Type: constants.ConfigmapEnvVarPostfix, - }, - autoReload: true, - expected: constants.Updated, - envVarRemoved: true, - }, - { - name: "No env var to remove", - containers: []v1.Container{ - { - Name: "app", - EnvFrom: []v1.EnvFromSource{ - { - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "my-configmap", - }, - }, - }, - }, - Env: []v1.EnvVar{}, - }, - }, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-configmap", - Type: constants.ConfigmapEnvVarPostfix, - }, - autoReload: true, - expected: constants.NotUpdated, - envVarRemoved: false, - }, - { - name: "Remove existing env var - secret envFrom", - containers: []v1.Container{ - { - Name: "app", - EnvFrom: []v1.EnvFromSource{ - { - SecretRef: &v1.SecretEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "my-secret", - }, - }, - }, - }, - Env: []v1.EnvVar{ - {Name: "STAKATER_MY_SECRET_SECRET", Value: "sha-value"}, - }, - }, - }, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-secret", - Type: constants.SecretEnvVarPostfix, - }, - autoReload: true, - expected: constants.Updated, - envVarRemoved: true, - }, - { - name: "No container found", - containers: []v1.Container{}, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-configmap", - Type: constants.ConfigmapEnvVarPostfix, - }, - autoReload: true, - expected: constants.NoContainerFound, - envVarRemoved: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - deployment := mockDeploymentForDelete("test-deploy", "default", tt.containers, tt.volumes) - - funcs := callbacks.RollingUpgradeFuncs{ - ContainersFunc: mockContainersFunc, - InitContainersFunc: mockInitContainersFunc, - VolumesFunc: mockVolumesFunc, - PodAnnotationsFunc: mockPodAnnotationsFunc, - PatchTemplatesFunc: mockPatchTemplatesFunc, - SupportsPatch: true, - } - - result := removeContainerEnvVars(funcs, deployment, tt.config, tt.autoReload) - - assert.Equal(t, tt.expected, result.Result) - - if tt.envVarRemoved { - containers := deployment.Spec.Template.Spec.Containers - for _, c := range containers { - for _, env := range c.Env { - envVarName := getEnvVarName(tt.config.ResourceName, tt.config.Type) - assert.NotEqual(t, envVarName, env.Name, "Env var should have been removed") - } - } - } - }) - } -} - -func TestInvokeDeleteStrategy(t *testing.T) { - originalStrategy := options.ReloadStrategy - defer func() { - options.ReloadStrategy = originalStrategy - }() - - tests := []struct { - name string - reloadStrategy string - containers []v1.Container - volumes []v1.Volume - config common.Config - }{ - { - name: "Annotations strategy", - reloadStrategy: constants.AnnotationsReloadStrategy, - containers: []v1.Container{ - { - Name: "app", - EnvFrom: []v1.EnvFromSource{ - { - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "my-configmap", - }, - }, - }, - }, - }, - }, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-configmap", - Type: constants.ConfigmapEnvVarPostfix, - SHAValue: "sha-value", - }, - }, - { - name: "EnvVars strategy", - reloadStrategy: constants.EnvVarsReloadStrategy, - containers: []v1.Container{ - { - Name: "app", - EnvFrom: []v1.EnvFromSource{ - { - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "my-configmap", - }, - }, - }, - }, - Env: []v1.EnvVar{ - {Name: "STAKATER_MY_CONFIGMAP_CONFIGMAP", Value: "sha-value"}, - }, - }, - }, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-configmap", - Type: constants.ConfigmapEnvVarPostfix, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - options.ReloadStrategy = tt.reloadStrategy - - deployment := mockDeploymentForDelete("test-deploy", "default", tt.containers, tt.volumes) - - funcs := callbacks.RollingUpgradeFuncs{ - ContainersFunc: mockContainersFunc, - InitContainersFunc: mockInitContainersFunc, - VolumesFunc: mockVolumesFunc, - PodAnnotationsFunc: mockPodAnnotationsFunc, - PatchTemplatesFunc: mockPatchTemplatesFunc, - SupportsPatch: true, - } - - result := invokeDeleteStrategy(funcs, deployment, tt.config, true) - - assert.NotNil(t, result) - }) - } -} - -func TestRemovePodAnnotations(t *testing.T) { - tests := []struct { - name string - containers []v1.Container - volumes []v1.Volume - config common.Config - }{ - { - name: "Remove pod annotations - configmap", - containers: []v1.Container{ - { - Name: "app", - EnvFrom: []v1.EnvFromSource{ - { - ConfigMapRef: &v1.ConfigMapEnvSource{ - LocalObjectReference: v1.LocalObjectReference{ - Name: "my-configmap", - }, - }, - }, - }, - }, - }, - volumes: []v1.Volume{}, - config: common.Config{ - ResourceName: "my-configmap", - Type: constants.ConfigmapEnvVarPostfix, - SHAValue: "sha-value", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - deployment := mockDeploymentForDelete("test-deploy", "default", tt.containers, tt.volumes) - - funcs := callbacks.RollingUpgradeFuncs{ - ContainersFunc: mockContainersFunc, - InitContainersFunc: mockInitContainersFunc, - VolumesFunc: mockVolumesFunc, - PodAnnotationsFunc: mockPodAnnotationsFunc, - PatchTemplatesFunc: mockPatchTemplatesFunc, - SupportsPatch: false, - } - - result := removePodAnnotations(funcs, deployment, tt.config, true) - - assert.Equal(t, constants.Updated, result.Result) - }) - } -} diff --git a/internal/pkg/handler/handlers_test.go b/internal/pkg/handler/handlers_test.go deleted file mode 100644 index dedefcc9..00000000 --- a/internal/pkg/handler/handlers_test.go +++ /dev/null @@ -1,281 +0,0 @@ -package handler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/metrics" -) - -// Helper function to create a test ConfigMap -func createTestConfigMap(data map[string]string) *v1.ConfigMap { - return &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-cm", - Namespace: "default", - }, - Data: data, - } -} - -// Helper function to create a test Secret -func createTestSecret(data map[string][]byte) *v1.Secret { - return &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-secret", - Namespace: "default", - }, - Data: data, - } -} - -// Helper function to create test metrics collectors -func createTestCollectors() metrics.Collectors { - return metrics.NewCollectors() -} - -// ============================================================ -// ResourceCreatedHandler Tests -// ============================================================ - -func TestResourceCreatedHandler_GetConfig_ConfigMap(t *testing.T) { - cm := createTestConfigMap(map[string]string{"key": "value"}) - handler := ResourceCreatedHandler{ - Resource: cm, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-cm", config.ResourceName) - assert.Equal(t, "default", config.Namespace) - assert.Equal(t, constants.ConfigmapEnvVarPostfix, config.Type) - assert.NotEmpty(t, config.SHAValue) - assert.Empty(t, oldSHA) -} - -func TestResourceCreatedHandler_GetConfig_Secret(t *testing.T) { - secret := createTestSecret(map[string][]byte{"key": []byte("value")}) - handler := ResourceCreatedHandler{ - Resource: secret, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-secret", config.ResourceName) - assert.Equal(t, "default", config.Namespace) - assert.Equal(t, constants.SecretEnvVarPostfix, config.Type) - assert.NotEmpty(t, config.SHAValue) - assert.Empty(t, oldSHA) -} - -func TestResourceCreatedHandler_GetConfig_InvalidResource(t *testing.T) { - handler := ResourceCreatedHandler{ - Resource: "invalid", - Collectors: createTestCollectors(), - } - - config, _ := handler.GetConfig() - - assert.Empty(t, config.ResourceName) -} - -func TestResourceCreatedHandler_Handle_NilResource(t *testing.T) { - handler := ResourceCreatedHandler{ - Resource: nil, - Collectors: createTestCollectors(), - } - - err := handler.Handle() - - assert.NoError(t, err) -} - -// ============================================================ -// ResourceDeleteHandler Tests -// ============================================================ - -func TestResourceDeleteHandler_GetConfig_ConfigMap(t *testing.T) { - cm := createTestConfigMap(map[string]string{"key": "value"}) - handler := ResourceDeleteHandler{ - Resource: cm, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-cm", config.ResourceName) - assert.Equal(t, "default", config.Namespace) - assert.Equal(t, constants.ConfigmapEnvVarPostfix, config.Type) - assert.NotEmpty(t, config.SHAValue) - assert.Empty(t, oldSHA) -} - -func TestResourceDeleteHandler_GetConfig_Secret(t *testing.T) { - secret := createTestSecret(map[string][]byte{"key": []byte("value")}) - handler := ResourceDeleteHandler{ - Resource: secret, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-secret", config.ResourceName) - assert.Equal(t, "default", config.Namespace) - assert.Equal(t, constants.SecretEnvVarPostfix, config.Type) - assert.NotEmpty(t, config.SHAValue) - assert.Empty(t, oldSHA) -} - -func TestResourceDeleteHandler_GetConfig_InvalidResource(t *testing.T) { - handler := ResourceDeleteHandler{ - Resource: "invalid", - Collectors: createTestCollectors(), - } - - config, _ := handler.GetConfig() - - assert.Empty(t, config.ResourceName) -} - -func TestResourceDeleteHandler_Handle_NilResource(t *testing.T) { - handler := ResourceDeleteHandler{ - Resource: nil, - Collectors: createTestCollectors(), - } - - err := handler.Handle() - - assert.NoError(t, err) -} - -// ============================================================ -// ResourceUpdatedHandler Tests -// ============================================================ - -func TestResourceUpdatedHandler_GetConfig_ConfigMap(t *testing.T) { - oldCM := createTestConfigMap(map[string]string{"key": "old-value"}) - newCM := createTestConfigMap(map[string]string{"key": "new-value"}) - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-cm", config.ResourceName) - assert.Equal(t, "default", config.Namespace) - assert.Equal(t, constants.ConfigmapEnvVarPostfix, config.Type) - assert.NotEmpty(t, config.SHAValue) - assert.NotEmpty(t, oldSHA) - assert.NotEqual(t, config.SHAValue, oldSHA) -} - -func TestResourceUpdatedHandler_GetConfig_ConfigMap_SameData(t *testing.T) { - oldCM := createTestConfigMap(map[string]string{"key": "same-value"}) - newCM := createTestConfigMap(map[string]string{"key": "same-value"}) - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-cm", config.ResourceName) - assert.Equal(t, config.SHAValue, oldSHA) -} - -func TestResourceUpdatedHandler_GetConfig_Secret(t *testing.T) { - oldSecret := createTestSecret(map[string][]byte{"key": []byte("old-value")}) - newSecret := createTestSecret(map[string][]byte{"key": []byte("new-value")}) - - handler := ResourceUpdatedHandler{ - Resource: newSecret, - OldResource: oldSecret, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-secret", config.ResourceName) - assert.Equal(t, "default", config.Namespace) - assert.Equal(t, constants.SecretEnvVarPostfix, config.Type) - assert.NotEmpty(t, config.SHAValue) - assert.NotEmpty(t, oldSHA) - assert.NotEqual(t, config.SHAValue, oldSHA) -} - -func TestResourceUpdatedHandler_GetConfig_Secret_SameData(t *testing.T) { - oldSecret := createTestSecret(map[string][]byte{"key": []byte("same-value")}) - newSecret := createTestSecret(map[string][]byte{"key": []byte("same-value")}) - - handler := ResourceUpdatedHandler{ - Resource: newSecret, - OldResource: oldSecret, - Collectors: createTestCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, "test-secret", config.ResourceName) - assert.Equal(t, config.SHAValue, oldSHA) -} - -func TestResourceUpdatedHandler_GetConfig_InvalidResource(t *testing.T) { - handler := ResourceUpdatedHandler{ - Resource: "invalid", - OldResource: "invalid", - Collectors: createTestCollectors(), - } - - config, _ := handler.GetConfig() - - assert.Empty(t, config.ResourceName) -} - -func TestResourceUpdatedHandler_Handle_NilResource(t *testing.T) { - handler := ResourceUpdatedHandler{ - Resource: nil, - OldResource: nil, - Collectors: createTestCollectors(), - } - - err := handler.Handle() - - assert.NoError(t, err) -} - -func TestResourceUpdatedHandler_Handle_NilOldResource(t *testing.T) { - cm := createTestConfigMap(map[string]string{"key": "value"}) - handler := ResourceUpdatedHandler{ - Resource: cm, - OldResource: nil, - Collectors: createTestCollectors(), - } - - err := handler.Handle() - - assert.NoError(t, err) -} - -func TestResourceUpdatedHandler_Handle_NoChange(t *testing.T) { - cm := createTestConfigMap(map[string]string{"key": "same-value"}) - handler := ResourceUpdatedHandler{ - Resource: cm, - OldResource: cm, - Collectors: createTestCollectors(), - } - - err := handler.Handle() - - assert.NoError(t, err) -} diff --git a/internal/pkg/handler/update_test.go b/internal/pkg/handler/update_test.go deleted file mode 100644 index 1ae10d41..00000000 --- a/internal/pkg/handler/update_test.go +++ /dev/null @@ -1,520 +0,0 @@ -package handler - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/stakater/Reloader/internal/pkg/constants" - "github.com/stakater/Reloader/internal/pkg/metrics" -) - -func TestResourceUpdatedHandler_GetConfig(t *testing.T) { - tests := []struct { - name string - oldResource any - newResource any - expectedName string - expectedNS string - expectedType string - expectSHANotEmpty bool - expectSHAChanged bool - }{ - { - name: "ConfigMap data changed", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key": "old-value"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key": "new-value"}, - }, - expectedName: "my-cm", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: true, - }, - { - name: "ConfigMap data unchanged", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key": "same-value"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key": "same-value"}, - }, - expectedName: "my-cm", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: false, - }, - { - name: "ConfigMap key added", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key1": "value1"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key1": "value1", "key2": "value2"}, - }, - expectedName: "my-cm", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: true, - }, - { - name: "ConfigMap key removed", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key1": "value1", "key2": "value2"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "my-cm", Namespace: "default"}, - Data: map[string]string{"key1": "value1"}, - }, - expectedName: "my-cm", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: true, - }, - { - name: "ConfigMap only labels changed - SHA unchanged", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cm", - Namespace: "default", - Labels: map[string]string{"version": "v1"}, - }, - Data: map[string]string{"key": "value"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cm", - Namespace: "default", - Labels: map[string]string{"version": "v2"}, - }, - Data: map[string]string{"key": "value"}, - }, - expectedName: "my-cm", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: false, - }, - { - name: "ConfigMap only annotations changed - SHA unchanged", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cm", - Namespace: "default", - Annotations: map[string]string{"note": "old"}, - }, - Data: map[string]string{"key": "value"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-cm", - Namespace: "default", - Annotations: map[string]string{"note": "new"}, - }, - Data: map[string]string{"key": "value"}, - }, - expectedName: "my-cm", - expectedNS: "default", - expectedType: constants.ConfigmapEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: false, - }, - { - name: "Secret data changed", - oldResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "my-secret", Namespace: "default"}, - Data: map[string][]byte{"password": []byte("old-pass")}, - }, - newResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "my-secret", Namespace: "default"}, - Data: map[string][]byte{"password": []byte("new-pass")}, - }, - expectedName: "my-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: true, - }, - { - name: "Secret data unchanged", - oldResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "my-secret", Namespace: "default"}, - Data: map[string][]byte{"password": []byte("same-pass")}, - }, - newResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "my-secret", Namespace: "default"}, - Data: map[string][]byte{"password": []byte("same-pass")}, - }, - expectedName: "my-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: false, - }, - { - name: "Secret key added", - oldResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "my-secret", Namespace: "default"}, - Data: map[string][]byte{"key1": []byte("value1")}, - }, - newResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "my-secret", Namespace: "default"}, - Data: map[string][]byte{"key1": []byte("value1"), "key2": []byte("value2")}, - }, - expectedName: "my-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: true, - }, - { - name: "Secret only labels changed - SHA unchanged", - oldResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", - Namespace: "default", - Labels: map[string]string{"env": "dev"}, - }, - Data: map[string][]byte{"key": []byte("value")}, - }, - newResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-secret", - Namespace: "default", - Labels: map[string]string{"env": "prod"}, - }, - Data: map[string][]byte{"key": []byte("value")}, - }, - expectedName: "my-secret", - expectedNS: "default", - expectedType: constants.SecretEnvVarPostfix, - expectSHANotEmpty: true, - expectSHAChanged: false, - }, - { - name: "Invalid resource type", - oldResource: "invalid", - newResource: "invalid", - expectedName: "", - expectedNS: "", - expectedType: "", - expectSHANotEmpty: false, - expectSHAChanged: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := ResourceUpdatedHandler{ - Resource: tt.newResource, - OldResource: tt.oldResource, - Collectors: metrics.NewCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.Equal(t, tt.expectedName, config.ResourceName) - assert.Equal(t, tt.expectedNS, config.Namespace) - assert.Equal(t, tt.expectedType, config.Type) - - if tt.expectSHANotEmpty { - assert.NotEmpty(t, config.SHAValue, "new SHA should not be empty") - assert.NotEmpty(t, oldSHA, "old SHA should not be empty") - } - - if tt.expectSHAChanged { - assert.NotEqual(t, config.SHAValue, oldSHA, "SHA should have changed") - } else if tt.expectSHANotEmpty { - assert.Equal(t, config.SHAValue, oldSHA, "SHA should not have changed") - } - }) - } -} - -func TestResourceUpdatedHandler_Handle(t *testing.T) { - tests := []struct { - name string - oldResource any - newResource any - expectError bool - }{ - { - name: "Both resources nil", - oldResource: nil, - newResource: nil, - expectError: false, - }, - { - name: "Old resource nil", - oldResource: nil, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "value"}, - }, - expectError: false, - }, - { - name: "New resource nil", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "value"}, - }, - newResource: nil, - expectError: false, - }, - { - name: "ConfigMap unchanged - no action", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "same"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "same"}, - }, - expectError: false, - }, - { - name: "ConfigMap changed - triggers update", - oldResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "old"}, - }, - newResource: &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "new"}, - }, - expectError: false, - }, - { - name: "Secret unchanged - no action", - oldResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret", Namespace: "default"}, - Data: map[string][]byte{"key": []byte("same")}, - }, - newResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret", Namespace: "default"}, - Data: map[string][]byte{"key": []byte("same")}, - }, - expectError: false, - }, - { - name: "Secret changed - triggers update", - oldResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret", Namespace: "default"}, - Data: map[string][]byte{"key": []byte("old")}, - }, - newResource: &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "secret", Namespace: "default"}, - Data: map[string][]byte{"key": []byte("new")}, - }, - expectError: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - handler := ResourceUpdatedHandler{ - Resource: tt.newResource, - OldResource: tt.oldResource, - Collectors: metrics.NewCollectors(), - } - - err := handler.Handle() - - if tt.expectError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestResourceUpdatedHandler_GetConfig_Annotations(t *testing.T) { - oldCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cm", - Namespace: "default", - Annotations: map[string]string{ - "old-annotation": "old-value", - }, - }, - Data: map[string]string{"key": "value"}, - } - - newCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cm", - Namespace: "default", - Annotations: map[string]string{ - "new-annotation": "new-value", - }, - }, - Data: map[string]string{"key": "value"}, - } - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: metrics.NewCollectors(), - } - - config, _ := handler.GetConfig() - - assert.Equal(t, "new-value", config.ResourceAnnotations["new-annotation"]) - _, hasOld := config.ResourceAnnotations["old-annotation"] - assert.False(t, hasOld) -} - -func TestResourceUpdatedHandler_GetConfig_Labels(t *testing.T) { - oldSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret", - Namespace: "default", - Labels: map[string]string{"version": "v1"}, - }, - Data: map[string][]byte{"key": []byte("value")}, - } - - newSecret := &v1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "secret", - Namespace: "default", - Labels: map[string]string{"version": "v2"}, - }, - Data: map[string][]byte{"key": []byte("value")}, - } - - handler := ResourceUpdatedHandler{ - Resource: newSecret, - OldResource: oldSecret, - Collectors: metrics.NewCollectors(), - } - - config, _ := handler.GetConfig() - - assert.Equal(t, "v2", config.Labels["version"]) -} - -func TestResourceUpdatedHandler_EmptyToNonEmpty(t *testing.T) { - oldCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{}, - } - newCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "value"}, - } - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: metrics.NewCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.NotEqual(t, config.SHAValue, oldSHA, "SHA should change when data is added") -} - -func TestResourceUpdatedHandler_NonEmptyToEmpty(t *testing.T) { - oldCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"key": "value"}, - } - newCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{}, - } - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: metrics.NewCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.NotEqual(t, config.SHAValue, oldSHA, "SHA should change when data is removed") -} - -func TestResourceUpdatedHandler_BinaryDataChange(t *testing.T) { - oldCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - BinaryData: map[string][]byte{"binary": []byte("old-binary")}, - } - newCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - BinaryData: map[string][]byte{"binary": []byte("new-binary")}, - } - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: metrics.NewCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.NotEqual(t, config.SHAValue, oldSHA, "SHA should change when binary data changes") -} - -func TestResourceUpdatedHandler_MixedDataAndBinaryData(t *testing.T) { - oldCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"text": "value"}, - BinaryData: map[string][]byte{"binary": []byte("binary-value")}, - } - newCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "default"}, - Data: map[string]string{"text": "value"}, - BinaryData: map[string][]byte{"binary": []byte("new-binary-value")}, - } - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: metrics.NewCollectors(), - } - - config, oldSHA := handler.GetConfig() - - assert.NotEqual(t, config.SHAValue, oldSHA, "SHA should change when binary data changes") -} - -func TestResourceUpdatedHandler_DifferentNamespaces(t *testing.T) { - oldCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "ns1"}, - Data: map[string]string{"key": "value"}, - } - newCM := &v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{Name: "cm", Namespace: "ns2"}, - Data: map[string]string{"key": "value"}, - } - - handler := ResourceUpdatedHandler{ - Resource: newCM, - OldResource: oldCM, - Collectors: metrics.NewCollectors(), - } - - config, _ := handler.GetConfig() - - assert.Equal(t, "ns2", config.Namespace) -}