From d527ae8d5a8bc3b284867e485b564dfe3a5f91e8 Mon Sep 17 00:00:00 2001 From: Jesse Stephens Date: Wed, 20 May 2026 14:19:13 -0500 Subject: [PATCH 1/2] fix: align resources-to-ignore=configMaps with renamed ResourceMap key PR #1061 renamed the configmap key in kube.ResourceMap from "configMaps" (camelCase) to "configmaps" (lowercase) to fix controllers not being able to mark themselves as initialized. However, two callers were not updated to match the new canonical key: 1. The Helm chart's deployment template still emits `--resources-to-ignore=configMaps` (camelCase) when `reloader.ignoreConfigMaps: true` is set. 2. The validation in `GetIgnoredResourcesList` only accepts the legacy camelCase spelling. Because `ignoredResourcesList.Contains(k)` uses case-sensitive string equality, the lookup against the new lowercase ResourceMap key never matches. The configmaps controller is created and starts watching ConfigMaps cluster-wide, even though the chart's ClusterRole template (also gated on `ignoreConfigMaps`) does not grant permission for it. The resulting pod logs are full of: configmaps is forbidden: User "system:serviceaccount:reloader:reloader-reloader" cannot list resource "configmaps" in API group "" at the cluster scope This change: - Updates the chart deployment template to emit the canonical lowercase `configmaps` value. - Normalizes the input in `GetIgnoredResourcesList`, accepting both `configMaps` (legacy, for backward compatibility with users who pass the flag directly) and `configmaps` (canonical), and emitting the canonical form to the caller. - Updates the flag help text and adds tests covering both spellings. --- .../chart/reloader/templates/deployment.yaml | 2 +- internal/pkg/util/util.go | 21 +++-- internal/pkg/util/util_test.go | 79 +++++++++++++++++++ 3 files changed, 95 insertions(+), 7 deletions(-) diff --git a/deployments/kubernetes/chart/reloader/templates/deployment.yaml b/deployments/kubernetes/chart/reloader/templates/deployment.yaml index e568f9fd0..632501b42 100644 --- a/deployments/kubernetes/chart/reloader/templates/deployment.yaml +++ b/deployments/kubernetes/chart/reloader/templates/deployment.yaml @@ -222,7 +222,7 @@ spec: - "--resources-to-ignore=secrets" {{- end }} {{- if .Values.reloader.ignoreConfigMaps }} - - "--resources-to-ignore=configMaps" + - "--resources-to-ignore=configmaps" {{- end }} {{- if and (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs) }} - "--ignored-workload-types=jobs,cronjobs" diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index 476cdb91a..8e057baa8 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -93,7 +93,7 @@ func ConfigureReloaderFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringVar(&options.LogFormat, "log-format", "", "Log format to use (empty string for text, or JSON)") 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.ResourcesToIgnore, "resources-to-ignore", options.ResourcesToIgnore, "list of resources to ignore (valid options 'configmaps' or 'secrets'; 'configMaps' is also accepted for backward compatibility)") 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") @@ -113,17 +113,26 @@ func GetIgnoredResourcesList() (List, error) { ignoredResourcesList := options.ResourcesToIgnore // getStringSliceFromFlags(cmd, "resources-to-ignore") + // Normalize to the canonical lowercase keys used in kube.ResourceMap. + // Accept the legacy "configMaps" spelling for backward compatibility with + // charts that still emit the camelCase form. + normalized := make(List, 0, len(ignoredResourcesList)) for _, v := range ignoredResourcesList { - if v != "configMaps" && v != "secrets" { - return nil, fmt.Errorf("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not '%s'", v) + switch v { + case "configMaps", "configmaps": + normalized = append(normalized, "configmaps") + case "secrets": + normalized = append(normalized, "secrets") + default: + return nil, fmt.Errorf("'resources-to-ignore' only accepts 'configmaps' or 'secrets', not '%s'", v) } } - if len(ignoredResourcesList) > 1 { - return nil, errors.New("'resources-to-ignore' only accepts 'configMaps' or 'secrets', not both") + if len(normalized) > 1 { + return nil, errors.New("'resources-to-ignore' only accepts 'configmaps' or 'secrets', not both") } - return ignoredResourcesList, nil + return normalized, nil } func GetIgnoredWorkloadTypesList() (List, error) { diff --git a/internal/pkg/util/util_test.go b/internal/pkg/util/util_test.go index 338f329f3..7399e4b3c 100644 --- a/internal/pkg/util/util_test.go +++ b/internal/pkg/util/util_test.go @@ -136,6 +136,85 @@ func TestGetIgnoredWorkloadTypesList(t *testing.T) { } } +func TestGetIgnoredResourcesList(t *testing.T) { + // Save original state + originalResources := options.ResourcesToIgnore + defer func() { + options.ResourcesToIgnore = originalResources + }() + + tests := []struct { + name string + resources []string + expectError bool + expected []string + }{ + { + name: "Lowercase configmaps (canonical) normalizes to configmaps", + resources: []string{"configmaps"}, + expectError: false, + expected: []string{"configmaps"}, + }, + { + name: "Legacy camelCase configMaps normalizes to configmaps", + resources: []string{"configMaps"}, + expectError: false, + expected: []string{"configmaps"}, + }, + { + name: "secrets", + resources: []string{"secrets"}, + expectError: false, + expected: []string{"secrets"}, + }, + { + name: "Empty list", + resources: []string{}, + expectError: false, + expected: []string{}, + }, + { + name: "Invalid resource", + resources: []string{"deployments"}, + expectError: true, + expected: nil, + }, + { + name: "Both configmaps and secrets rejected", + resources: []string{"configmaps", "secrets"}, + expectError: true, + expected: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + options.ResourcesToIgnore = tt.resources + result, err := GetIgnoredResourcesList() + + 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 result[i] != expected { + t.Errorf("Expected %v, got %v", tt.expected, result) + break + } + } + } + }) + } +} + func TestListContains(t *testing.T) { tests := []struct { name string From 8f94e3051eb2d108e5cc852a64bd90443c8d79d9 Mon Sep 17 00:00:00 2001 From: Jesse Stephens Date: Fri, 12 Jun 2026 08:29:56 -0500 Subject: [PATCH 2/2] refactor: case-insensitive normalization for resources-to-ignore Use strings.ToLower so any casing (configMaps, ConfigMaps, sEcrets) normalizes to the canonical lowercase ResourceMap key, and simplify the flag help text. Addresses PR review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) --- internal/pkg/util/util.go | 12 ++++++------ internal/pkg/util/util_test.go | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/internal/pkg/util/util.go b/internal/pkg/util/util.go index 8e057baa8..e6c3eff2c 100644 --- a/internal/pkg/util/util.go +++ b/internal/pkg/util/util.go @@ -93,7 +93,7 @@ func ConfigureReloaderFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringVar(&options.LogFormat, "log-format", "", "Log format to use (empty string for text, or JSON)") 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'; 'configMaps' is also accepted for backward compatibility)") + 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") @@ -113,13 +113,13 @@ func GetIgnoredResourcesList() (List, error) { ignoredResourcesList := options.ResourcesToIgnore // getStringSliceFromFlags(cmd, "resources-to-ignore") - // Normalize to the canonical lowercase keys used in kube.ResourceMap. - // Accept the legacy "configMaps" spelling for backward compatibility with - // charts that still emit the camelCase form. + // Normalize to the canonical lowercase keys used in kube.ResourceMap so the + // comparison is case-insensitive (e.g. "configMaps", "ConfigMaps", "sEcrets" + // all map to their canonical lowercase form). normalized := make(List, 0, len(ignoredResourcesList)) for _, v := range ignoredResourcesList { - switch v { - case "configMaps", "configmaps": + switch strings.ToLower(v) { + case "configmaps": normalized = append(normalized, "configmaps") case "secrets": normalized = append(normalized, "secrets") diff --git a/internal/pkg/util/util_test.go b/internal/pkg/util/util_test.go index 7399e4b3c..31c82c416 100644 --- a/internal/pkg/util/util_test.go +++ b/internal/pkg/util/util_test.go @@ -161,12 +161,24 @@ func TestGetIgnoredResourcesList(t *testing.T) { expectError: false, expected: []string{"configmaps"}, }, + { + name: "Mixed-case ConfigMaps normalizes to configmaps", + resources: []string{"ConfigMaps"}, + expectError: false, + expected: []string{"configmaps"}, + }, { name: "secrets", resources: []string{"secrets"}, expectError: false, expected: []string{"secrets"}, }, + { + name: "Mixed-case sEcrets normalizes to secrets", + resources: []string{"sEcrets"}, + expectError: false, + expected: []string{"secrets"}, + }, { name: "Empty list", resources: []string{},