diff --git a/cmd/main.go b/cmd/main.go index 646b9f51..41b273a9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -8,6 +8,7 @@ import ( "flag" "log/slog" "os" + "strings" temporaliov1alpha1 "github.com/temporalio/temporal-worker-controller/api/v1alpha1" "github.com/temporalio/temporal-worker-controller/internal/controller" @@ -20,6 +21,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" @@ -41,8 +43,12 @@ func main() { var metricsAddr string var enableLeaderElection bool var probeAddr string + var watchNamespace string flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.StringVar(&watchNamespace,"watch-namespace","", + "Namespace(s) that the controller watches. Can be a single namespace or a comma-separated list. "+ + "If empty, the controller watches all namespaces.", ) flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") @@ -52,10 +58,29 @@ func main() { opts.BindFlags(flag.CommandLine) flag.Parse() + // If flag is not set, fall back to environment variable + if watchNamespace == "" { + watchNamespace = os.Getenv("WATCH_NAMESPACE") + } + + // Parse comma-separated namespaces into []string, trimming whitespace and dropping empty entries + var watchNamespaces []string + if watchNamespace != "" { + parts := strings.Split(watchNamespace, ",") + watchNamespaces = make([]string, 0, len(parts)) + for _, p := range parts { + ns := strings.TrimSpace(p) + if ns == "" { + continue + } + watchNamespaces = append(watchNamespaces, ns) + } + } + //ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) ctrl.SetLogger(zap.New(zap.JSONEncoder())) - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + managerOptions := ctrl.Options{ Scheme: scheme, Metrics: metricsserver.Options{ BindAddress: metricsAddr, @@ -74,7 +99,23 @@ func main() { // if you are doing or is intended to do any operation such as perform cleanups // after the manager stops then its usage might be unsafe. // LeaderElectionReleaseOnCancel: true, - }) + } + + // Scope manager cache/watches to 0/1/N namespaces. + if len(watchNamespaces) > 0 { + setupLog.Info("running controller in namespace-scoped mode", "namespaces", watchNamespaces) + + defaultNamespaces := map[string]cache.Config{} + for _, ns := range watchNamespaces { + defaultNamespaces[ns] = cache.Config{} + } + + managerOptions.Cache = cache.Options{ + DefaultNamespaces: defaultNamespaces, + } + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions) + if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) diff --git a/helm/temporal-worker-controller/templates/_helpers.tpl b/helm/temporal-worker-controller/templates/_helpers.tpl index 5b143384..ba851463 100644 --- a/helm/temporal-worker-controller/templates/_helpers.tpl +++ b/helm/temporal-worker-controller/templates/_helpers.tpl @@ -18,3 +18,25 @@ Used for matchLabels (Deployments, Services, affinities, etc.) app.kubernetes.io/name: temporal-worker-controller app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} + +{{/* +Return Role or ClusterRole depending on ownNamespace +*/}} +{{- define "temporal-worker-controller.rbac.roleKind" -}} +{{- if .Values.rbac.ownNamespace -}} +Role +{{- else -}} +ClusterRole +{{- end -}} +{{- end -}} + +{{/* +Return RoleBinding or ClusterRoleBinding depending on ownNamespace +*/}} +{{- define "temporal-worker-controller.rbac.roleBindingKind" -}} +{{- if .Values.rbac.ownNamespace -}} +RoleBinding +{{- else -}} +ClusterRoleBinding +{{- end -}} +{{- end -}} diff --git a/helm/temporal-worker-controller/templates/manager.yaml b/helm/temporal-worker-controller/templates/manager.yaml index 50f8d7b1..5e45e777 100644 --- a/helm/temporal-worker-controller/templates/manager.yaml +++ b/helm/temporal-worker-controller/templates/manager.yaml @@ -64,6 +64,10 @@ spec: value: "{{ .Release.Name }}/{{ .Release.Namespace }}" - name: CONTROLLER_VERSION value: "{{ .Values.image.tag | default .Chart.AppVersion }}" + {{- with .Values.watchNamespace }} + - name: WATCH_NAMESPACE + value: {{ . | quote }} + {{- end }} args: - --leader-elect {{- if .Values.metrics.enabled }} diff --git a/helm/temporal-worker-controller/templates/rbac.yaml b/helm/temporal-worker-controller/templates/rbac.yaml index 94b19079..c0d23124 100644 --- a/helm/temporal-worker-controller/templates/rbac.yaml +++ b/helm/temporal-worker-controller/templates/rbac.yaml @@ -59,9 +59,12 @@ subjects: namespace: {{ .Release.Namespace }} --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: {{ include "temporal-worker-controller.rbac.roleKind" . }} metadata: name: {{ .Release.Name }}-{{ .Release.Namespace }}-manager-role + {{- if .Values.rbac.ownNamespace }} + namespace: {{ .Release.Namespace }} + {{- end }} rules: - apiGroups: - "" @@ -125,15 +128,18 @@ rules: - update --- apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: {{ include "temporal-worker-controller.rbac.roleBindingKind" . }} metadata: labels: app.kubernetes.io/component: rbac {{- include "temporal-worker-controller.labels" . | nindent 4 }} name: {{ .Release.Name }}-{{ .Release.Namespace }}-manager-rolebinding + {{- if .Values.rbac.ownNamespace }} + namespace: {{ .Release.Namespace }} + {{- end }} roleRef: apiGroup: rbac.authorization.k8s.io - kind: ClusterRole + kind: {{ include "temporal-worker-controller.rbac.roleKind" . }} name: {{ .Release.Name }}-{{ .Release.Namespace }}-manager-role subjects: - kind: ServiceAccount @@ -142,12 +148,15 @@ subjects: --- # permissions for end users to edit temporalconnections. apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: {{ include "temporal-worker-controller.rbac.roleKind" . }} metadata: labels: app.kubernetes.io/component: rbac {{- include "temporal-worker-controller.labels" . | nindent 4 }} name: {{ .Release.Name }}-{{ .Release.Namespace }}-temporalconnection-editor-role + {{- if .Values.rbac.ownNamespace }} + namespace: {{ .Release.Namespace }} + {{- end }} rules: - apiGroups: - temporal.io.temporal.io @@ -170,15 +179,18 @@ rules: --- # permissions for end users to view temporalconnections. apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: {{ include "temporal-worker-controller.rbac.roleKind" . }} metadata: labels: app.kubernetes.io/component: rbac {{- include "temporal-worker-controller.labels" . | nindent 4 }} name: {{ .Release.Name }}-{{ .Release.Namespace }}-temporalconnection-viewer-role + {{- if .Values.rbac.ownNamespace }} + namespace: {{ .Release.Namespace }} + {{- end }} rules: - apiGroups: - - temporal.io.temporal.io + - temporal.io resources: - temporalconnections verbs: @@ -186,7 +198,7 @@ rules: - list - watch - apiGroups: - - temporal.io.temporal.io + - temporal.io resources: - temporalconnections/status verbs: @@ -194,12 +206,15 @@ rules: --- # permissions for end users to edit temporalworkerdeployments. apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: {{ include "temporal-worker-controller.rbac.roleKind" . }} metadata: labels: app.kubernetes.io/component: rbac {{- include "temporal-worker-controller.labels" . | nindent 4 }} name: {{ .Release.Name }}-{{ .Release.Namespace }}-temporalworkerdeployment-editor-role + {{- if .Values.rbac.ownNamespace }} + namespace: {{ .Release.Namespace }} + {{- end }} rules: - apiGroups: - temporal.io @@ -222,12 +237,15 @@ rules: --- # permissions for end users to view temporalworkerdeployments. apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole +kind: {{ include "temporal-worker-controller.rbac.roleKind" . }} metadata: labels: app.kubernetes.io/component: rbac {{- include "temporal-worker-controller.labels" . | nindent 4 }} name: {{ .Release.Name }}-{{ .Release.Namespace }}-temporalworkerdeployment-viewer-role + {{- if .Values.rbac.ownNamespace }} + namespace: {{ .Release.Namespace }} + {{- end }} rules: - apiGroups: - temporal.io diff --git a/helm/temporal-worker-controller/values.yaml b/helm/temporal-worker-controller/values.yaml index 765321dd..a6491d2c 100644 --- a/helm/temporal-worker-controller/values.yaml +++ b/helm/temporal-worker-controller/values.yaml @@ -33,6 +33,16 @@ terminationGracePeriodSeconds: 10 rbac: # Specifies whether RBAC resources should be created create: true + # Specifies whether the RBAC resources should be namespace-scoped or not + # Set to true to create Roles and RoleBindings in the release namespace instead of ClusterRoles and ClusterRoleBindings + ownNamespace: false + +# Comma-separated list of namespaces the controller should watch. +# Examples: +# watchNamespace: "foo" +# watchNamespace: "foo,bar" +# If empty, the controller watches all namespaces (cluster-wide). +watchNamespace: "" serviceAccount: # Specifies whether a ServiceAccount should be created