Skip to content

Commit 7b12f9a

Browse files
authored
Merge pull request #46 from Infisical/daniel/scoped-namespaces
feat: multiple scoped namespaces
2 parents 89b3e69 + 116492c commit 7b12f9a

File tree

10 files changed

+240
-57
lines changed

10 files changed

+240
-57
lines changed

cmd/main.go

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"fmt"
2323
"os"
2424
"path/filepath"
25+
"strings"
2526

2627
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2728
// to ensure that exec-entrypoint and run can make use of them.
@@ -65,10 +66,12 @@ func main() {
6566
var probeAddr string
6667
var secureMetrics bool
6768
var enableHTTP2 bool
68-
var namespace string
69+
var namespaces string
70+
var deprecatedScopedNamespaceWarning bool
6971

7072
var tlsOpts []func(*tls.Config)
71-
flag.StringVar(&namespace, "namespace", "", "Watch InfisicalSecrets scoped in the provided namespace only")
73+
flag.StringVar(&namespaces, "namespaces", "", "Comma-separated list of namespaces to watch. If empty, watches all namespaces (cluster-scoped)")
74+
flag.BoolVar(&deprecatedScopedNamespaceWarning, "deprecated-scoped-namespace-warning", false, "If true, logs a deprecation warning for the scopedNamespace field")
7275
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
7376
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
7477
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
@@ -203,15 +206,35 @@ func main() {
203206
// LeaderElectionReleaseOnCancel: true,
204207
}
205208

209+
// Parse namespaces and configure cache for namespace-scoped watching
210+
var namespaceList []string
211+
if namespaces != "" {
212+
for _, ns := range strings.Split(namespaces, ",") {
213+
ns = strings.TrimSpace(ns)
214+
if ns != "" {
215+
namespaceList = append(namespaceList, ns)
216+
}
217+
}
218+
}
219+
220+
isNamespaceScoped := len(namespaceList) > 0
221+
222+
// Log deprecation warning if using the deprecated scopedNamespace field
223+
if deprecatedScopedNamespaceWarning {
224+
setupLog.Info("WARNING: The 'scopedNamespace' field is deprecated and will be removed in a future version. Please use 'scopedNamespaces' (plural) instead.")
225+
}
226+
206227
// Only set cache options if we're namespace-scoped
207-
if namespace != "" {
228+
if isNamespaceScoped {
229+
defaultNamespaces := make(map[string]cache.Config)
230+
for _, ns := range namespaceList {
231+
defaultNamespaces[ns] = cache.Config{}
232+
}
208233
managerOptions.Cache = cache.Options{
209-
Scheme: scheme,
210-
DefaultNamespaces: map[string]cache.Config{
211-
namespace: {}, // whichever namespace the operator is running in
212-
},
234+
Scheme: scheme,
235+
DefaultNamespaces: defaultNamespaces,
213236
}
214-
ctrl.Log.Info(fmt.Sprintf("Watching CRDs in [namespace=%s]", namespace))
237+
ctrl.Log.Info(fmt.Sprintf("Watching CRDs in namespaces: %v", namespaceList))
215238
}
216239

217240
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions)
@@ -226,17 +249,15 @@ func main() {
226249
Client: mgr.GetClient(),
227250
Scheme: mgr.GetScheme(),
228251
BaseLogger: ctrl.Log,
229-
Namespace: namespace,
230-
IsNamespaceScoped: namespace != "",
252+
IsNamespaceScoped: isNamespaceScoped,
231253
}).SetupWithManager(mgr); err != nil {
232254
setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret")
233255
os.Exit(1)
234256
}
235257
if err := (&controller.InfisicalPushSecretReconciler{
236258
Client: mgr.GetClient(),
237259
Scheme: mgr.GetScheme(),
238-
IsNamespaceScoped: namespace != "",
239-
Namespace: namespace,
260+
IsNamespaceScoped: isNamespaceScoped,
240261
BaseLogger: ctrl.Log,
241262
}).SetupWithManager(mgr); err != nil {
242263
setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret")
@@ -246,8 +267,7 @@ func main() {
246267
Client: mgr.GetClient(),
247268
Scheme: mgr.GetScheme(),
248269
BaseLogger: ctrl.Log,
249-
IsNamespaceScoped: namespace != "",
250-
Namespace: namespace,
270+
IsNamespaceScoped: isNamespaceScoped,
251271
}).SetupWithManager(mgr); err != nil {
252272
setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret")
253273
os.Exit(1)

config/manager/kustomization.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
resources:
2-
- manager.yaml
31
apiVersion: kustomize.config.k8s.io/v1beta1
42
kind: Kustomization
53
images:

helm-charts/secrets-operator/templates/_helpers.tpl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,40 @@ Create the name of the service account to use
5959
{{- else }}
6060
{{- printf "%s-controller-manager" (include "secrets-operator.fullname" .) }}
6161
{{- end }}
62+
{{- end }}
63+
64+
{{/*
65+
Compute the list of scoped namespaces.
66+
scopedNamespaces takes precedence over the deprecated scopedNamespace.
67+
Handles both array input (--set "scopedNamespaces={ns1,ns2}") and
68+
comma-separated string input (--set scopedNamespaces="ns1,ns2").
69+
Returns a JSON object with a "list" key that should be parsed with fromJson.
70+
Usage: $namespaces := (include "secrets-operator.scopedNamespaces" . | fromJson).list
71+
*/}}
72+
{{- define "secrets-operator.scopedNamespaces" -}}
73+
{{- if .Values.scopedNamespaces -}}
74+
{{- if kindIs "string" .Values.scopedNamespaces -}}
75+
{{- /* Handle comma-separated string input */ -}}
76+
{"list": {{ splitList "," .Values.scopedNamespaces | toJson }}}
77+
{{- else -}}
78+
{{- /* Handle array input */ -}}
79+
{"list": {{ .Values.scopedNamespaces | toJson }}}
80+
{{- end -}}
81+
{{- else if .Values.scopedNamespace -}}
82+
{"list": {{ list .Values.scopedNamespace | toJson }}}
83+
{{- else -}}
84+
{"list": []}
85+
{{- end -}}
86+
{{- end }}
87+
88+
{{/*
89+
Check if we're using the deprecated scopedNamespace field.
90+
Returns "true" or "false" as a string.
91+
*/}}
92+
{{- define "secrets-operator.usingDeprecatedScopedNamespace" -}}
93+
{{- if and (not .Values.scopedNamespaces) .Values.scopedNamespace -}}
94+
true
95+
{{- else -}}
96+
false
97+
{{- end -}}
6298
{{- end }}

helm-charts/secrets-operator/templates/deployment.yaml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@ spec:
2222
containers:
2323
- args:
2424
{{- toYaml .Values.controllerManager.manager.args | nindent 8 }}
25-
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
26-
- --namespace={{ .Values.scopedNamespace }}
25+
{{- $namespaces := (include "secrets-operator.scopedNamespaces" . | fromJson).list }}
26+
{{- if and $namespaces .Values.scopedRBAC }}
27+
- --namespaces={{ join "," $namespaces }}
28+
{{- if eq (include "secrets-operator.usingDeprecatedScopedNamespace" .) "true" }}
29+
- --deprecated-scoped-namespace-warning
30+
{{- end }}
2731
{{- end }}
2832
command:
2933
- /manager

helm-charts/secrets-operator/templates/manager-rbac.yaml

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,8 @@
1-
apiVersion: rbac.authorization.k8s.io/v1
2-
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
3-
kind: Role
4-
{{- else }}
5-
kind: ClusterRole
6-
{{- end }}
7-
metadata:
8-
name: {{ include "secrets-operator.fullname" . }}-manager-role
9-
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
10-
namespace: {{ .Values.scopedNamespace | quote }}
11-
{{- end }}
12-
labels:
13-
{{- include "secrets-operator.labels" . | nindent 4 }}
1+
{{- $namespaces := (include "secrets-operator.scopedNamespaces" . | fromJson).list }}
2+
{{- $isScopedMode := and $namespaces .Values.scopedRBAC }}
3+
4+
{{- /* Define the rules once to avoid repetition */ -}}
5+
{{- define "secrets-operator.managerRules" }}
146
rules:
157
- apiGroups:
168
- ""
@@ -95,30 +87,59 @@ rules:
9587
- get
9688
- patch
9789
- update
90+
{{- end }}
91+
92+
{{- if $isScopedMode }}
93+
{{- /* Scoped mode: Create a Role and RoleBinding in each namespace */ -}}
94+
{{- range $ns := $namespaces }}
95+
---
96+
apiVersion: rbac.authorization.k8s.io/v1
97+
kind: Role
98+
metadata:
99+
name: {{ include "secrets-operator.fullname" $ }}-manager-role
100+
namespace: {{ $ns | quote }}
101+
labels:
102+
{{- include "secrets-operator.labels" $ | nindent 4 }}
103+
{{- include "secrets-operator.managerRules" $ }}
98104
---
99105
apiVersion: rbac.authorization.k8s.io/v1
100-
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
101106
kind: RoleBinding
107+
metadata:
108+
name: {{ include "secrets-operator.fullname" $ }}-manager-rolebinding
109+
namespace: {{ $ns | quote }}
110+
labels:
111+
{{- include "secrets-operator.labels" $ | nindent 4 }}
112+
roleRef:
113+
apiGroup: rbac.authorization.k8s.io
114+
kind: Role
115+
name: {{ include "secrets-operator.fullname" $ }}-manager-role
116+
subjects:
117+
- kind: ServiceAccount
118+
name: {{ include "secrets-operator.serviceAccountName" $ }}
119+
namespace: {{ $.Release.Namespace }}
120+
{{- end }}
102121
{{- else }}
122+
{{- /* Cluster-scoped mode: Create a ClusterRole and ClusterRoleBinding */ -}}
123+
apiVersion: rbac.authorization.k8s.io/v1
124+
kind: ClusterRole
125+
metadata:
126+
name: {{ include "secrets-operator.fullname" . }}-manager-role
127+
labels:
128+
{{- include "secrets-operator.labels" . | nindent 4 }}
129+
{{- include "secrets-operator.managerRules" . }}
130+
---
131+
apiVersion: rbac.authorization.k8s.io/v1
103132
kind: ClusterRoleBinding
104-
{{- end }}
105133
metadata:
106134
name: {{ include "secrets-operator.fullname" . }}-manager-rolebinding
107-
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
108-
namespace: {{ .Values.scopedNamespace | quote }}
109-
{{- end }}
110135
labels:
111-
112136
{{- include "secrets-operator.labels" . | nindent 4 }}
113137
roleRef:
114138
apiGroup: rbac.authorization.k8s.io
115-
{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
116-
kind: Role
117-
{{- else }}
118139
kind: ClusterRole
119-
{{- end }}
120-
name: '{{ include "secrets-operator.fullname" . }}-manager-role'
140+
name: {{ include "secrets-operator.fullname" . }}-manager-role
121141
subjects:
122142
- kind: ServiceAccount
123-
name: '{{ include "secrets-operator.serviceAccountName" . }}'
124-
namespace: '{{ .Release.Namespace }}'
143+
name: {{ include "secrets-operator.serviceAccountName" . }}
144+
namespace: {{ .Release.Namespace }}
145+
{{- end }}

helm-charts/secrets-operator/values.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,19 @@ controllerManager:
3232
type: RuntimeDefault
3333
replicas: 1
3434
kubernetesClusterDomain: cluster.local
35+
36+
# DEPRECATED: Use scopedNamespaces instead. This field will be removed in a future version.
37+
# If both scopedNamespace and scopedNamespaces are set, scopedNamespaces takes precedence.
3538
scopedNamespace: ""
39+
40+
# List of namespaces to watch. If empty, the operator watches all namespaces (cluster-scoped).
41+
# When scopedRBAC is true, a Role and RoleBinding will be created in each namespace.
42+
# Example:
43+
# scopedNamespaces:
44+
# - team-a-namespace
45+
# - team-b-namespace
46+
scopedNamespaces: []
47+
3648
scopedRBAC: false
3749
installCRDs: true
3850
imagePullSecrets: []

internal/controller/infisicaldynamicsecret_controller.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ type InfisicalDynamicSecretReconciler struct {
4545
BaseLogger logr.Logger
4646
Scheme *runtime.Scheme
4747
Random *rand.Rand
48-
Namespace string
4948
IsNamespaceScoped bool
5049
}
5150

internal/controller/infisicalpushsecret_controller.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ type InfisicalPushSecretReconciler struct {
5050
BaseLogger logr.Logger
5151
Scheme *runtime.Scheme
5252
IsNamespaceScoped bool
53-
Namespace string
5453
}
5554

5655
var infisicalPushSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables)

internal/controller/infisicalsecret_controller.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ type InfisicalSecretReconciler struct {
4646
Scheme *runtime.Scheme
4747

4848
SourceCh chan event.TypedGenericEvent[client.Object]
49-
Namespace string
5049
IsNamespaceScoped bool
5150
}
5251

0 commit comments

Comments
 (0)