From deb71f12d47142902894d1476267c52679ef9b1d Mon Sep 17 00:00:00 2001 From: thekkagent Date: Thu, 16 Apr 2026 18:06:05 +0800 Subject: [PATCH 1/7] feat(helm): add pod extensibility controls Add pod/deployment extensibility to the Helm chart, focused on Deployment.spec.template scope: imagePullSecrets (global + per-agent with opt-out), per-agent imagePullPolicy, serviceAccountName, probes, lifecycle, initContainers, sidecars, extraVolumes, extraVolumeMounts, podAnnotations and podLabels with reserved-key protection. Also fixes Test 1-7 to pass botToken after upstream discord.enabled gate change. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/templates/_helpers.tpl | 52 ++++++++++++++++++++++++- charts/openab/templates/deployment.yaml | 42 +++++++++++++++++++- charts/openab/values.yaml | 19 +++++++++ 3 files changed, 109 insertions(+), 4 deletions(-) diff --git a/charts/openab/templates/_helpers.tpl b/charts/openab/templates/_helpers.tpl index 770d557..2cadac7 100644 --- a/charts/openab/templates/_helpers.tpl +++ b/charts/openab/templates/_helpers.tpl @@ -51,9 +51,57 @@ app.kubernetes.io/component: {{ .agent }} {{- end }} {{- end }} -{{/* Resolve imagePullPolicy: global default (per-agent image string has no pullPolicy) */}} +{{/* Resolve imagePullPolicy: per-agent override or global default */}} {{- define "openab.agentImagePullPolicy" -}} -{{- .ctx.Values.image.pullPolicy }} +{{- default .ctx.Values.image.pullPolicy .cfg.imagePullPolicy }} +{{- end }} + +{{/* Resolve imagePullSecrets: per-agent override (if explicitly set, including empty list) or global default */}} +{{- define "openab.agentImagePullSecrets" -}} +{{- $pullSecrets := .ctx.Values.imagePullSecrets -}} +{{- if hasKey .cfg "imagePullSecrets" -}} +{{- $pullSecrets = .cfg.imagePullSecrets -}} +{{- end }} +{{- range $pullSecrets }} +- name: {{ . | quote }} +{{- end }} +{{- end }} + +{{/* Resolve serviceAccountName: per-agent only, empty by default (uses namespace default SA) */}} +{{- define "openab.agentServiceAccountName" -}} +{{- default "" .cfg.serviceAccountName }} +{{- end }} + +{{/* +Pod annotations: global baseline + per-agent override, with reserved +chart-managed annotations (checksum/config) merged last so users cannot +clobber them and produce duplicate YAML keys. +*/}} +{{- define "openab.agentPodAnnotations" -}} +{{- $reserved := dict "checksum/config" (.cfg | toJson | sha256sum) -}} +{{- $annotations := mergeOverwrite (dict) + (.ctx.Values.podAnnotations | default (dict)) + (.cfg.podAnnotations | default (dict)) + $reserved -}} +{{- toYaml $annotations }} +{{- end }} + +{{/* +Pod labels: global baseline + per-agent override, with reserved selector +labels merged last so users cannot hijack them. Hijacking would produce +duplicate YAML keys AND break Deployment→Pod selector matching. +*/}} +{{- define "openab.agentPodLabels" -}} +{{- $reserved := dict + "app.kubernetes.io/name" (include "openab.name" .ctx) + "app.kubernetes.io/instance" .ctx.Release.Name + "app.kubernetes.io/component" .agent +-}} +{{- $labels := mergeOverwrite (dict) + (.ctx.Values.podLabels | default (dict)) + (.cfg.podLabels | default (dict)) + $reserved -}} +{{- toYaml $labels }} {{- end }} {{/* Agent enabled: default true unless explicitly set to false */}} diff --git a/charts/openab/templates/deployment.yaml b/charts/openab/templates/deployment.yaml index 2352ba5..899f3f4 100644 --- a/charts/openab/templates/deployment.yaml +++ b/charts/openab/templates/deployment.yaml @@ -21,14 +21,27 @@ spec: template: metadata: annotations: - checksum/config: {{ $cfg | toJson | sha256sum }} + {{- include "openab.agentPodAnnotations" $d | nindent 8 }} labels: - {{- include "openab.selectorLabels" $d | nindent 8 }} + {{- include "openab.agentPodLabels" $d | nindent 8 }} spec: + {{- $imagePullSecrets := include "openab.agentImagePullSecrets" $d | trim }} + {{- if $imagePullSecrets }} + imagePullSecrets: + {{- $imagePullSecrets | nindent 8 }} + {{- end }} + {{- $serviceAccountName := include "openab.agentServiceAccountName" $d | trim }} + {{- if $serviceAccountName }} + serviceAccountName: {{ $serviceAccountName }} + {{- end }} {{- with $.Values.podSecurityContext }} securityContext: {{- toYaml . | nindent 8 }} {{- end }} + {{- with $cfg.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} containers: - name: openab image: {{ include "openab.agentImage" $d | quote }} @@ -80,6 +93,22 @@ spec: resources: {{- toYaml . | nindent 12 }} {{- end }} + {{- with $cfg.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $cfg.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $cfg.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $cfg.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} volumeMounts: - name: config mountPath: /etc/openab @@ -99,6 +128,12 @@ spec: mountPath: {{ $cfg.workingDir | default "/home/agent" }}/GEMINI.md subPath: AGENTS.md {{- end }} + {{- with $cfg.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with $cfg.sidecars }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- with $cfg.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} @@ -120,5 +155,8 @@ spec: persistentVolumeClaim: claimName: {{ include "openab.agentFullname" $d }} {{- end }} + {{- with $cfg.extraVolumes }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} {{- end }} diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index 1608b78..4e70523 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -4,6 +4,11 @@ image: tag: "" pullPolicy: IfNotPresent +imagePullSecrets: [] + +podAnnotations: {} +podLabels: {} + podSecurityContext: runAsNonRoot: true runAsUser: 1000 @@ -110,6 +115,9 @@ agents: # size: 1Gi # image: "ghcr.io/openabdev/openab-cursor:latest" image: "" + imagePullPolicy: "" + # imagePullSecrets: unset inherits global; set to [] to opt out. + # imagePullSecrets: [] command: kiro-cli args: - acp @@ -164,6 +172,17 @@ agents: # The PVC file is NOT deleted but becomes invisible to the agent. Remove agentsMd to restore. agentsMd: "" resources: {} + serviceAccountName: "" + podAnnotations: {} + podLabels: {} + livenessProbe: {} + readinessProbe: {} + startupProbe: {} + lifecycle: {} + initContainers: [] + sidecars: [] + extraVolumes: [] + extraVolumeMounts: [] nodeSelector: {} tolerations: [] affinity: {} From 58a0b730fcad5c6f0527b33b031adde34ff1193f Mon Sep 17 00:00:00 2001 From: thekkagent Date: Thu, 16 Apr 2026 18:08:16 +0800 Subject: [PATCH 2/7] feat(helm): support valueFrom and other complex env var types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The env map previously only supported simple key-value strings. This adds polymorphic rendering via kindIs "map" so users can use valueFrom (fieldRef, secretKeyRef, configMapKeyRef, etc.) while maintaining full backward compatibility with existing string values. In deployment.yaml, map-typed values render as toYaml (e.g. valueFrom blocks). In configmap.yaml, map-typed values are filtered out since config.toml only supports string env vars — valueFrom is a Kubernetes concept handled at the pod level. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/templates/configmap.yaml | 10 ++++++++-- charts/openab/templates/deployment.yaml | 4 ++++ charts/openab/values.yaml | 9 ++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/charts/openab/templates/configmap.yaml b/charts/openab/templates/configmap.yaml index 84dcb1d..9a842dc 100644 --- a/charts/openab/templates/configmap.yaml +++ b/charts/openab/templates/configmap.yaml @@ -81,8 +81,14 @@ data: command = "{{ $cfg.command }}" args = {{ if $cfg.args }}{{ $cfg.args | toJson }}{{ else }}[]{{ end }} working_dir = "{{ $cfg.workingDir | default "/home/agent" }}" - {{- if $cfg.env }} - env = { {{ $first := true }}{{ range $k, $v := $cfg.env }}{{ if not $first }}, {{ end }}{{ $k }} = "{{ $v }}"{{ $first = false }}{{ end }} } + {{- $stringEnv := dict }} + {{- range $k, $v := $cfg.env }} + {{- if not (kindIs "map" $v) }} + {{- $_ := set $stringEnv $k $v }} + {{- end }} + {{- end }} + {{- if $stringEnv }} + env = { {{ $first := true }}{{ range $k, $v := $stringEnv }}{{ if not $first }}, {{ end }}{{ $k }} = "{{ $v }}"{{ $first = false }}{{ end }} } {{- end }} [pool] diff --git a/charts/openab/templates/deployment.yaml b/charts/openab/templates/deployment.yaml index 899f3f4..87e9065 100644 --- a/charts/openab/templates/deployment.yaml +++ b/charts/openab/templates/deployment.yaml @@ -83,7 +83,11 @@ spec: value: {{ $cfg.workingDir | default "/home/agent" }} {{- range $k, $v := $cfg.env }} - name: {{ $k }} + {{- if kindIs "map" $v }} + {{- toYaml $v | nindent 14 }} + {{- else }} value: {{ $v | quote }} + {{- end }} {{- end }} {{- with $cfg.envFrom }} envFrom: diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index 4e70523..4f25a3a 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -40,7 +40,14 @@ agents: # # trustedBotIds: [] # empty = any bot (mode permitting) # trustedBotIds: [] # workingDir: /home/agent - # env: {} + # env: + # # simple key-value + # MY_VAR: "hello" + # # valueFrom (fieldRef, secretKeyRef, configMapKeyRef, etc.) + # MY_POD_NAME: + # valueFrom: + # fieldRef: + # fieldPath: metadata.name # envFrom: [] # pool: # maxSessions: 10 From c0bd68f19eb7853b5a03ae56d1fc6937e68d605f Mon Sep 17 00:00:00 2001 From: thekkagent Date: Thu, 16 Apr 2026 18:09:02 +0800 Subject: [PATCH 3/7] refactor(helm): rename sidecars to extraContainers, add value comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename `sidecars` to `extraContainers` to avoid confusion with K8s 1.28+ native sidecar support — this field renders into the main containers array, not as native sidecars. Also adds a "Pod extensibility" comment header and documents that `{}` means disabled for probes and lifecycle fields. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/templates/deployment.yaml | 2 +- charts/openab/values.yaml | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/charts/openab/templates/deployment.yaml b/charts/openab/templates/deployment.yaml index 87e9065..0e7b18c 100644 --- a/charts/openab/templates/deployment.yaml +++ b/charts/openab/templates/deployment.yaml @@ -135,7 +135,7 @@ spec: {{- with $cfg.extraVolumeMounts }} {{- toYaml . | nindent 12 }} {{- end }} - {{- with $cfg.sidecars }} + {{- with $cfg.extraContainers }} {{- toYaml . | nindent 8 }} {{- end }} {{- with $cfg.nodeSelector }} diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index 4f25a3a..92c0957 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -179,15 +179,16 @@ agents: # The PVC file is NOT deleted but becomes invisible to the agent. Remove agentsMd to restore. agentsMd: "" resources: {} + # -- Pod extensibility serviceAccountName: "" podAnnotations: {} podLabels: {} - livenessProbe: {} - readinessProbe: {} - startupProbe: {} - lifecycle: {} + livenessProbe: {} # {} = disabled + readinessProbe: {} # {} = disabled + startupProbe: {} # {} = disabled + lifecycle: {} # {} = disabled initContainers: [] - sidecars: [] + extraContainers: [] extraVolumes: [] extraVolumeMounts: [] nodeSelector: {} From f6ad77c29f08e162c60a5b01e68f5d8ee9b232d7 Mon Sep 17 00:00:00 2001 From: thekkagent Date: Thu, 16 Apr 2026 18:45:39 +0800 Subject: [PATCH 4/7] fix(helm): escape TOML env values, reject list env vars, support K8s imagePullSecrets format - Fix pre-existing TOML injection risk where double quotes in env string values produced invalid TOML. Use toJson for proper escaping. - Add kindIs "slice" guard in both deployment.yaml and configmap.yaml to reject list-typed env values with a clear error message. - Derive agentPodLabels reserved keys from selectorLabels helper to prevent future drift between Deployment selector and pod labels. - Support both K8s native object format ({ name: regcred }) and shorthand string format for imagePullSecrets. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/templates/_helpers.tpl | 10 +++++----- charts/openab/templates/configmap.yaml | 6 ++++-- charts/openab/templates/deployment.yaml | 3 +++ charts/openab/values.yaml | 3 +++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/charts/openab/templates/_helpers.tpl b/charts/openab/templates/_helpers.tpl index 2cadac7..3fac016 100644 --- a/charts/openab/templates/_helpers.tpl +++ b/charts/openab/templates/_helpers.tpl @@ -63,9 +63,13 @@ app.kubernetes.io/component: {{ .agent }} {{- $pullSecrets = .cfg.imagePullSecrets -}} {{- end }} {{- range $pullSecrets }} +{{- if kindIs "map" . }} +- name: {{ .name | quote }} +{{- else }} - name: {{ . | quote }} {{- end }} {{- end }} +{{- end }} {{/* Resolve serviceAccountName: per-agent only, empty by default (uses namespace default SA) */}} {{- define "openab.agentServiceAccountName" -}} @@ -92,11 +96,7 @@ labels merged last so users cannot hijack them. Hijacking would produce duplicate YAML keys AND break Deployment→Pod selector matching. */}} {{- define "openab.agentPodLabels" -}} -{{- $reserved := dict - "app.kubernetes.io/name" (include "openab.name" .ctx) - "app.kubernetes.io/instance" .ctx.Release.Name - "app.kubernetes.io/component" .agent --}} +{{- $reserved := include "openab.selectorLabels" . | fromYaml -}} {{- $labels := mergeOverwrite (dict) (.ctx.Values.podLabels | default (dict)) (.cfg.podLabels | default (dict)) diff --git a/charts/openab/templates/configmap.yaml b/charts/openab/templates/configmap.yaml index 9a842dc..75a539f 100644 --- a/charts/openab/templates/configmap.yaml +++ b/charts/openab/templates/configmap.yaml @@ -83,12 +83,14 @@ data: working_dir = "{{ $cfg.workingDir | default "/home/agent" }}" {{- $stringEnv := dict }} {{- range $k, $v := $cfg.env }} - {{- if not (kindIs "map" $v) }} + {{- if kindIs "slice" $v }} + {{- fail (printf "env.%s is a list — env values must be strings or maps (valueFrom)" $k) }} + {{- else if not (kindIs "map" $v) }} {{- $_ := set $stringEnv $k $v }} {{- end }} {{- end }} {{- if $stringEnv }} - env = { {{ $first := true }}{{ range $k, $v := $stringEnv }}{{ if not $first }}, {{ end }}{{ $k }} = "{{ $v }}"{{ $first = false }}{{ end }} } + env = { {{ $first := true }}{{ range $k, $v := $stringEnv }}{{ if not $first }}, {{ end }}{{ $k }} = {{ $v | toJson }}{{ $first = false }}{{ end }} } {{- end }} [pool] diff --git a/charts/openab/templates/deployment.yaml b/charts/openab/templates/deployment.yaml index 0e7b18c..3575fd6 100644 --- a/charts/openab/templates/deployment.yaml +++ b/charts/openab/templates/deployment.yaml @@ -82,6 +82,9 @@ spec: - name: HOME value: {{ $cfg.workingDir | default "/home/agent" }} {{- range $k, $v := $cfg.env }} + {{- if kindIs "slice" $v }} + {{- fail (printf "env.%s is a list — env values must be strings or maps (valueFrom)" $k) }} + {{- end }} - name: {{ $k }} {{- if kindIs "map" $v }} {{- toYaml $v | nindent 14 }} diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index 92c0957..e83e4b0 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -4,6 +4,9 @@ image: tag: "" pullPolicy: IfNotPresent +# Both formats supported: +# - name: regcred # K8s native format +# - my-secret # shorthand string format imagePullSecrets: [] podAnnotations: {} From 9e1e5aac772d61dfc3487d56e0a8c29333066ba1 Mon Sep 17 00:00:00 2001 From: thekkagent Date: Fri, 17 Apr 2026 10:45:28 +0800 Subject: [PATCH 5/7] test(helm): migrate bash tests to helm unittest YAML format Replace the deleted helm-template-test.sh with helm unittest YAML files: - deployment_test.yaml: pod extensibility controls (18 tests) - env_test.yaml: polymorphic env rendering + configmap filtering (5 tests) Upstream already migrated to helm unittest (configmap_test.yaml, adapter-enablement_test.yaml). Total: 36 tests across 5 suites. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/tests/deployment_test.yaml | 215 +++++++++++++++++++++++ charts/openab/tests/env_test.yaml | 83 +++++++++ 2 files changed, 298 insertions(+) create mode 100644 charts/openab/tests/deployment_test.yaml create mode 100644 charts/openab/tests/env_test.yaml diff --git a/charts/openab/tests/deployment_test.yaml b/charts/openab/tests/deployment_test.yaml new file mode 100644 index 0000000..fdd3e24 --- /dev/null +++ b/charts/openab/tests/deployment_test.yaml @@ -0,0 +1,215 @@ +suite: pod extensibility controls +templates: + - templates/deployment.yaml +tests: + # -- imagePullSecrets -- + + - it: renders global imagePullSecrets (string shorthand) + set: + imagePullSecrets: + - regcred + asserts: + - contains: + path: spec.template.spec.imagePullSecrets + content: + name: "regcred" + + - it: renders global imagePullSecrets (K8s native object format) + set: + imagePullSecrets: + - name: regcred + asserts: + - contains: + path: spec.template.spec.imagePullSecrets + content: + name: "regcred" + + - it: per-agent imagePullSecrets=[] opts out of global + set: + imagePullSecrets: + - global-secret + agents.kiro.imagePullSecrets: [] + asserts: + - isNull: + path: spec.template.spec.imagePullSecrets + + # -- imagePullPolicy -- + + - it: per-agent imagePullPolicy overrides global default + set: + image.pullPolicy: IfNotPresent + agents.kiro.imagePullPolicy: Always + asserts: + - equal: + path: spec.template.spec.containers[0].imagePullPolicy + value: Always + + # -- initContainers -- + + - it: renders initContainers + set: + agents.kiro.initContainers: + - name: setup + image: busybox:1.36 + command: ["sh", "-c", "echo setup"] + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: setup + - equal: + path: spec.template.spec.initContainers[0].image + value: busybox:1.36 + + # -- extraContainers -- + + - it: renders extraContainers into pod spec + set: + agents.kiro.extraContainers: + - name: logtail + image: busybox:1.36 + asserts: + - equal: + path: spec.template.spec.containers[1].name + value: logtail + - equal: + path: spec.template.spec.containers[1].image + value: busybox:1.36 + + # -- extraVolumes / extraVolumeMounts -- + + - it: renders extra volumes and mounts + set: + agents.kiro.extraVolumes: + - name: scratch + emptyDir: {} + agents.kiro.extraVolumeMounts: + - name: scratch + mountPath: /scratch + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: scratch + emptyDir: {} + - contains: + path: spec.template.spec.containers[0].volumeMounts + content: + name: scratch + mountPath: /scratch + + # -- probes, lifecycle, serviceAccountName -- + + - it: renders serviceAccountName + set: + agents.kiro.serviceAccountName: openab-agent + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: openab-agent + + - it: renders livenessProbe + set: + agents.kiro.livenessProbe: + httpGet: + path: /healthz + port: 8080 + asserts: + - equal: + path: spec.template.spec.containers[0].livenessProbe.httpGet.path + value: /healthz + + - it: renders readinessProbe + set: + agents.kiro.readinessProbe: + httpGet: + path: /readyz + port: 8080 + asserts: + - equal: + path: spec.template.spec.containers[0].readinessProbe.httpGet.path + value: /readyz + + - it: renders startupProbe + set: + agents.kiro.startupProbe: + exec: + command: ["pgrep", "openab"] + asserts: + - equal: + path: spec.template.spec.containers[0].startupProbe.exec.command + value: ["pgrep", "openab"] + + - it: renders lifecycle hooks + set: + agents.kiro.lifecycle: + preStop: + exec: + command: ["sh", "-c", "sleep 1"] + asserts: + - equal: + path: spec.template.spec.containers[0].lifecycle.preStop.exec.command + value: ["sh", "-c", "sleep 1"] + + # -- podAnnotations / podLabels -- + + - it: renders global and per-agent pod annotations + set: + podAnnotations: + team: platform + agents.kiro.podAnnotations: + trace: enabled + asserts: + - equal: + path: spec.template.metadata.annotations.team + value: platform + - equal: + path: spec.template.metadata.annotations.trace + value: enabled + + - it: renders global and per-agent pod labels + set: + podLabels: + tier: agents + agents.kiro.podLabels: + agent: kiro + asserts: + - equal: + path: spec.template.metadata.labels.tier + value: agents + - equal: + path: spec.template.metadata.labels.agent + value: kiro + + # -- reserved key protection -- + + - it: podLabels cannot hijack reserved selector labels + set: + agents.kiro.podLabels: + app.kubernetes.io/name: hacked + asserts: + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/name"] + value: openab + + - it: podAnnotations cannot hijack checksum/config + set: + agents.kiro.podAnnotations: + checksum/config: pwned + asserts: + - notEqual: + path: spec.template.metadata.annotations["checksum/config"] + value: pwned + - matchRegex: + path: spec.template.metadata.annotations["checksum/config"] + pattern: "^[a-f0-9]{64}$" + + - it: per-agent podLabels override global for same non-reserved key + set: + podLabels: + tier: GLOBAL + agents.kiro.podLabels: + tier: PERAGENT + asserts: + - equal: + path: spec.template.metadata.labels.tier + value: PERAGENT diff --git a/charts/openab/tests/env_test.yaml b/charts/openab/tests/env_test.yaml new file mode 100644 index 0000000..934a85e --- /dev/null +++ b/charts/openab/tests/env_test.yaml @@ -0,0 +1,83 @@ +suite: env polymorphic rendering +templates: + - templates/deployment.yaml +tests: + - it: renders simple string env as value + set: + agents.kiro.env: + MY_VAR: hello + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: MY_VAR + value: "hello" + + - it: renders valueFrom env (fieldRef) + set: + agents.kiro.env: + MY_POD_NAME: + valueFrom: + fieldRef: + fieldPath: metadata.name + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: MY_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + + - it: renders mixed string and valueFrom env + set: + agents.kiro.env: + SIMPLE_VAR: simple + POD_NAME: + valueFrom: + fieldRef: + fieldPath: metadata.name + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SIMPLE_VAR + value: "simple" + - contains: + path: spec.template.spec.containers[0].env + content: + name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + + - it: rejects list-typed env values + set: + agents.kiro.env: + BAD: + - item1 + - item2 + asserts: + - failedTemplate: + errorPattern: "env.BAD is a list" + +--- +suite: env configmap filtering +templates: + - templates/configmap.yaml +tests: + - it: filters map-typed env from config.toml + set: + agents.kiro.env: + KEEP_THIS: hello + SKIP_THIS: + valueFrom: + fieldRef: + fieldPath: metadata.name + asserts: + - matchRegex: + path: data["config.toml"] + pattern: 'KEEP_THIS = "hello"' + - notMatchRegex: + path: data["config.toml"] + pattern: "SKIP_THIS" From 8dad081a51b97af3dc583c0a0ca4ddf46bf04750 Mon Sep 17 00:00:00 2001 From: thekkagent Date: Fri, 17 Apr 2026 11:13:05 +0800 Subject: [PATCH 6/7] refactor(helm): unify discord.enabled check via openab.discordEnabled helper Before: configmap used strict truthy check (`if ($cfg.discord).enabled`) while deployment and secret used default-true (`ne toString "false"`). This asymmetry came from upstream fix commits and caused different behavior for edge cases across templates. Introduces `openab.discordEnabled` helper following the same pattern as `openab.agentEnabled` and `openab.persistenceEnabled`. Returns "false" when discord config is absent or explicitly disabled, "true" otherwise. All three templates (configmap, deployment, secret) now use the helper for consistent behavior. Also strengthens the reserved-label-hijack test to assert both `app.kubernetes.io/name` and `app.kubernetes.io/component` are protected. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/templates/_helpers.tpl | 5 +++++ charts/openab/templates/configmap.yaml | 2 +- charts/openab/templates/deployment.yaml | 2 +- charts/openab/templates/secret.yaml | 2 +- charts/openab/tests/deployment_test.yaml | 4 ++++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/charts/openab/templates/_helpers.tpl b/charts/openab/templates/_helpers.tpl index 3fac016..92a99a1 100644 --- a/charts/openab/templates/_helpers.tpl +++ b/charts/openab/templates/_helpers.tpl @@ -113,3 +113,8 @@ duplicate YAML keys AND break Deployment→Pod selector matching. {{- define "openab.persistenceEnabled" -}} {{- if and . .persistence (eq (.persistence.enabled | toString) "false") }}false{{ else }}true{{ end }} {{- end }} + +{{/* Discord adapter enabled: default true unless explicitly set to false; returns false when discord config is absent */}} +{{- define "openab.discordEnabled" -}} +{{- if and . .discord (ne (.discord.enabled | toString) "false") }}true{{ else }}false{{ end }} +{{- end }} diff --git a/charts/openab/templates/configmap.yaml b/charts/openab/templates/configmap.yaml index 75a539f..7900042 100644 --- a/charts/openab/templates/configmap.yaml +++ b/charts/openab/templates/configmap.yaml @@ -10,7 +10,7 @@ metadata: {{- include "openab.labels" $d | nindent 4 }} data: config.toml: | - {{- if ($cfg.discord).enabled }} + {{- if ne (include "openab.discordEnabled" $cfg) "false" }} [discord] bot_token = "${DISCORD_BOT_TOKEN}" {{- range $cfg.discord.allowedChannels }} diff --git a/charts/openab/templates/deployment.yaml b/charts/openab/templates/deployment.yaml index 3575fd6..c1bf7da 100644 --- a/charts/openab/templates/deployment.yaml +++ b/charts/openab/templates/deployment.yaml @@ -51,7 +51,7 @@ spec: {{- toYaml . | nindent 12 }} {{- end }} env: - {{- if and (ne (toString ($cfg.discord).enabled) "false") ($cfg.discord).botToken }} + {{- if and (ne (include "openab.discordEnabled" $cfg) "false") ($cfg.discord).botToken }} - name: DISCORD_BOT_TOKEN valueFrom: secretKeyRef: diff --git a/charts/openab/templates/secret.yaml b/charts/openab/templates/secret.yaml index 7af14ae..1f18472 100644 --- a/charts/openab/templates/secret.yaml +++ b/charts/openab/templates/secret.yaml @@ -1,6 +1,6 @@ {{- range $name, $cfg := .Values.agents }} {{- if ne (include "openab.agentEnabled" $cfg) "false" }} -{{- $hasDiscord := and (ne (toString ($cfg.discord).enabled) "false") ($cfg.discord).botToken }} +{{- $hasDiscord := and (ne (include "openab.discordEnabled" $cfg) "false") ($cfg.discord).botToken }} {{- $hasSlack := and ($cfg.slack).enabled (or ($cfg.slack).botToken ($cfg.slack).appToken) }} {{- $hasStt := and ($cfg.stt).enabled ($cfg.stt).apiKey }} {{- if or $hasDiscord $hasSlack $hasStt }} diff --git a/charts/openab/tests/deployment_test.yaml b/charts/openab/tests/deployment_test.yaml index fdd3e24..c07c8b4 100644 --- a/charts/openab/tests/deployment_test.yaml +++ b/charts/openab/tests/deployment_test.yaml @@ -186,10 +186,14 @@ tests: set: agents.kiro.podLabels: app.kubernetes.io/name: hacked + app.kubernetes.io/component: evil asserts: - equal: path: spec.template.metadata.labels["app.kubernetes.io/name"] value: openab + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/component"] + value: kiro - it: podAnnotations cannot hijack checksum/config set: From e657e86c090afedb5ca3542eba200b745118b210 Mon Sep 17 00:00:00 2001 From: thekkagent Date: Fri, 17 Apr 2026 16:45:11 +0800 Subject: [PATCH 7/7] feat(helm): add ServiceAccount, RBAC, and PodDisruptionBudget templates Phase 2 of helm chart extensibility (#397): - ServiceAccount (optional, opt-in via serviceAccount.create) - Supports custom name, annotations (e.g. IRSA), and automountServiceAccountToken - Backward compat: existing serviceAccountName still works for external SAs - Role + RoleBinding (optional, opt-in via rbac.create) - ClusterRole + ClusterRoleBinding (optional, opt-in via rbac.createClusterRole) - PodDisruptionBudget (optional, opt-in via podDisruptionBudget.enabled) - Supports both minAvailable and maxUnavailable (mutually exclusive) - values.yaml warns against minAvailable: 1 with the hardcoded replicas: 1 All resources are per-agent and follow the existing multi-agent loop pattern. All defaults are opt-in (false) to preserve existing behavior. 25 new tests added across 3 suites (serviceaccount, rbac, pdb). Total: 61 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) --- charts/openab/templates/_helpers.tpl | 12 +- charts/openab/templates/clusterrole.yaml | 20 +++ .../openab/templates/clusterrolebinding.yaml | 22 +++ charts/openab/templates/pdb.yaml | 29 ++++ charts/openab/templates/role.yaml | 20 +++ charts/openab/templates/rolebinding.yaml | 22 +++ charts/openab/templates/serviceaccount.yaml | 23 +++ charts/openab/tests/pdb_test.yaml | 56 +++++++ charts/openab/tests/rbac_test.yaml | 140 ++++++++++++++++++ charts/openab/tests/serviceaccount_test.yaml | 87 +++++++++++ charts/openab/values.yaml | 22 +++ 11 files changed, 451 insertions(+), 2 deletions(-) create mode 100644 charts/openab/templates/clusterrole.yaml create mode 100644 charts/openab/templates/clusterrolebinding.yaml create mode 100644 charts/openab/templates/pdb.yaml create mode 100644 charts/openab/templates/role.yaml create mode 100644 charts/openab/templates/rolebinding.yaml create mode 100644 charts/openab/templates/serviceaccount.yaml create mode 100644 charts/openab/tests/pdb_test.yaml create mode 100644 charts/openab/tests/rbac_test.yaml create mode 100644 charts/openab/tests/serviceaccount_test.yaml diff --git a/charts/openab/templates/_helpers.tpl b/charts/openab/templates/_helpers.tpl index 92a99a1..408d0db 100644 --- a/charts/openab/templates/_helpers.tpl +++ b/charts/openab/templates/_helpers.tpl @@ -71,9 +71,17 @@ app.kubernetes.io/component: {{ .agent }} {{- end }} {{- end }} -{{/* Resolve serviceAccountName: per-agent only, empty by default (uses namespace default SA) */}} +{{/* +Resolve serviceAccountName: +- If serviceAccount.create is true: use serviceAccount.name or fallback to +- Else: use serviceAccountName (for referencing externally-created SAs), or empty (namespace default) +*/}} {{- define "openab.agentServiceAccountName" -}} -{{- default "" .cfg.serviceAccountName }} +{{- if (.cfg.serviceAccount).create -}} +{{- default (include "openab.agentFullname" .) .cfg.serviceAccount.name -}} +{{- else -}} +{{- default "" .cfg.serviceAccountName -}} +{{- end -}} {{- end }} {{/* diff --git a/charts/openab/templates/clusterrole.yaml b/charts/openab/templates/clusterrole.yaml new file mode 100644 index 0000000..c895a6e --- /dev/null +++ b/charts/openab/templates/clusterrole.yaml @@ -0,0 +1,20 @@ +{{- range $name, $cfg := .Values.agents }} +{{- if ne (include "openab.agentEnabled" $cfg) "false" }} +{{- if ($cfg.rbac).createClusterRole }} +{{- $d := dict "ctx" $ "agent" $name "cfg" $cfg }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "openab.agentFullname" $d }} + labels: + {{- include "openab.labels" $d | nindent 4 }} +{{- with $cfg.rbac.clusterRules }} +rules: + {{- toYaml . | nindent 2 }} +{{- else }} +rules: [] +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/openab/templates/clusterrolebinding.yaml b/charts/openab/templates/clusterrolebinding.yaml new file mode 100644 index 0000000..d52decb --- /dev/null +++ b/charts/openab/templates/clusterrolebinding.yaml @@ -0,0 +1,22 @@ +{{- range $name, $cfg := .Values.agents }} +{{- if ne (include "openab.agentEnabled" $cfg) "false" }} +{{- if ($cfg.rbac).createClusterRole }} +{{- $d := dict "ctx" $ "agent" $name "cfg" $cfg }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "openab.agentFullname" $d }} + labels: + {{- include "openab.labels" $d | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "openab.agentFullname" $d }} +subjects: + - kind: ServiceAccount + name: {{ include "openab.agentServiceAccountName" $d }} + namespace: {{ $.Release.Namespace }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/openab/templates/pdb.yaml b/charts/openab/templates/pdb.yaml new file mode 100644 index 0000000..f44f825 --- /dev/null +++ b/charts/openab/templates/pdb.yaml @@ -0,0 +1,29 @@ +{{- range $name, $cfg := .Values.agents }} +{{- if ne (include "openab.agentEnabled" $cfg) "false" }} +{{- if ($cfg.podDisruptionBudget).enabled }} +{{- $d := dict "ctx" $ "agent" $name "cfg" $cfg }} +{{- $pdb := $cfg.podDisruptionBudget }} +{{- if and (hasKey $pdb "minAvailable") (hasKey $pdb "maxUnavailable") (ne $pdb.minAvailable nil) (ne $pdb.maxUnavailable nil) }} +{{- fail (printf "agents.%s.podDisruptionBudget: cannot set both minAvailable and maxUnavailable" $name) }} +{{- end }} +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "openab.agentFullname" $d }} + labels: + {{- include "openab.labels" $d | nindent 4 }} +spec: + {{- if ne ($pdb.minAvailable | toString) "" }} + minAvailable: {{ $pdb.minAvailable }} + {{- else if ne ($pdb.maxUnavailable | toString) "" }} + maxUnavailable: {{ $pdb.maxUnavailable }} + {{- else }} + {{- fail (printf "agents.%s.podDisruptionBudget: must set either minAvailable or maxUnavailable" $name) }} + {{- end }} + selector: + matchLabels: + {{- include "openab.selectorLabels" $d | nindent 6 }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/openab/templates/role.yaml b/charts/openab/templates/role.yaml new file mode 100644 index 0000000..8f4441c --- /dev/null +++ b/charts/openab/templates/role.yaml @@ -0,0 +1,20 @@ +{{- range $name, $cfg := .Values.agents }} +{{- if ne (include "openab.agentEnabled" $cfg) "false" }} +{{- if ($cfg.rbac).create }} +{{- $d := dict "ctx" $ "agent" $name "cfg" $cfg }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "openab.agentFullname" $d }} + labels: + {{- include "openab.labels" $d | nindent 4 }} +{{- with $cfg.rbac.rules }} +rules: + {{- toYaml . | nindent 2 }} +{{- else }} +rules: [] +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/openab/templates/rolebinding.yaml b/charts/openab/templates/rolebinding.yaml new file mode 100644 index 0000000..b6a4780 --- /dev/null +++ b/charts/openab/templates/rolebinding.yaml @@ -0,0 +1,22 @@ +{{- range $name, $cfg := .Values.agents }} +{{- if ne (include "openab.agentEnabled" $cfg) "false" }} +{{- if ($cfg.rbac).create }} +{{- $d := dict "ctx" $ "agent" $name "cfg" $cfg }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "openab.agentFullname" $d }} + labels: + {{- include "openab.labels" $d | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "openab.agentFullname" $d }} +subjects: + - kind: ServiceAccount + name: {{ include "openab.agentServiceAccountName" $d }} + namespace: {{ $.Release.Namespace }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/openab/templates/serviceaccount.yaml b/charts/openab/templates/serviceaccount.yaml new file mode 100644 index 0000000..329c8b2 --- /dev/null +++ b/charts/openab/templates/serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- range $name, $cfg := .Values.agents }} +{{- if ne (include "openab.agentEnabled" $cfg) "false" }} +{{- if ($cfg.serviceAccount).create }} +{{- $d := dict "ctx" $ "agent" $name "cfg" $cfg }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "openab.agentServiceAccountName" $d }} + labels: + {{- include "openab.labels" $d | nindent 4 }} + {{- with $cfg.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- if hasKey $cfg.serviceAccount "automountServiceAccountToken" }} +automountServiceAccountToken: {{ $cfg.serviceAccount.automountServiceAccountToken }} +{{- else }} +automountServiceAccountToken: true +{{- end }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/openab/tests/pdb_test.yaml b/charts/openab/tests/pdb_test.yaml new file mode 100644 index 0000000..07bc2e2 --- /dev/null +++ b/charts/openab/tests/pdb_test.yaml @@ -0,0 +1,56 @@ +suite: PodDisruptionBudget +templates: + - templates/pdb.yaml +tests: + - it: does not create PDB by default + asserts: + - hasDocuments: + count: 0 + + - it: creates PDB with minAvailable when enabled + set: + agents.kiro.podDisruptionBudget.enabled: true + agents.kiro.podDisruptionBudget.minAvailable: 1 + asserts: + - isKind: + of: PodDisruptionBudget + - equal: + path: spec.minAvailable + value: 1 + - equal: + path: spec.selector.matchLabels["app.kubernetes.io/component"] + value: kiro + + - it: creates PDB with maxUnavailable + set: + agents.kiro.podDisruptionBudget.enabled: true + agents.kiro.podDisruptionBudget.maxUnavailable: 1 + asserts: + - equal: + path: spec.maxUnavailable + value: 1 + + - it: supports percentage values + set: + agents.kiro.podDisruptionBudget.enabled: true + agents.kiro.podDisruptionBudget.minAvailable: 50% + asserts: + - equal: + path: spec.minAvailable + value: 50% + + - it: fails when both minAvailable and maxUnavailable are set + set: + agents.kiro.podDisruptionBudget.enabled: true + agents.kiro.podDisruptionBudget.minAvailable: 1 + agents.kiro.podDisruptionBudget.maxUnavailable: 1 + asserts: + - failedTemplate: + errorPattern: "cannot set both minAvailable and maxUnavailable" + + - it: fails when neither minAvailable nor maxUnavailable are set + set: + agents.kiro.podDisruptionBudget.enabled: true + asserts: + - failedTemplate: + errorPattern: "must set either minAvailable or maxUnavailable" diff --git a/charts/openab/tests/rbac_test.yaml b/charts/openab/tests/rbac_test.yaml new file mode 100644 index 0000000..9de0817 --- /dev/null +++ b/charts/openab/tests/rbac_test.yaml @@ -0,0 +1,140 @@ +suite: RBAC (Role + ClusterRole) +release: + name: test +templates: + - templates/role.yaml + - templates/rolebinding.yaml + - templates/clusterrole.yaml + - templates/clusterrolebinding.yaml +tests: + - it: creates no RBAC resources by default + asserts: + - hasDocuments: + count: 0 + + - it: creates Role when rbac.create=true + template: templates/role.yaml + set: + agents.kiro.rbac.create: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: Role + + - it: creates RoleBinding when rbac.create=true + template: templates/rolebinding.yaml + set: + agents.kiro.rbac.create: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: RoleBinding + + - it: Role renders rules correctly + template: templates/role.yaml + set: + agents.kiro.rbac.create: true + agents.kiro.rbac.rules: + - apiGroups: [""] + resources: ["configmaps"] + verbs: ["get", "list"] + asserts: + - isKind: + of: Role + - equal: + path: metadata.name + value: test-openab-kiro + - equal: + path: rules[0].apiGroups + value: [""] + - equal: + path: rules[0].resources + value: ["configmaps"] + - equal: + path: rules[0].verbs + value: ["get", "list"] + + - it: Role with empty rules still renders + template: templates/role.yaml + set: + agents.kiro.rbac.create: true + asserts: + - isKind: + of: Role + - equal: + path: rules + value: [] + + - it: RoleBinding references the Role and ServiceAccount + template: templates/rolebinding.yaml + set: + agents.kiro.serviceAccount.create: true + agents.kiro.rbac.create: true + asserts: + - isKind: + of: RoleBinding + - equal: + path: roleRef.kind + value: Role + - equal: + path: roleRef.name + value: test-openab-kiro + - equal: + path: subjects[0].kind + value: ServiceAccount + - equal: + path: subjects[0].name + value: test-openab-kiro + + - it: creates ClusterRole when rbac.createClusterRole=true + template: templates/clusterrole.yaml + set: + agents.kiro.rbac.createClusterRole: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: ClusterRole + + - it: creates ClusterRoleBinding when rbac.createClusterRole=true + template: templates/clusterrolebinding.yaml + set: + agents.kiro.rbac.createClusterRole: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: ClusterRoleBinding + + - it: ClusterRole renders clusterRules correctly + template: templates/clusterrole.yaml + set: + agents.kiro.rbac.createClusterRole: true + agents.kiro.rbac.clusterRules: + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get"] + asserts: + - isKind: + of: ClusterRole + - equal: + path: rules[0].resources + value: ["nodes"] + + - it: rbac.create=false does not create Role + template: templates/role.yaml + set: + agents.kiro.rbac.createClusterRole: true + asserts: + - hasDocuments: + count: 0 + + - it: rbac.createClusterRole=false does not create ClusterRole + template: templates/clusterrole.yaml + set: + agents.kiro.rbac.create: true + asserts: + - hasDocuments: + count: 0 diff --git a/charts/openab/tests/serviceaccount_test.yaml b/charts/openab/tests/serviceaccount_test.yaml new file mode 100644 index 0000000..5d39110 --- /dev/null +++ b/charts/openab/tests/serviceaccount_test.yaml @@ -0,0 +1,87 @@ +suite: ServiceAccount creation +release: + name: test +templates: + - templates/serviceaccount.yaml + - templates/deployment.yaml +tests: + - it: does not create SA by default + template: templates/serviceaccount.yaml + asserts: + - hasDocuments: + count: 0 + + - it: creates SA when serviceAccount.create=true + template: templates/serviceaccount.yaml + set: + agents.kiro.serviceAccount.create: true + asserts: + - hasDocuments: + count: 1 + - isKind: + of: ServiceAccount + - equal: + path: metadata.name + value: test-openab-kiro + - equal: + path: automountServiceAccountToken + value: true + + - it: uses custom SA name when set + template: templates/serviceaccount.yaml + set: + agents.kiro.serviceAccount.create: true + agents.kiro.serviceAccount.name: my-custom-sa + asserts: + - equal: + path: metadata.name + value: my-custom-sa + + - it: renders IRSA annotations + template: templates/serviceaccount.yaml + set: + agents.kiro.serviceAccount.create: true + agents.kiro.serviceAccount.annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::123:role/my-role + asserts: + - equal: + path: metadata.annotations["eks.amazonaws.com/role-arn"] + value: arn:aws:iam::123:role/my-role + + - it: supports disabling automountServiceAccountToken + template: templates/serviceaccount.yaml + set: + agents.kiro.serviceAccount.create: true + agents.kiro.serviceAccount.automountServiceAccountToken: false + asserts: + - equal: + path: automountServiceAccountToken + value: false + + - it: deployment uses created SA name + template: templates/deployment.yaml + set: + agents.kiro.serviceAccount.create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: test-openab-kiro + + - it: deployment still supports external serviceAccountName for backward compat + template: templates/deployment.yaml + set: + agents.kiro.serviceAccountName: external-sa + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: external-sa + + - it: serviceAccount.create=true overrides external serviceAccountName + template: templates/deployment.yaml + set: + agents.kiro.serviceAccountName: external-sa + agents.kiro.serviceAccount.create: true + asserts: + - equal: + path: spec.template.spec.serviceAccountName + value: test-openab-kiro diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index e83e4b0..22de070 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -194,6 +194,28 @@ agents: extraContainers: [] extraVolumes: [] extraVolumeMounts: [] + # -- ServiceAccount, RBAC, PodDisruptionBudget + # serviceAccountName (above) still supported for referencing externally-created SAs. + # When serviceAccount.create is true, the chart creates a new SA named + # (or serviceAccount.name if set), overriding serviceAccountName. + serviceAccount: + create: false + name: "" # empty = use + automountServiceAccountToken: true # set false if agent does not need K8s API access + annotations: {} # e.g. eks.amazonaws.com/role-arn for IRSA + rbac: + # namespaced Role + RoleBinding + create: false + rules: [] + # cluster-scope ClusterRole + ClusterRoleBinding + createClusterRole: false + clusterRules: [] + # ⚠️ PDB with minAvailable: 1 combined with the hardcoded replicas: 1 will prevent + # node drains. Use maxUnavailable: 1 instead if you need drain support. + podDisruptionBudget: + enabled: false + minAvailable: null + maxUnavailable: null nodeSelector: {} tolerations: [] affinity: {}