diff --git a/helm/kagent-tools/templates/_helpers.tpl b/helm/kagent-tools/templates/_helpers.tpl index 40158ae..f2d27f3 100644 --- a/helm/kagent-tools/templates/_helpers.tpl +++ b/helm/kagent-tools/templates/_helpers.tpl @@ -73,10 +73,29 @@ Service account name: default when useDefaultServiceAccount is true, otherwise t {{- end }} {{/* -Watch namespaces - transforms list of namespaces cached by the controller into comma-separated string -Removes duplicates +Watch namespaces - comma-separated list for controllers that watch a subset of namespaces. +Precedence: controller.watchNamespaces (explicit override) > rbac.namespaces > empty (watch all). */}} {{- define "kagent-tools.watchNamespaces" -}} -{{- $nsSet := dict }} -{{- .Values.controller.watchNamespaces | default list | uniq | join "," }} +{{- $ctrl := index .Values "controller" | default dict -}} +{{- if index $ctrl "watchNamespaces" -}} +{{- index $ctrl "watchNamespaces" | uniq | join "," -}} +{{- else if and .Values.rbac .Values.rbac.namespaces -}} +{{- .Values.rbac.namespaces | uniq | join "," -}} +{{- end -}} +{{- end }} + +{{/* +Guards on the rbac block +*/}} +{{- define "kagent-tools.rbac.validate" -}} +{{- if and .Values.rbac (hasKey .Values.rbac "clusterScoped") -}} +{{- fail "rbac.clusterScoped has been removed. Leave rbac.namespaces empty for cluster-scoped RBAC, or set rbac.namespaces=[, ...] for namespaced RBAC." -}} +{{- end -}} +{{- if and .Values.rbac .Values.rbac.namespaces -}} +{{- $installNs := include "kagent-tools.namespace" . -}} +{{- if not (has $installNs .Values.rbac.namespaces) -}} +{{- fail (printf "rbac.namespaces is set but does not include the install namespace %q" $installNs) -}} +{{- end -}} +{{- end -}} {{- end -}} diff --git a/helm/kagent-tools/templates/clusterrole.yaml b/helm/kagent-tools/templates/clusterrole.yaml index 48b615b..2ddb85d 100644 --- a/helm/kagent-tools/templates/clusterrole.yaml +++ b/helm/kagent-tools/templates/clusterrole.yaml @@ -74,7 +74,7 @@ - apiGroups: ["*"] resources: ["*"] verbs: ["*"] -{{- if .Values.rbac.clusterScoped }} +{{- if not .Values.rbac.namespaces }} - nonResourceURLs: ["*"] verbs: ["*"] {{- end }} @@ -82,23 +82,9 @@ {{- end -}} {{- if and (not .Values.useDefaultServiceAccount) .Values.rbac.create }} -{{- if .Values.rbac.clusterScoped }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - {{- if .Values.rbac.readOnly }} - name: {{ include "kagent-tools.fullname" . }}-read-role - {{- else }} - name: {{ include "kagent-tools.fullname" . }}-cluster-admin-role - {{- end }} - labels: - {{- include "kagent-tools.labels" . | nindent 4 }} -rules: - {{- include "kagent-tools.rules" . | nindent 2 }} - -{{- else }} -{{- $namespaces := .Values.rbac.namespaces | default (list (include "kagent-tools.namespace" .)) }} -{{- range $namespace := $namespaces }} +{{- include "kagent-tools.rbac.validate" . -}} +{{- if .Values.rbac.namespaces }} +{{- range $namespace := (.Values.rbac.namespaces | uniq | sortAlpha) }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role @@ -114,5 +100,19 @@ metadata: rules: {{- include "kagent-tools.rules" $ | nindent 2 }} {{- end }} + +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + {{- if .Values.rbac.readOnly }} + name: {{ include "kagent-tools.fullname" . }}-read-role + {{- else }} + name: {{ include "kagent-tools.fullname" . }}-cluster-admin-role + {{- end }} + labels: + {{- include "kagent-tools.labels" . | nindent 4 }} +rules: + {{- include "kagent-tools.rules" . | nindent 2 }} {{- end }} {{- end }} diff --git a/helm/kagent-tools/templates/clusterrolebinding.yaml b/helm/kagent-tools/templates/clusterrolebinding.yaml index d6671a2..4324043 100644 --- a/helm/kagent-tools/templates/clusterrolebinding.yaml +++ b/helm/kagent-tools/templates/clusterrolebinding.yaml @@ -1,32 +1,8 @@ {{- if and (not .Values.useDefaultServiceAccount) .Values.rbac.create }} +{{- include "kagent-tools.rbac.validate" . -}} -{{- if .Values.rbac.clusterScoped }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - {{- if .Values.rbac.readOnly }} - name: {{ include "kagent-tools.fullname" . }}-read-rolebinding - {{- else }} - name: {{ include "kagent-tools.fullname" . }}-cluster-admin-rolebinding - {{- end }} - labels: - {{- include "kagent-tools.labels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - {{- if .Values.rbac.readOnly }} - name: {{ include "kagent-tools.fullname" . }}-read-role - {{- else }} - name: {{ include "kagent-tools.fullname" . }}-cluster-admin-role - {{- end }} -subjects: -- kind: ServiceAccount - name: {{ include "kagent-tools.fullname" . }} - namespace: {{ include "kagent-tools.namespace" . }} - -{{- else }} -{{- $namespaces := .Values.rbac.namespaces | default (list (include "kagent-tools.namespace" .)) }} -{{- range $namespace := $namespaces }} +{{- if .Values.rbac.namespaces }} +{{- range $namespace := (.Values.rbac.namespaces | uniq | sortAlpha) }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -52,6 +28,30 @@ subjects: name: {{ include "kagent-tools.fullname" $ }} namespace: {{ include "kagent-tools.namespace" $ }} {{- end }} + +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + {{- if .Values.rbac.readOnly }} + name: {{ include "kagent-tools.fullname" . }}-read-rolebinding + {{- else }} + name: {{ include "kagent-tools.fullname" . }}-cluster-admin-rolebinding + {{- end }} + labels: + {{- include "kagent-tools.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + {{- if .Values.rbac.readOnly }} + name: {{ include "kagent-tools.fullname" . }}-read-role + {{- else }} + name: {{ include "kagent-tools.fullname" . }}-cluster-admin-role + {{- end }} +subjects: +- kind: ServiceAccount + name: {{ include "kagent-tools.fullname" . }} + namespace: {{ include "kagent-tools.namespace" . }} {{- end }} {{- end }} diff --git a/helm/kagent-tools/templates/deployment.yaml b/helm/kagent-tools/templates/deployment.yaml index a78779c..c4b4a35 100644 --- a/helm/kagent-tools/templates/deployment.yaml +++ b/helm/kagent-tools/templates/deployment.yaml @@ -106,7 +106,8 @@ spec: containerPort: {{ .Values.tools.metrics.port | default .Values.service.ports.tools.targetPort }} protocol: TCP readinessProbe: - tcpSocket: + httpGet: + path: /health port: http-tools initialDelaySeconds: 15 periodSeconds: 15 diff --git a/helm/kagent-tools/tests/rbac_test.yaml b/helm/kagent-tools/tests/rbac_test.yaml index 15d05ca..41a991e 100644 --- a/helm/kagent-tools/tests/rbac_test.yaml +++ b/helm/kagent-tools/tests/rbac_test.yaml @@ -12,9 +12,10 @@ tests: of: ClusterRoleBinding template: clusterrolebinding.yaml - - it: should render Roles when clusterScoped is false + - it: should render Roles when rbac.namespaces is set set: - rbac.clusterScoped: false + rbac.namespaces: + - NAMESPACE asserts: - isKind: of: Role @@ -31,36 +32,102 @@ tests: value: NAMESPACE template: clusterrolebinding.yaml - - it: should render multiple roles and bindings when namespaces are specified and clusterScoped is false + - it: should render a single role/binding in the listed namespace only (no release-ns fallback) + set: + rbac.namespaces: + - NAMESPACE + asserts: + - hasDocuments: + count: 1 + template: clusterrole.yaml + - equal: + path: metadata.namespace + value: NAMESPACE + template: clusterrole.yaml + - hasDocuments: + count: 1 + template: clusterrolebinding.yaml + - equal: + path: metadata.namespace + value: NAMESPACE + template: clusterrolebinding.yaml + + - it: should render multiple roles and bindings when namespaces are specified set: - rbac.clusterScoped: false rbac.namespaces: - ns1 + - NAMESPACE - ns2 asserts: - hasDocuments: - count: 2 + count: 3 template: clusterrole.yaml - equal: path: metadata.namespace - value: ns1 + value: NAMESPACE template: clusterrole.yaml documentIndex: 0 - equal: path: metadata.namespace - value: ns2 + value: ns1 template: clusterrole.yaml documentIndex: 1 + - equal: + path: metadata.namespace + value: ns2 + template: clusterrole.yaml + documentIndex: 2 - hasDocuments: - count: 2 + count: 3 template: clusterrolebinding.yaml - equal: path: metadata.namespace - value: ns1 + value: NAMESPACE template: clusterrolebinding.yaml documentIndex: 0 + - equal: + path: metadata.namespace + value: ns1 + template: clusterrolebinding.yaml + documentIndex: 1 - equal: path: metadata.namespace value: ns2 template: clusterrolebinding.yaml + documentIndex: 2 + + - it: should fail rendering if the removed rbac.clusterScoped field is set + set: + rbac.clusterScoped: false + template: clusterrolebinding.yaml + asserts: + - failedTemplate: + errorMessage: "rbac.clusterScoped has been removed. Leave rbac.namespaces empty for cluster-scoped RBAC, or set rbac.namespaces=[, ...] for namespaced RBAC." + + - it: should fail rendering if rbac.namespaces is set but does not include the install namespace + set: + rbac.namespaces: + - some-other-ns + template: clusterrolebinding.yaml + asserts: + - failedTemplate: + errorMessage: 'rbac.namespaces is set but does not include the install namespace "NAMESPACE"' + + - it: should accept a custom install namespace when listed in rbac.namespaces + set: + namespaceOverride: my-ns + rbac.namespaces: + - my-ns + - other-ns + template: clusterrole.yaml + asserts: + - hasDocuments: + count: 2 + - equal: + path: metadata.namespace + value: my-ns + documentIndex: 0 + - equal: + path: metadata.namespace + value: other-ns documentIndex: 1 diff --git a/helm/kagent-tools/values.yaml b/helm/kagent-tools/values.yaml index 2246cf9..b398a00 100644 --- a/helm/kagent-tools/values.yaml +++ b/helm/kagent-tools/values.yaml @@ -104,11 +104,9 @@ rbac: # When false, no ClusterRole or ClusterRoleBinding are created. # The ServiceAccount is still created allowing you to attach your own roles externally. create: true - # -- If true, creates ClusterRole and ClusterRoleBinding resources. - # If false, creates Role and RoleBinding resources instead. - clusterScoped: true - # -- When clusterScoped is false, specify additional namespaces to create Roles and RoleBindings in. - # If empty, defaults to the release namespace. + # -- Namespaces in which to create Role and RoleBinding resources. + # If empty (default), the chart creates cluster-scoped ClusterRole and ClusterRoleBinding resources. + # If set, the chart creates a Role + RoleBinding per listed namespace (install namespace must be included). namespaces: [] # When true, deploys a read-only ClusterRole (get, list, watch) instead of cluster-admin. # Pairs well with the --read-only CLI flag which disables write operations at the application layer.