Skip to content

Commit b52d0f7

Browse files
authored
Merge pull request #1184 from stakater/feat/update-chart-v2
added support for multiple namespaces (v2)
2 parents 6037ca4 + 80032eb commit b52d0f7

49 files changed

Lines changed: 1959 additions & 427 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cmd/reloader/main.go

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,15 @@ func newReloaderCommand() *cobra.Command {
5454
}
5555

5656
func run(cmd *cobra.Command, args []string) error {
57-
if err := config.ApplyFlags(cfg); err != nil {
57+
// Configure logging first so ApplyFlags can surface namespace-scope warnings
58+
// through a ready logger instead of returning them to the caller.
59+
log, err := configureLogging(config.LoggingFlags())
60+
if err != nil {
61+
return fmt.Errorf("configuring logging: %w", err)
62+
}
63+
controllerruntime.SetLogger(log)
64+
65+
if err := config.ApplyFlags(cfg, log); err != nil {
5866
return fmt.Errorf("applying flags: %w", err)
5967
}
6068

@@ -72,19 +80,12 @@ func run(cmd *cobra.Command, args []string) error {
7280
}
7381
}
7482

75-
log, err := configureLogging(cfg.LogFormat, cfg.LogLevel)
76-
if err != nil {
77-
return fmt.Errorf("configuring logging: %w", err)
78-
}
79-
80-
controllerruntime.SetLogger(log)
81-
8283
log.Info("Starting Reloader")
8384

84-
if cfg.WatchedNamespace != "" {
85-
log.Info("watching single namespace", "namespace", cfg.WatchedNamespace)
86-
} else {
85+
if cfg.IsGlobalMode() {
8786
log.Info("watching all namespaces")
87+
} else {
88+
log.Info("watching scoped namespaces", "namespaces", cfg.WatchedNamespaces)
8889
}
8990

9091
if len(cfg.NamespaceSelectors) > 0 {
@@ -149,14 +150,13 @@ func run(cmd *cobra.Command, args []string) error {
149150
return fmt.Errorf("setting up reconcilers: %w", err)
150151
}
151152

152-
// Skip metadata publisher when ConfigMaps are ignored (no RBAC permissions)
153-
if !cfg.IsResourceIgnored("configmaps") {
154-
if err := mgr.Add(metadata.Runnable(mgr.GetClient(), cfg, log)); err != nil {
155-
log.Error(err, "Failed to add metadata publisher")
156-
// Non-fatal, continue starting
157-
}
158-
} else {
159-
log.Info("skipping metadata publisher (configmaps ignored)")
153+
// Meta-info is internal instance metadata and is always published. The
154+
// publisher builds its own uncached client (see metadata.Runnable) because
155+
// the ConfigMap lives in Reloader's own namespace, which the manager cache
156+
// does not cover in scoped mode.
157+
if err := mgr.Add(metadata.Runnable(mgr.GetConfig(), mgr.GetScheme(), cfg, log)); err != nil {
158+
log.Error(err, "Failed to add metadata publisher")
159+
// Non-fatal, continue starting
160160
}
161161

162162
if cfg.EnablePProf {

deployments/kubernetes/chart/reloader/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,12 @@ helm uninstall {{RELEASE_NAME}} -n {{NAMESPACE}}
5656
| `reloader.reloadOnDelete` | Enable reload on delete events. Valid value are either `true` or `false` | boolean | `false` |
5757
| `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` |
5858
| `reloader.reloadStrategy` | Strategy to trigger resource restart, set to either `default`, `env-vars` or `annotations` | enumeration | `default` |
59-
| `reloader.ignoreNamespaces` | List of comma separated namespaces to ignore, if multiple are provided, they are combined with the AND operator | string | `""` |
59+
| `reloader.ignoreNamespaces` | List of comma separated namespaces to ignore, if multiple are provided, they are combined with the AND operator. Only honored when `reloader.watchGlobally` is `true`; in single-namespace and scoped (`reloader.namespaces`) modes the watched set is already explicit and this value is ignored. | string | `""` |
6060
| `reloader.namespaceSelector` | List of comma separated k8s label selectors for namespaces selection. The parameter only used when `reloader.watchGlobally` is `true`. See [LIST and WATCH filtering](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#list-and-watch-filtering) for more details on label-selector | string | `""` |
6161
| `reloader.resourceLabelSelector` | List of comma separated label selectors, if multiple are provided they are combined with the AND operator | string | `""` |
6262
| `reloader.logFormat` | Set type of log format. Value could be either `json` or `""` | string | `""` |
6363
| `reloader.watchGlobally` | Allow Reloader to watch in all namespaces (`true`) or just in a single namespace (`false`) | boolean | `true` |
64+
| `reloader.namespaces` | Explicit namespaces to watch (scoped mode). When non-empty and `reloader.watchGlobally` is `false`, Reloader watches exactly these namespaces and the chart creates a namespace-scoped Role + RoleBinding in each (no ClusterRole). The release namespace is not watched for reloads unless you list it explicitly; the chart only grants it a minimal Role for Reloader's internal meta-info ConfigMap (and leader-election in HA). Accepts either a YAML list (`["team-a","team-b"]`) or a comma-separated string (`"team-a,team-b"`). | list/string | `[]` |
6465
| `reloader.enableHA` | Enable leadership election allowing you to run multiple replicas | boolean | `false` |
6566
| `reloader.enablePProf` | Enables pprof for profiling | boolean | `false` |
6667
| `reloader.pprofAddr` | Address to start pprof server on | string | `:6060` |

deployments/kubernetes/chart/reloader/templates/_helpers.tpl

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,231 @@ Create the namespace selector if it does not watch globally
8888
{{- end -}}
8989
{{- end -}}
9090

91+
{{/*
92+
Namespaces to watch in scoped mode: exactly the user-supplied reloader.namespaces,
93+
trimmed, de-duped and sorted. The release namespace is intentionally NOT added here
94+
— Reloader watches only what the user asked for. An empty result is not necessarily
95+
global mode: with watchGlobally=false it becomes single-namespace mode (the release
96+
namespace, injected via --namespaces by reloader-effectiveNamespaces-csv); only with
97+
watchGlobally=true does empty mean watch-all.
98+
Returns a JSON-encoded list; consumers use mustFromJson to iterate.
99+
*/}}
100+
{{- define "reloader-watchNamespaces" -}}
101+
{{- $ns := .Values.reloader.namespaces | default list -}}
102+
{{- if kindIs "string" $ns -}}
103+
{{- $ns = splitList "," $ns -}}
104+
{{- end -}}
105+
{{- $clean := list -}}
106+
{{- range $ns -}}
107+
{{- $t := . | toString | trim -}}
108+
{{- if $t -}}
109+
{{- $clean = append $clean $t -}}
110+
{{- end -}}
111+
{{- end -}}
112+
{{- $clean | uniq | sortAlpha | toJson -}}
113+
{{- end -}}
114+
115+
{{/*
116+
Comma-joined form of reloader-watchNamespaces, for the --namespaces CLI flag.
117+
*/}}
118+
{{- define "reloader-watchNamespaces-csv" -}}
119+
{{- include "reloader-watchNamespaces" . | mustFromJson | join "," -}}
120+
{{- end -}}
121+
122+
{{/*
123+
The effective watched namespaces for the --namespaces CLI flag — the single
124+
chart-side source of truth for watch scope, so the binary never has to fall back
125+
to the KUBERNETES_NAMESPACE env:
126+
- scoped mode -> the cleaned reloader.namespaces list
127+
- single-namespace mode -> the release namespace (watchGlobally=false, no list)
128+
- global mode -> empty (no --namespaces flag; watch all)
129+
Returns a comma-joined string ("" in global mode).
130+
*/}}
131+
{{- define "reloader-effectiveNamespaces-csv" -}}
132+
{{- $watch := include "reloader-watchNamespaces" . | mustFromJson -}}
133+
{{- if $watch -}}
134+
{{- $watch | join "," -}}
135+
{{- else if not .Values.reloader.watchGlobally -}}
136+
{{- .Values.namespace | default .Release.Namespace -}}
137+
{{- end -}}
138+
{{- end -}}
139+
140+
{{/*
141+
Whether Reloader runs in scoped mode. This is the single source of truth for the
142+
scoped-vs-global decision: it is true only when the cleaned watch list
143+
(reloader-watchNamespaces) is non-empty. Gate on this rather than the raw
144+
.Values.reloader.namespaces, which is truthy even for values like " , " that trim
145+
to an empty list (those must fall through to global/single-namespace mode).
146+
Returns "true" (truthy) or "" (falsy).
147+
*/}}
148+
{{- define "reloader-isScoped" -}}
149+
{{- if include "reloader-watchNamespaces" . | mustFromJson -}}
150+
true
151+
{{- end -}}
152+
{{- end -}}
153+
154+
{{/*
155+
Fails the render on an inconsistent namespace configuration: reloader.namespaces
156+
(scoped mode) requires reloader.watchGlobally=false. Included from deployment.yaml
157+
so it is validated once regardless of which templates render.
158+
*/}}
159+
{{- define "reloader-validate-namespaces" -}}
160+
{{- if and .Values.reloader.watchGlobally (include "reloader-isScoped" .) -}}
161+
{{- fail "reloader.namespaces is set but reloader.watchGlobally is true; set reloader.watchGlobally=false to use scoped namespace mode." -}}
162+
{{- end -}}
163+
{{- end -}}
164+
165+
{{/*
166+
RBAC rules Reloader needs in its own (release) namespace, independent of the
167+
watched namespaces. Reloader publishes an internal meta-info ConfigMap there in
168+
every mode, so configmap write access is always granted. In scoped mode the
169+
release namespace is not covered by the watch RBAC, so under HA the leader-election
170+
leases and the events it emits are granted here too; in global/single mode those
171+
are already covered by the ClusterRole or the single-namespace Role.
172+
Expects the root context ($) as its argument.
173+
*/}}
174+
{{- define "reloader-release-rules" }}
175+
- apiGroups:
176+
- ""
177+
resources:
178+
- configmaps
179+
verbs:
180+
- get
181+
- create
182+
- update
183+
- patch
184+
{{- if and (include "reloader-isScoped" .) .Values.reloader.enableHA }}
185+
- apiGroups:
186+
- "coordination.k8s.io"
187+
resources:
188+
- leases
189+
verbs:
190+
- create
191+
- get
192+
- update
193+
- apiGroups:
194+
- ""
195+
- "events.k8s.io"
196+
resources:
197+
- events
198+
verbs:
199+
- create
200+
- patch
201+
- update
202+
{{- end }}
203+
{{- end -}}
204+
205+
{{/*
206+
The namespaced RBAC rules granted to Reloader in every watched namespace.
207+
Shared between the single-namespace Role and the per-namespace scoped Roles so
208+
the rule set is defined once. Expects the root context ($) as its argument.
209+
*/}}
210+
{{- define "reloader-namespaced-rules" }}
211+
- apiGroups:
212+
- ""
213+
resources:
214+
{{- if .Values.reloader.ignoreSecrets }}{{- else }}
215+
- secrets
216+
{{- end }}
217+
{{- if .Values.reloader.ignoreConfigMaps }}{{- else }}
218+
- configmaps
219+
{{- end }}
220+
verbs:
221+
- list
222+
- get
223+
- watch
224+
{{- if and (.Capabilities.APIVersions.Has "apps.openshift.io/v1") (.Values.reloader.isOpenshift) }}
225+
- apiGroups:
226+
- "apps.openshift.io"
227+
- ""
228+
resources:
229+
- deploymentconfigs
230+
verbs:
231+
- list
232+
- get
233+
- update
234+
- patch
235+
{{- end }}
236+
{{- if and (.Capabilities.APIVersions.Has "argoproj.io/v1alpha1") (.Values.reloader.isArgoRollouts) }}
237+
- apiGroups:
238+
- "argoproj.io"
239+
resources:
240+
- rollouts
241+
verbs:
242+
- list
243+
- get
244+
- watch
245+
- update
246+
- patch
247+
{{- end }}
248+
- apiGroups:
249+
- "apps"
250+
resources:
251+
- deployments
252+
- daemonsets
253+
- statefulsets
254+
verbs:
255+
- list
256+
- get
257+
- watch
258+
- update
259+
- patch
260+
{{- if .Values.reloader.ignoreCronJobs }}{{- else }}
261+
- apiGroups:
262+
- "batch"
263+
resources:
264+
- cronjobs
265+
verbs:
266+
- list
267+
- get
268+
- watch
269+
- update
270+
- patch
271+
{{- end }}
272+
{{- if .Values.reloader.ignoreJobs }}{{- else }}
273+
- apiGroups:
274+
- "batch"
275+
resources:
276+
- jobs
277+
verbs:
278+
- create
279+
- delete
280+
- list
281+
- get
282+
- watch
283+
{{- end }}
284+
{{- if .Values.reloader.enableHA }}
285+
- apiGroups:
286+
- "coordination.k8s.io"
287+
resources:
288+
- leases
289+
verbs:
290+
- create
291+
- get
292+
- update
293+
{{- end}}
294+
{{- if .Values.reloader.enableCSIIntegration }}
295+
- apiGroups:
296+
- "secrets-store.csi.x-k8s.io"
297+
resources:
298+
- secretproviderclasspodstatuses
299+
- secretproviderclasses
300+
verbs:
301+
- list
302+
- get
303+
- watch
304+
{{- end}}
305+
- apiGroups:
306+
- ""
307+
- "events.k8s.io"
308+
resources:
309+
- events
310+
verbs:
311+
- create
312+
- patch
313+
- update
314+
{{- end -}}
315+
91316
{{/*
92317
Normalizes global.imagePullSecrets to a list of objects with name fields.
93318
Supports both of these in values.yaml:

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ rules:
7474
verbs:
7575
- list
7676
- get
77+
- watch
7778
- update
7879
- patch
79-
- watch
8080
{{- if .Values.reloader.ignoreCronJobs }}{{- else }}
8181
- apiGroups:
8282
- "batch"
@@ -124,9 +124,11 @@ rules:
124124
{{- end}}
125125
- apiGroups:
126126
- ""
127+
- "events.k8s.io"
127128
resources:
128129
- events
129130
verbs:
130131
- create
131132
- patch
133+
- update
132134
{{- end }}

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{- include "reloader-validate-namespaces" . -}}
12
apiVersion: apps/v1
23
kind: Deployment
34
metadata:
@@ -143,12 +144,6 @@ spec:
143144
fieldRef:
144145
fieldPath: {{ $value | quote}}
145146
{{- end }}
146-
{{- end }}
147-
{{- if eq .Values.reloader.watchGlobally false }}
148-
- name: KUBERNETES_NAMESPACE
149-
valueFrom:
150-
fieldRef:
151-
fieldPath: metadata.namespace
152147
{{- end }}
153148

154149
- name: RELOADER_NAMESPACE
@@ -215,7 +210,7 @@ spec:
215210
{{- . | toYaml | nindent 10 }}
216211
{{- end }}
217212
{{- end }}
218-
{{- 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) (.Values.reloader.enableCSIIntegration)}}
213+
{{- if or (.Values.reloader.logFormat) (.Values.reloader.logLevel) (.Values.reloader.ignoreSecrets) (and .Values.reloader.ignoreNamespaces .Values.reloader.watchGlobally) (include "reloader-effectiveNamespaces-csv" .) (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) (.Values.reloader.enableCSIIntegration)}}
219214
args:
220215
{{- if .Values.reloader.logFormat }}
221216
- "--log-format={{ .Values.reloader.logFormat }}"
@@ -236,7 +231,10 @@ spec:
236231
{{- else if .Values.reloader.ignoreCronJobs }}
237232
- "--ignored-workload-types=cronjobs"
238233
{{- end }}
239-
{{- if .Values.reloader.ignoreNamespaces }}
234+
{{- if (include "reloader-effectiveNamespaces-csv" .) }}
235+
- "--namespaces={{ include "reloader-effectiveNamespaces-csv" . }}"
236+
{{- end }}
237+
{{- if and .Values.reloader.ignoreNamespaces .Values.reloader.watchGlobally }}
240238
- "--namespaces-to-ignore={{ .Values.reloader.ignoreNamespaces }}"
241239
{{- end }}
242240
{{- if (include "reloader-namespaceSelector" .) }}
@@ -252,7 +250,7 @@ spec:
252250
{{- end }}
253251
{{- end }}
254252
{{- if .Values.reloader.enableCSIIntegration }}
255-
- "--enable-csi-integration"
253+
- "--enable-csi-integration=true"
256254
{{- end }}
257255
{{- if .Values.reloader.custom_annotations }}
258256
{{- if .Values.reloader.custom_annotations.configmap }}

0 commit comments

Comments
 (0)