Skip to content

Commit dd0807e

Browse files
praddy26Felix-Stakater
authored andcommitted
fix: Controller not respecting ignore* flags
1 parent b8edc25 commit dd0807e

File tree

11 files changed

+494
-8
lines changed

11 files changed

+494
-8
lines changed

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Code of Conduct
22

3-
Reloader follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
3+
Reloader follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).

README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,13 +329,30 @@ Reloader supports multiple strategies for triggering rolling updates when a watc
329329
|------|-------------|
330330
| `--resources-to-ignore=configmaps` | Ignore ConfigMaps (only one type can be ignored at a time) |
331331
| `--resources-to-ignore=secrets` | Ignore Secrets (cannot combine with configMaps) |
332+
| `--ignored-workload-types=jobs,cronjobs` | Ignore specific workload types from reload monitoring |
332333
| `--resource-label-selector=key=value` | Only watch ConfigMaps/Secrets with matching labels |
333334

334-
> **⚠️ Note:**
335-
> Only **one** resource type can be ignored at a time.
336-
> Trying to ignore **both `configmaps` and `secrets`** will cause an error in Reloader.
335+
> **⚠️ Note:**
336+
>
337+
> Only **one** resource type can be ignored at a time.
338+
> Trying to ignore **both `configmaps` and `secrets`** will cause an error in Reloader.
337339
> ✅ **Workaround:** Scale the Reloader deployment to `0` replicas if you want to disable it completely.
338340

341+
**💡 Workload Type Examples:**
342+
343+
```bash
344+
# Ignore only Jobs
345+
--ignored-workload-types=jobs
346+
347+
# Ignore only CronJobs
348+
--ignored-workload-types=cronjobs
349+
350+
# Ignore both (comma-separated)
351+
--ignored-workload-types=jobs,cronjobs
352+
```
353+
354+
> **🔧 Use Case:** Ignoring workload types is useful when you don't want certain types of workloads to be automatically reloaded.
355+
339356
#### 3. 🧩 Namespace Filtering
340357

341358
| Flag | Description |

deployments/kubernetes/chart/reloader/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ helm install stakater/reloader # For helm3 add --generate-name flag or set the r
1515
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
1616

1717
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.
18+
19+
helm install stakater/reloader --set reloader.ignoreJobs=true --set reloader.ignoreCronJobs=true --generate-name # Install Reloader ignoring Jobs and CronJobs from reload monitoring
1820
```
1921

2022
## Uninstalling
@@ -48,6 +50,8 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
4850
| `reloader.isOpenshift` | Enable OpenShift DeploymentConfigs. Valid value are either `true` or `false` | boolean | `false` |
4951
| `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` |
5052
| `reloader.ignoreConfigMaps` | To ignore configmaps. Valid value are either `true` or `false` | boolean | `false` |
53+
| `reloader.ignoreJobs` | To ignore jobs from reload monitoring. Valid value are either `true` or `false`. Translates to `--ignored-workload-types=jobs` | boolean | `false` |
54+
| `reloader.ignoreCronJobs` | To ignore CronJobs from reload monitoring. Valid value are either `true` or `false`. Translates to `--ignored-workload-types=cronjobs` | boolean | `false` |
5155
| `reloader.reloadOnCreate` | Enable reload on create events. Valid value are either `true` or `false` | boolean | `false` |
5256
| `reloader.reloadOnDelete` | Enable reload on delete events. Valid value are either `true` or `false` | boolean | `false` |
5357
| `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` |
@@ -59,7 +63,7 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
5963
| `reloader.watchGlobally` | Allow Reloader to watch in all namespaces (`true`) or just in a single namespace (`false`) | boolean | `true` |
6064
| `reloader.enableHA` | Enable leadership election allowing you to run multiple replicas | boolean | `false` |
6165
| `reloader.enablePProf` | Enables pprof for profiling | boolean | `false` |
62-
| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` |
66+
| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` |
6367
| `reloader.readOnlyRootFileSystem` | Enforce readOnlyRootFilesystem | boolean | `false` |
6468
| `reloader.legacy.rbac` | | boolean | `false` |
6569
| `reloader.matchLabels` | Pod labels to match | map | `{}` |
@@ -116,6 +120,10 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
116120
- Only one of these resources can be ignored at a time:
117121
- `ignoreConfigMaps` **or** `ignoreSecrets`
118122
- Trying to ignore both will cause Helm template compilation errors
123+
- The `ignoreJobs` and `ignoreCronJobs` flags can be used together or individually
124+
- When both are enabled, translates to `--ignored-workload-types=jobs,cronjobs`
125+
- When used individually, translates to `--ignored-workload-types=jobs` or `--ignored-workload-types=cronjobs`
126+
- These flags prevent Reloader from monitoring and reloading the specified workload types
119127

120128
### Special Integrations
121129
- OpenShift (`DeploymentConfig`) and Argo Rollouts support must be **explicitly enabled**

deployments/kubernetes/chart/reloader/templates/deployment.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ spec:
155155

156156
- name: RELOADER_DEPLOYMENT_NAME
157157
value: {{ template "reloader-fullname" . }}
158-
158+
159159
{{- if .Values.reloader.enableHA }}
160160
- name: POD_NAME
161161
valueFrom:
@@ -210,7 +210,7 @@ spec:
210210
{{- . | toYaml | nindent 10 }}
211211
{{- end }}
212212
{{- end }}
213-
{{- 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)}}
213+
{{- 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)}}
214214
args:
215215
{{- if .Values.reloader.logFormat }}
216216
- "--log-format={{ .Values.reloader.logFormat }}"
@@ -224,6 +224,13 @@ spec:
224224
{{- if .Values.reloader.ignoreConfigMaps }}
225225
- "--resources-to-ignore=configMaps"
226226
{{- end }}
227+
{{- if and (.Values.reloader.ignoreJobs) (.Values.reloader.ignoreCronJobs) }}
228+
- "--ignored-workload-types=jobs,cronjobs"
229+
{{- else if .Values.reloader.ignoreJobs }}
230+
- "--ignored-workload-types=jobs"
231+
{{- else if .Values.reloader.ignoreCronJobs }}
232+
- "--ignored-workload-types=cronjobs"
233+
{{- end }}
227234
{{- if .Values.reloader.ignoreNamespaces }}
228235
- "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}"
229236
{{- end }}

deployments/kubernetes/chart/reloader/tests/deployment_test.yaml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,44 @@ tests:
6161
valueFrom:
6262
fieldRef:
6363
fieldPath: metadata.name
64+
65+
- it: sets ignored-workload-types argument when ignoreJobs is true
66+
set:
67+
reloader:
68+
ignoreJobs: true
69+
asserts:
70+
- contains:
71+
path: spec.template.spec.containers[0].args
72+
content: "--ignored-workload-types=jobs"
73+
74+
- it: sets ignored-workload-types argument when ignoreCronJobs is true
75+
set:
76+
reloader:
77+
ignoreCronJobs: true
78+
asserts:
79+
- contains:
80+
path: spec.template.spec.containers[0].args
81+
content: "--ignored-workload-types=cronjobs"
82+
83+
- it: sets ignored-workload-types argument when both ignoreJobs and ignoreCronJobs are true
84+
set:
85+
reloader:
86+
ignoreJobs: true
87+
ignoreCronJobs: true
88+
asserts:
89+
- contains:
90+
path: spec.template.spec.containers[0].args
91+
content: "--ignored-workload-types=jobs,cronjobs"
92+
93+
- it: does not set ignored-workload-types argument when both ignoreJobs and ignoreCronJobs are false
94+
set:
95+
reloader:
96+
ignoreJobs: false
97+
ignoreCronJobs: false
98+
asserts:
99+
- notContains:
100+
path: spec.template.spec.containers[0].args
101+
content: "--ignored-workload-types=jobs"
102+
- notContains:
103+
path: spec.template.spec.containers[0].args
104+
content: "--ignored-workload-types=cronjobs"

deployments/kubernetes/chart/reloader/values.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ reloader:
2727
isOpenshift: false
2828
ignoreSecrets: false
2929
ignoreConfigMaps: false
30+
# Set to true to exclude Job workloads from automatic reload monitoring
31+
# Useful when you don't want Jobs to be restarted when their referenced ConfigMaps/Secrets change
3032
ignoreJobs: false
33+
# Set to true to exclude CronJob workloads from automatic reload monitoring
34+
# Useful when you don't want CronJobs to be restarted when their referenced ConfigMaps/Secrets change
3135
ignoreCronJobs: false
3236
reloadOnCreate: false
3337
reloadOnDelete: false
@@ -84,7 +88,7 @@ reloader:
8488
# - key: "node-role.kubernetes.io/infra-worker"
8589
# operator: "Exists"
8690
affinity: {}
87-
91+
8892
volumeMounts: []
8993
volumes: []
9094

internal/pkg/options/flags.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ var (
6565
WebhookUrl = ""
6666
// ResourcesToIgnore is a list of resources to ignore when watching for changes
6767
ResourcesToIgnore = []string{}
68+
// WorkloadTypesToIgnore is a list of workload types to ignore when watching for changes
69+
WorkloadTypesToIgnore = []string{}
6870
// NamespacesToIgnore is a list of namespace names to ignore when watching for changes
6971
NamespacesToIgnore = []string{}
7072
// NamespaceSelectors is a list of namespace selectors to watch for changes

internal/pkg/util/util.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func ConfigureReloaderFlags(cmd *cobra.Command) {
8383
cmd.PersistentFlags().StringVar(&options.LogLevel, "log-level", "info", "Log level to use (trace, debug, info, warning, error, fatal and panic)")
8484
cmd.PersistentFlags().StringVar(&options.WebhookUrl, "webhook-url", "", "webhook to trigger instead of performing a reload")
8585
cmd.PersistentFlags().StringSliceVar(&options.ResourcesToIgnore, "resources-to-ignore", options.ResourcesToIgnore, "list of resources to ignore (valid options 'configMaps' or 'secrets')")
86+
cmd.PersistentFlags().StringSliceVar(&options.WorkloadTypesToIgnore, "ignored-workload-types", options.WorkloadTypesToIgnore, "list of workload types to ignore (valid options: 'jobs', 'cronjobs', or both)")
8687
cmd.PersistentFlags().StringSliceVar(&options.NamespacesToIgnore, "namespaces-to-ignore", options.NamespacesToIgnore, "list of namespaces to ignore")
8788
cmd.PersistentFlags().StringSliceVar(&options.NamespaceSelectors, "namespace-selector", options.NamespaceSelectors, "list of key:value labels to filter on for namespaces")
8889
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) {
112113

113114
return ignoredResourcesList, nil
114115
}
116+
117+
func GetIgnoredWorkloadTypesList() (List, error) {
118+
119+
ignoredWorkloadTypesList := options.WorkloadTypesToIgnore
120+
121+
for _, v := range ignoredWorkloadTypesList {
122+
if v != "jobs" && v != "cronjobs" {
123+
return nil, fmt.Errorf("'ignored-workload-types' accepts 'jobs', 'cronjobs', or both, not '%s'", v)
124+
}
125+
}
126+
127+
return ignoredWorkloadTypesList, nil
128+
}

internal/pkg/util/util_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package util
33
import (
44
"testing"
55

6+
"github.com/stakater/Reloader/internal/pkg/options"
67
v1 "k8s.io/api/core/v1"
78
)
89

@@ -45,3 +46,141 @@ func TestGetHashFromConfigMap(t *testing.T) {
4546
}
4647
}
4748
}
49+
50+
func TestGetIgnoredWorkloadTypesList(t *testing.T) {
51+
// Save original state
52+
originalWorkloadTypes := options.WorkloadTypesToIgnore
53+
defer func() {
54+
options.WorkloadTypesToIgnore = originalWorkloadTypes
55+
}()
56+
57+
tests := []struct {
58+
name string
59+
workloadTypes []string
60+
expectError bool
61+
expected []string
62+
}{
63+
{
64+
name: "Both jobs and cronjobs",
65+
workloadTypes: []string{"jobs", "cronjobs"},
66+
expectError: false,
67+
expected: []string{"jobs", "cronjobs"},
68+
},
69+
{
70+
name: "Only jobs",
71+
workloadTypes: []string{"jobs"},
72+
expectError: false,
73+
expected: []string{"jobs"},
74+
},
75+
{
76+
name: "Only cronjobs",
77+
workloadTypes: []string{"cronjobs"},
78+
expectError: false,
79+
expected: []string{"cronjobs"},
80+
},
81+
{
82+
name: "Empty list",
83+
workloadTypes: []string{},
84+
expectError: false,
85+
expected: []string{},
86+
},
87+
{
88+
name: "Invalid workload type",
89+
workloadTypes: []string{"invalid"},
90+
expectError: true,
91+
expected: nil,
92+
},
93+
{
94+
name: "Mixed valid and invalid",
95+
workloadTypes: []string{"jobs", "invalid"},
96+
expectError: true,
97+
expected: nil,
98+
},
99+
{
100+
name: "Duplicate values",
101+
workloadTypes: []string{"jobs", "jobs"},
102+
expectError: false,
103+
expected: []string{"jobs", "jobs"},
104+
},
105+
}
106+
107+
for _, tt := range tests {
108+
t.Run(tt.name, func(t *testing.T) {
109+
// Set the global option
110+
options.WorkloadTypesToIgnore = tt.workloadTypes
111+
112+
result, err := GetIgnoredWorkloadTypesList()
113+
114+
if tt.expectError && err == nil {
115+
t.Errorf("Expected error but got none")
116+
}
117+
118+
if !tt.expectError && err != nil {
119+
t.Errorf("Expected no error but got: %v", err)
120+
}
121+
122+
if !tt.expectError {
123+
if len(result) != len(tt.expected) {
124+
t.Errorf("Expected %v, got %v", tt.expected, result)
125+
return
126+
}
127+
128+
for i, expected := range tt.expected {
129+
if i >= len(result) || result[i] != expected {
130+
t.Errorf("Expected %v, got %v", tt.expected, result)
131+
break
132+
}
133+
}
134+
}
135+
})
136+
}
137+
}
138+
139+
func TestListContains(t *testing.T) {
140+
tests := []struct {
141+
name string
142+
list List
143+
item string
144+
expected bool
145+
}{
146+
{
147+
name: "List contains item",
148+
list: List{"jobs", "cronjobs"},
149+
item: "jobs",
150+
expected: true,
151+
},
152+
{
153+
name: "List does not contain item",
154+
list: List{"jobs"},
155+
item: "cronjobs",
156+
expected: false,
157+
},
158+
{
159+
name: "Empty list",
160+
list: List{},
161+
item: "jobs",
162+
expected: false,
163+
},
164+
{
165+
name: "Case sensitive matching",
166+
list: List{"jobs", "cronjobs"},
167+
item: "Jobs",
168+
expected: false,
169+
},
170+
{
171+
name: "Multiple occurrences",
172+
list: List{"jobs", "jobs", "cronjobs"},
173+
item: "jobs",
174+
expected: true,
175+
},
176+
}
177+
178+
for _, tt := range tests {
179+
t.Run(tt.name, func(t *testing.T) {
180+
result := tt.list.Contains(tt.item)
181+
if result != tt.expected {
182+
t.Errorf("Expected %v, got %v", tt.expected, result)
183+
}
184+
})
185+
}
186+
}

0 commit comments

Comments
 (0)