From 60d2b9b52393b4b9064756d8419232e66e13f8fa Mon Sep 17 00:00:00 2001 From: Pradeep Lakshmi Narasimha Date: Mon, 18 Aug 2025 18:29:47 +0530 Subject: [PATCH] fix: Controller not respecting ignore* flags --- CODE_OF_CONDUCT.md | 2 +- README.md | 23 +- .../kubernetes/chart/reloader/README.md | 10 +- .../chart/reloader/templates/deployment.yaml | 11 +- .../chart/reloader/tests/deployment_test.yaml | 41 ++++ .../kubernetes/chart/reloader/values.yaml | 6 +- internal/pkg/handler/upgrade_test.go | 2 +- internal/pkg/options/flags.go | 2 + internal/pkg/util/util.go | 14 ++ internal/pkg/util/util_test.go | 139 +++++++++++ pkg/common/common.go | 30 +++ pkg/common/common_test.go | 224 ++++++++++++++++++ 12 files changed, 495 insertions(+), 9 deletions(-) create mode 100644 pkg/common/common_test.go diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 219830feb..e5649af1f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,3 +1,3 @@ # Code of Conduct -Reloader follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). +Reloader follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). diff --git a/README.md b/README.md index 3e887e4a0..1c9a887b3 100644 --- a/README.md +++ b/README.md @@ -329,13 +329,30 @@ Reloader supports multiple strategies for triggering rolling updates when a watc |------|-------------| | `--resources-to-ignore=configmaps` | Ignore ConfigMaps (only one type can be ignored at a time) | | `--resources-to-ignore=secrets` | Ignore Secrets (cannot combine with configMaps) | +| `--ignored-workload-types=jobs,cronjobs` | Ignore specific workload types from reload monitoring | | `--resource-label-selector=key=value` | Only watch ConfigMaps/Secrets with matching labels | -> **⚠️ Note:** -> Only **one** resource type can be ignored at a time. -> Trying to ignore **both `configmaps` and `secrets`** will cause an error in Reloader. +> **⚠️ Note:** +> +> Only **one** resource type can be ignored at a time. +> Trying to ignore **both `configmaps` and `secrets`** will cause an error in Reloader. > ✅ **Workaround:** Scale the Reloader deployment to `0` replicas if you want to disable it completely. +**💡 Workload Type Examples:** + +```bash +# Ignore only Jobs +--ignored-workload-types=jobs + +# Ignore only CronJobs +--ignored-workload-types=cronjobs + +# Ignore both (comma-separated) +--ignored-workload-types=jobs,cronjobs +``` + +> **🔧 Use Case:** Ignoring workload types is useful when you don't want certain types of workloads to be automatically reloaded. + #### 3. 🧩 Namespace Filtering | Flag | Description | diff --git a/deployments/kubernetes/chart/reloader/README.md b/deployments/kubernetes/chart/reloader/README.md index 057a4ec72..a37e74ac4 100644 --- a/deployments/kubernetes/chart/reloader/README.md +++ b/deployments/kubernetes/chart/reloader/README.md @@ -14,6 +14,8 @@ helm install stakater/reloader # For helm3 add --generate-name flag or set the r helm install {{RELEASE_NAME}} stakater/reloader -n {{NAMESPACE}} --set reloader.watchGlobally=false # By default, Reloader watches in all namespaces. To watch in single namespace, set watchGlobally=false helm install stakater/reloader --set reloader.watchGlobally=false --namespace test --generate-name # Install Reloader in `test` namespace which will only watch `Deployments`, `Daemonsets` `Statefulsets` and `Rollouts` in `test` namespace. + +helm install stakater/reloader --set reloader.ignoreJobs=true --set reloader.ignoreCronJobs=true --generate-name # Install Reloader ignoring Jobs and CronJobs from reload monitoring ``` ## Uninstalling @@ -47,6 +49,8 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}} | `reloader.isOpenshift` | Enable OpenShift DeploymentConfigs. Valid value are either `true` or `false` | boolean | `false` | | `reloader.ignoreSecrets` | To ignore secrets. Valid value are either `true` or `false`. Either `ignoreSecrets` or `ignoreConfigMaps` can be ignored, not both at the same time | boolean | `false` | | `reloader.ignoreConfigMaps` | To ignore configmaps. Valid value are either `true` or `false` | boolean | `false` | +| `reloader.ignoreJobs` | To ignore jobs from reload monitoring. Valid value are either `true` or `false`. Translates to `--ignored-workload-types=jobs` | boolean | `false` | +| `reloader.ignoreCronJobs` | To ignore CronJobs from reload monitoring. Valid value are either `true` or `false`. Translates to `--ignored-workload-types=cronjobs` | boolean | `false` | | `reloader.reloadOnCreate` | Enable reload on create events. Valid value are either `true` or `false` | boolean | `false` | | `reloader.reloadOnDelete` | Enable reload on delete events. Valid value are either `true` or `false` | boolean | `false` | | `reloader.syncAfterRestart` | Enable sync after Reloader restarts for **Add** events, works only when reloadOnCreate is `true`. Valid value are either `true` or `false` | boolean | `false` | @@ -58,7 +62,7 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}} | `reloader.watchGlobally` | Allow Reloader to watch in all namespaces (`true`) or just in a single namespace (`false`) | boolean | `true` | | `reloader.enableHA` | Enable leadership election allowing you to run multiple replicas | boolean | `false` | | `reloader.enablePProf` | Enables pprof for profiling | boolean | `false` | -| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` | +| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` | | `reloader.readOnlyRootFileSystem` | Enforce readOnlyRootFilesystem | boolean | `false` | | `reloader.legacy.rbac` | | boolean | `false` | | `reloader.matchLabels` | Pod labels to match | map | `{}` | @@ -115,6 +119,10 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}} - Only one of these resources can be ignored at a time: - `ignoreConfigMaps` **or** `ignoreSecrets` - Trying to ignore both will cause Helm template compilation errors +- The `ignoreJobs` and `ignoreCronJobs` flags can be used together or individually + - When both are enabled, translates to `--ignored-workload-types=jobs,cronjobs` + - When used individually, translates to `--ignored-workload-types=jobs` or `--ignored-workload-types=cronjobs` + - These flags prevent Reloader from monitoring and reloading the specified workload types ### Special Integrations - OpenShift (`DeploymentConfig`) and Argo Rollouts support must be **explicitly enabled** diff --git a/deployments/kubernetes/chart/reloader/templates/deployment.yaml b/deployments/kubernetes/chart/reloader/templates/deployment.yaml index d282312a2..de4222e41 100644 --- a/deployments/kubernetes/chart/reloader/templates/deployment.yaml +++ b/deployments/kubernetes/chart/reloader/templates/deployment.yaml @@ -155,7 +155,7 @@ spec: - name: RELOADER_DEPLOYMENT_NAME value: {{ template "reloader-fullname" . }} - + {{- if .Values.reloader.enableHA }} - name: POD_NAME valueFrom: @@ -210,7 +210,7 @@ spec: {{- . | toYaml | nindent 10 }} {{- end }} {{- end }} - {{- if or (.Values.reloader.logFormat) (.Values.reloader.logLevel) (.Values.reloader.ignoreSecrets) (.Values.reloader.ignoreNamespaces) (include "reloader-namespaceSelector" .) (.Values.reloader.resourceLabelSelector) (.Values.reloader.ignoreConfigMaps) (.Values.reloader.custom_annotations) (eq .Values.reloader.isArgoRollouts true) (eq .Values.reloader.reloadOnCreate true) (eq .Values.reloader.reloadOnDelete true) (ne .Values.reloader.reloadStrategy "default") (.Values.reloader.enableHA) (.Values.reloader.autoReloadAll)}} + {{- if or (.Values.reloader.logFormat) (.Values.reloader.logLevel) (.Values.reloader.ignoreSecrets) (.Values.reloader.ignoreNamespaces) (include "reloader-namespaceSelector" .) (.Values.reloader.resourceLabelSelector) (.Values.reloader.ignoreConfigMaps) (.Values.reloader.custom_annotations) (eq .Values.reloader.isArgoRollouts true) (eq .Values.reloader.reloadOnCreate true) (eq .Values.reloader.reloadOnDelete true) (ne .Values.reloader.reloadStrategy "default") (.Values.reloader.enableHA) (.Values.reloader.autoReloadAll) (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs)}} args: {{- if .Values.reloader.logFormat }} - "--log-format={{ .Values.reloader.logFormat }}" @@ -224,6 +224,13 @@ spec: {{- if .Values.reloader.ignoreConfigMaps }} - "--resources-to-ignore=configMaps" {{- end }} + {{- if and (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs) }} + - "--ignored-workload-types=jobs,cronjobs" + {{- else if .Values.reloader.ignoreJobs }} + - "--ignored-workload-types=jobs" + {{- else if .Values.reloader.ignoreCronJobs }} + - "--ignored-workload-types=cronjobs" + {{- end }} {{- if .Values.reloader.ignoreNamespaces }} - "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}" {{- end }} diff --git a/deployments/kubernetes/chart/reloader/tests/deployment_test.yaml b/deployments/kubernetes/chart/reloader/tests/deployment_test.yaml index aee0f9fb1..2838fe41b 100644 --- a/deployments/kubernetes/chart/reloader/tests/deployment_test.yaml +++ b/deployments/kubernetes/chart/reloader/tests/deployment_test.yaml @@ -61,3 +61,44 @@ tests: valueFrom: fieldRef: fieldPath: metadata.name + + - it: sets ignored-workload-types argument when ignoreJobs is true + set: + reloader: + ignoreJobs: true + asserts: + - contains: + path: spec.template.spec.containers[0].args + content: "--ignored-workload-types=jobs" + + - it: sets ignored-workload-types argument when ignoreCronJobs is true + set: + reloader: + ignoreCronJobs: true + asserts: + - contains: + path: spec.template.spec.containers[0].args + content: "--ignored-workload-types=cronjobs" + + - it: sets ignored-workload-types argument when both ignoreJobs and ignoreCronJobs are true + set: + reloader: + ignoreJobs: true + ignoreCronJobs: true + asserts: + - contains: + path: spec.template.spec.containers[0].args + content: "--ignored-workload-types=jobs,cronjobs" + + - it: does not set ignored-workload-types argument when both ignoreJobs and ignoreCronJobs are false + set: + reloader: + ignoreJobs: false + ignoreCronJobs: false + asserts: + - notContains: + path: spec.template.spec.containers[0].args + content: "--ignored-workload-types=jobs" + - notContains: + path: spec.template.spec.containers[0].args + content: "--ignored-workload-types=cronjobs" diff --git a/deployments/kubernetes/chart/reloader/values.yaml b/deployments/kubernetes/chart/reloader/values.yaml index d257c31a5..b315bbb80 100644 --- a/deployments/kubernetes/chart/reloader/values.yaml +++ b/deployments/kubernetes/chart/reloader/values.yaml @@ -27,7 +27,11 @@ reloader: isOpenshift: false ignoreSecrets: false ignoreConfigMaps: false + # Set to true to exclude Job workloads from automatic reload monitoring + # Useful when you don't want Jobs to be restarted when their referenced ConfigMaps/Secrets change ignoreJobs: false + # Set to true to exclude CronJob workloads from automatic reload monitoring + # Useful when you don't want CronJobs to be restarted when their referenced ConfigMaps/Secrets change ignoreCronJobs: false reloadOnCreate: false reloadOnDelete: false @@ -84,7 +88,7 @@ reloader: # - key: "node-role.kubernetes.io/infra-worker" # operator: "Exists" affinity: {} - + volumeMounts: [] volumes: [] diff --git a/internal/pkg/handler/upgrade_test.go b/internal/pkg/handler/upgrade_test.go index 7259fba18..9a0e94587 100644 --- a/internal/pkg/handler/upgrade_test.go +++ b/internal/pkg/handler/upgrade_test.go @@ -4267,7 +4267,7 @@ func TestGetContainerUsingResourceWithArgoRolloutEmptyContainers(t *testing.T) { // Use proper Argo Rollout object instead of Pod mockRollout := MockArgoRolloutWithEmptyContainers(namespace, "test-rollout") - config := util.Config{ + config := common.Config{ Namespace: namespace, ResourceName: resourceName, Type: constants.ConfigmapEnvVarPostfix, diff --git a/internal/pkg/options/flags.go b/internal/pkg/options/flags.go index f909e0a1c..0f99be8af 100644 --- a/internal/pkg/options/flags.go +++ b/internal/pkg/options/flags.go @@ -65,6 +65,8 @@ var ( WebhookUrl = "" // ResourcesToIgnore is a list of resources to ignore when watching for changes ResourcesToIgnore = []string{} + // WorkloadTypesToIgnore is a list of workload types to ignore when watching for changes + WorkloadTypesToIgnore = []string{} // NamespacesToIgnore is a list of namespace names to ignore when watching for changes NamespacesToIgnore = []string{} // NamespaceSelectors is a list of namespace selectors to watch for changes diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index d434bc1eb..ec86d1c9c 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -83,6 +83,7 @@ func ConfigureReloaderFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringVar(&options.LogLevel, "log-level", "info", "Log level to use (trace, debug, info, warning, error, fatal and panic)") cmd.PersistentFlags().StringVar(&options.WebhookUrl, "webhook-url", "", "webhook to trigger instead of performing a reload") cmd.PersistentFlags().StringSliceVar(&options.ResourcesToIgnore, "resources-to-ignore", options.ResourcesToIgnore, "list of resources to ignore (valid options 'configMaps' or 'secrets')") + cmd.PersistentFlags().StringSliceVar(&options.WorkloadTypesToIgnore, "ignored-workload-types", options.WorkloadTypesToIgnore, "list of workload types to ignore (valid options: 'jobs', 'cronjobs', or both)") cmd.PersistentFlags().StringSliceVar(&options.NamespacesToIgnore, "namespaces-to-ignore", options.NamespacesToIgnore, "list of namespaces to ignore") cmd.PersistentFlags().StringSliceVar(&options.NamespaceSelectors, "namespace-selector", options.NamespaceSelectors, "list of key:value labels to filter on for namespaces") cmd.PersistentFlags().StringSliceVar(&options.ResourceSelectors, "resource-label-selector", options.ResourceSelectors, "list of key:value labels to filter on for configmaps and secrets") @@ -112,3 +113,16 @@ func GetIgnoredResourcesList() (List, error) { return ignoredResourcesList, nil } + +func GetIgnoredWorkloadTypesList() (List, error) { + + ignoredWorkloadTypesList := options.WorkloadTypesToIgnore + + for _, v := range ignoredWorkloadTypesList { + if v != "jobs" && v != "cronjobs" { + return nil, fmt.Errorf("'ignored-workload-types' accepts 'jobs', 'cronjobs', or both, not '%s'", v) + } + } + + return ignoredWorkloadTypesList, nil +} diff --git a/internal/pkg/util/util_test.go b/internal/pkg/util/util_test.go index 88c6bad5d..338f329f3 100644 --- a/internal/pkg/util/util_test.go +++ b/internal/pkg/util/util_test.go @@ -3,6 +3,7 @@ package util import ( "testing" + "github.com/stakater/Reloader/internal/pkg/options" v1 "k8s.io/api/core/v1" ) @@ -45,3 +46,141 @@ func TestGetHashFromConfigMap(t *testing.T) { } } } + +func TestGetIgnoredWorkloadTypesList(t *testing.T) { + // Save original state + originalWorkloadTypes := options.WorkloadTypesToIgnore + defer func() { + options.WorkloadTypesToIgnore = originalWorkloadTypes + }() + + tests := []struct { + name string + workloadTypes []string + expectError bool + expected []string + }{ + { + name: "Both jobs and cronjobs", + workloadTypes: []string{"jobs", "cronjobs"}, + expectError: false, + expected: []string{"jobs", "cronjobs"}, + }, + { + name: "Only jobs", + workloadTypes: []string{"jobs"}, + expectError: false, + expected: []string{"jobs"}, + }, + { + name: "Only cronjobs", + workloadTypes: []string{"cronjobs"}, + expectError: false, + expected: []string{"cronjobs"}, + }, + { + name: "Empty list", + workloadTypes: []string{}, + expectError: false, + expected: []string{}, + }, + { + name: "Invalid workload type", + workloadTypes: []string{"invalid"}, + expectError: true, + expected: nil, + }, + { + name: "Mixed valid and invalid", + workloadTypes: []string{"jobs", "invalid"}, + expectError: true, + expected: nil, + }, + { + name: "Duplicate values", + workloadTypes: []string{"jobs", "jobs"}, + expectError: false, + expected: []string{"jobs", "jobs"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the global option + options.WorkloadTypesToIgnore = tt.workloadTypes + + result, err := GetIgnoredWorkloadTypesList() + + if tt.expectError && err == nil { + t.Errorf("Expected error but got none") + } + + if !tt.expectError && err != nil { + t.Errorf("Expected no error but got: %v", err) + } + + if !tt.expectError { + if len(result) != len(tt.expected) { + t.Errorf("Expected %v, got %v", tt.expected, result) + return + } + + for i, expected := range tt.expected { + if i >= len(result) || result[i] != expected { + t.Errorf("Expected %v, got %v", tt.expected, result) + break + } + } + } + }) + } +} + +func TestListContains(t *testing.T) { + tests := []struct { + name string + list List + item string + expected bool + }{ + { + name: "List contains item", + list: List{"jobs", "cronjobs"}, + item: "jobs", + expected: true, + }, + { + name: "List does not contain item", + list: List{"jobs"}, + item: "cronjobs", + expected: false, + }, + { + name: "Empty list", + list: List{}, + item: "jobs", + expected: false, + }, + { + name: "Case sensitive matching", + list: List{"jobs", "cronjobs"}, + item: "Jobs", + expected: false, + }, + { + name: "Multiple occurrences", + list: List{"jobs", "jobs", "cronjobs"}, + item: "jobs", + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.list.Contains(tt.item) + if result != tt.expected { + t.Errorf("Expected %v, got %v", tt.expected, result) + } + }) + } +} diff --git a/pkg/common/common.go b/pkg/common/common.go index 715f00206..84d982748 100644 --- a/pkg/common/common.go +++ b/pkg/common/common.go @@ -10,6 +10,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stakater/Reloader/internal/pkg/constants" "github.com/stakater/Reloader/internal/pkg/options" + "github.com/stakater/Reloader/internal/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" @@ -74,6 +75,8 @@ type ReloaderOptions struct { WebhookUrl string `json:"webhookUrl"` // ResourcesToIgnore is a list of resource types to ignore (e.g., "configmaps" or "secrets") ResourcesToIgnore []string `json:"resourcesToIgnore"` + // WorkloadTypesToIgnore is a list of workload types to ignore (e.g., "jobs" or "cronjobs") + WorkloadTypesToIgnore []string `json:"workloadTypesToIgnore"` // NamespaceSelectors is a list of label selectors to filter namespaces to watch NamespaceSelectors []string `json:"namespaceSelectors"` // ResourceSelectors is a list of label selectors to filter ConfigMaps and Secrets to watch @@ -182,6 +185,32 @@ func GetResourceLabelSelector(slice []string) (string, error) { // ShouldReload checks if a resource should be reloaded based on its annotations and the provided options. func ShouldReload(config Config, resourceType string, annotations Map, podAnnotations Map, options *ReloaderOptions) ReloadCheckResult { + // Check if this workload type should be ignored + if len(options.WorkloadTypesToIgnore) > 0 { + ignoredWorkloadTypes, err := util.GetIgnoredWorkloadTypesList() + if err != nil { + logrus.Errorf("Failed to parse ignored workload types: %v", err) + } else { + // Map Kubernetes resource types to CLI-friendly names for comparison + var resourceToCheck string + switch resourceType { + case "Job": + resourceToCheck = "jobs" + case "CronJob": + resourceToCheck = "cronjobs" + default: + resourceToCheck = resourceType // For other types, use as-is + } + + // Check if current resource type should be ignored + if ignoredWorkloadTypes.Contains(resourceToCheck) { + return ReloadCheckResult{ + ShouldReload: false, + } + } + } + } + ignoreResourceAnnotatonValue := config.ResourceAnnotations[options.IgnoreResourceAnnotation] if ignoreResourceAnnotatonValue == "true" { return ReloadCheckResult{ @@ -304,6 +333,7 @@ func GetCommandLineOptions() *ReloaderOptions { CommandLineOptions.EnableHA = options.EnableHA CommandLineOptions.WebhookUrl = options.WebhookUrl CommandLineOptions.ResourcesToIgnore = options.ResourcesToIgnore + CommandLineOptions.WorkloadTypesToIgnore = options.WorkloadTypesToIgnore CommandLineOptions.NamespaceSelectors = options.NamespaceSelectors CommandLineOptions.ResourceSelectors = options.ResourceSelectors CommandLineOptions.NamespacesToIgnore = options.NamespacesToIgnore diff --git a/pkg/common/common_test.go b/pkg/common/common_test.go new file mode 100644 index 000000000..532d3adfa --- /dev/null +++ b/pkg/common/common_test.go @@ -0,0 +1,224 @@ +package common + +import ( + "testing" + + "github.com/stakater/Reloader/internal/pkg/options" +) + +func TestShouldReload_IgnoredWorkloadTypes(t *testing.T) { + // Save original state + originalWorkloadTypes := options.WorkloadTypesToIgnore + defer func() { + options.WorkloadTypesToIgnore = originalWorkloadTypes + }() + + tests := []struct { + name string + ignoredWorkloadTypes []string + resourceType string + shouldReload bool + description string + }{ + { + name: "Jobs ignored - Job should not reload", + ignoredWorkloadTypes: []string{"jobs"}, + resourceType: "Job", + shouldReload: false, + description: "When jobs are ignored, Job resources should not be reloaded", + }, + { + name: "Jobs ignored - CronJob should reload", + ignoredWorkloadTypes: []string{"jobs"}, + resourceType: "CronJob", + shouldReload: true, + description: "When jobs are ignored, CronJob resources should still be processed", + }, + { + name: "CronJobs ignored - CronJob should not reload", + ignoredWorkloadTypes: []string{"cronjobs"}, + resourceType: "CronJob", + shouldReload: false, + description: "When cronjobs are ignored, CronJob resources should not be reloaded", + }, + { + name: "CronJobs ignored - Job should reload", + ignoredWorkloadTypes: []string{"cronjobs"}, + resourceType: "Job", + shouldReload: true, + description: "When cronjobs are ignored, Job resources should still be processed", + }, + { + name: "Both ignored - Job should not reload", + ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, + resourceType: "Job", + shouldReload: false, + description: "When both are ignored, Job resources should not be reloaded", + }, + { + name: "Both ignored - CronJob should not reload", + ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, + resourceType: "CronJob", + shouldReload: false, + description: "When both are ignored, CronJob resources should not be reloaded", + }, + { + name: "Both ignored - Deployment should reload", + ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, + resourceType: "Deployment", + shouldReload: true, + description: "When both are ignored, other workload types should still be processed", + }, + { + name: "None ignored - Job should reload", + ignoredWorkloadTypes: []string{}, + resourceType: "Job", + shouldReload: true, + description: "When nothing is ignored, all workload types should be processed", + }, + { + name: "None ignored - CronJob should reload", + ignoredWorkloadTypes: []string{}, + resourceType: "CronJob", + shouldReload: true, + description: "When nothing is ignored, all workload types should be processed", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the ignored workload types + options.WorkloadTypesToIgnore = tt.ignoredWorkloadTypes + + // Create minimal test config and options + config := Config{ + ResourceName: "test-resource", + Annotation: "configmap.reloader.stakater.com/reload", + } + + annotations := Map{ + "configmap.reloader.stakater.com/reload": "test-config", + } + + // Create ReloaderOptions with the ignored workload types + opts := &ReloaderOptions{ + WorkloadTypesToIgnore: tt.ignoredWorkloadTypes, + AutoReloadAll: true, // Enable auto-reload to simplify test + ReloaderAutoAnnotation: "reloader.stakater.com/auto", + } + + // Call ShouldReload + result := ShouldReload(config, tt.resourceType, annotations, Map{}, opts) + + // Check the result + if result.ShouldReload != tt.shouldReload { + t.Errorf("For resource type %s with ignored types %v, expected ShouldReload=%v, got=%v", + tt.resourceType, tt.ignoredWorkloadTypes, tt.shouldReload, result.ShouldReload) + } + + t.Logf("✓ %s", tt.description) + }) + } +} + +func TestShouldReload_IgnoredWorkloadTypes_ValidationError(t *testing.T) { + // Save original state + originalWorkloadTypes := options.WorkloadTypesToIgnore + defer func() { + options.WorkloadTypesToIgnore = originalWorkloadTypes + }() + + // Test with invalid workload type - should still continue processing + options.WorkloadTypesToIgnore = []string{"invalid"} + + config := Config{ + ResourceName: "test-resource", + Annotation: "configmap.reloader.stakater.com/reload", + } + + annotations := Map{ + "configmap.reloader.stakater.com/reload": "test-config", + } + + opts := &ReloaderOptions{ + WorkloadTypesToIgnore: []string{"invalid"}, + AutoReloadAll: true, // Enable auto-reload to simplify test + ReloaderAutoAnnotation: "reloader.stakater.com/auto", + } + + // Should not panic and should continue with normal processing + result := ShouldReload(config, "Job", annotations, Map{}, opts) + + // Since validation failed, it should continue with normal processing (should reload) + if !result.ShouldReload { + t.Errorf("Expected ShouldReload=true when validation fails, got=%v", result.ShouldReload) + } +} + +// Test that validates the fix for issue #996 +func TestShouldReload_IssueRBACPermissionFixed(t *testing.T) { + // Save original state + originalWorkloadTypes := options.WorkloadTypesToIgnore + defer func() { + options.WorkloadTypesToIgnore = originalWorkloadTypes + }() + + tests := []struct { + name string + ignoredWorkloadTypes []string + resourceType string + description string + }{ + { + name: "Issue #996 - ignoreJobs prevents Job processing", + ignoredWorkloadTypes: []string{"jobs"}, + resourceType: "Job", + description: "Job resources are skipped entirely, preventing RBAC permission errors", + }, + { + name: "Issue #996 - ignoreCronJobs prevents CronJob processing", + ignoredWorkloadTypes: []string{"cronjobs"}, + resourceType: "CronJob", + description: "CronJob resources are skipped entirely, preventing RBAC permission errors", + }, + { + name: "Issue #996 - both ignored prevent both types", + ignoredWorkloadTypes: []string{"jobs", "cronjobs"}, + resourceType: "Job", + description: "Job resources are skipped entirely when both types are ignored", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the ignored workload types + options.WorkloadTypesToIgnore = tt.ignoredWorkloadTypes + + config := Config{ + ResourceName: "test-resource", + Annotation: "configmap.reloader.stakater.com/reload", + } + + annotations := Map{ + "configmap.reloader.stakater.com/reload": "test-config", + } + + opts := &ReloaderOptions{ + WorkloadTypesToIgnore: tt.ignoredWorkloadTypes, + AutoReloadAll: true, // Enable auto-reload to simplify test + ReloaderAutoAnnotation: "reloader.stakater.com/auto", + } + + // Call ShouldReload + result := ShouldReload(config, tt.resourceType, annotations, Map{}, opts) + + // Should not reload when workload type is ignored + if result.ShouldReload { + t.Errorf("Expected ShouldReload=false for ignored workload type %s, got=%v", + tt.resourceType, result.ShouldReload) + } + + t.Logf("✓ %s", tt.description) + }) + } +}