From 995286e24845d2e12b853496dbda0d73ce89dc30 Mon Sep 17 00:00:00 2001 From: yen <5915590+antigenius0910@users.noreply.github.com> Date: Fri, 22 May 2026 03:10:22 -0500 Subject: [PATCH 1/2] feat(openab): add existingSecret support for Slack agent credentials Add `agents..slack.existingSecret` to the openab chart. When set, the chart references the named Kubernetes Secret for SLACK_BOT_TOKEN and SLACK_APP_TOKEN instead of creating a chart-managed Secret from values. Adapts the existingSecret pattern from the openab-telegram chart (#873) to the multi-agent structure of openab, scoped per-agent. Enables ESO/Vault/SealedSecrets workflows where Slack tokens rotate without requiring a Helm re-apply. Behavior: - existingSecret unset: chart creates Secret with slack tokens (unchanged) - existingSecret set, slack-only agent: no chart-managed Secret created - existingSecret set + discord/stt/gateway: chart Secret omits slack keys; deployment references existingSecret for slack envs only (dual-secret) Closes #900 Co-Authored-By: Claude Opus 4.7 (1M context) --- charts/openab/README.md | 1 + charts/openab/templates/_helpers.tpl | 11 ++ charts/openab/templates/deployment.yaml | 8 +- charts/openab/templates/secret.yaml | 6 +- .../tests/slack-existing-secret_test.yaml | 132 ++++++++++++++++++ charts/openab/values.yaml | 8 ++ 6 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 charts/openab/tests/slack-existing-secret_test.yaml diff --git a/charts/openab/README.md b/charts/openab/README.md index 1ef465392..183c79255 100644 --- a/charts/openab/README.md +++ b/charts/openab/README.md @@ -28,6 +28,7 @@ Each agent lives under `agents.`. | `slack.enabled` | Enable the Slack adapter for the agent. | `false` | | `slack.botToken` | Slack Bot User OAuth token. | `""` | | `slack.appToken` | Slack App-Level token for Socket Mode. | `""` | +| `slack.existingSecret` | Name of a pre-existing K8s Secret containing `slack-bot-token` and `slack-app-token`. When set, `botToken`/`appToken` above are ignored and the chart skips creating those keys. Enables External Secrets Operator / Vault / SealedSecrets workflows. | `""` | | `slack.allowedChannels` | Slack channel allowlist. Empty means allow all channels by default. | `[]` | | `slack.allowedUsers` | Slack user allowlist. Empty means allow all users by default. | `[]` | | `nameOverride` | Override this agent's generated resource name. | `""` | diff --git a/charts/openab/templates/_helpers.tpl b/charts/openab/templates/_helpers.tpl index 6aa197764..80600ff04 100644 --- a/charts/openab/templates/_helpers.tpl +++ b/charts/openab/templates/_helpers.tpl @@ -45,6 +45,17 @@ app.kubernetes.io/component: {{ .agent }} {{- end }} {{- end }} +{{/* Secret name to use for Slack credentials. + If existingSecret is set, reference it; otherwise fall back to the chart-managed agent secret. + Call with: dict "ctx" $ "agent" $name "cfg" $cfg */}} +{{- define "openab.slackSecretName" -}} +{{- if and .cfg.slack .cfg.slack.existingSecret -}} +{{- .cfg.slack.existingSecret -}} +{{- else -}} +{{- include "openab.agentFullname" . -}} +{{- end -}} +{{- end }} + {{/* Resolve image: agent-level string override → global default (repository:tag, tag defaults to appVersion). Caveat: "contains :" treats registry ports (e.g. my-registry:5000/img) as tagged. Not an issue for ghcr.io / Docker Hub; revisit if custom registries with ports are needed. */}} diff --git a/charts/openab/templates/deployment.yaml b/charts/openab/templates/deployment.yaml index a47a3e8be..7659c1f71 100644 --- a/charts/openab/templates/deployment.yaml +++ b/charts/openab/templates/deployment.yaml @@ -49,18 +49,18 @@ spec: name: {{ include "openab.agentFullname" $d }} key: discord-bot-token {{- end }} - {{- if and ($cfg.slack).enabled ($cfg.slack).botToken }} + {{- if and ($cfg.slack).enabled (or ($cfg.slack).botToken ($cfg.slack).existingSecret) }} - name: SLACK_BOT_TOKEN valueFrom: secretKeyRef: - name: {{ include "openab.agentFullname" $d }} + name: {{ include "openab.slackSecretName" $d }} key: slack-bot-token {{- end }} - {{- if and ($cfg.slack).enabled ($cfg.slack).appToken }} + {{- if and ($cfg.slack).enabled (or ($cfg.slack).appToken ($cfg.slack).existingSecret) }} - name: SLACK_APP_TOKEN valueFrom: secretKeyRef: - name: {{ include "openab.agentFullname" $d }} + name: {{ include "openab.slackSecretName" $d }} key: slack-app-token {{- end }} {{- if and ($cfg.stt).enabled ($cfg.stt).apiKey }} diff --git a/charts/openab/templates/secret.yaml b/charts/openab/templates/secret.yaml index 4dc5ba871..b30fac2b9 100644 --- a/charts/openab/templates/secret.yaml +++ b/charts/openab/templates/secret.yaml @@ -1,7 +1,7 @@ {{- range $name, $cfg := .Values.agents }} {{- if ne (include "openab.agentEnabled" $cfg) "false" }} {{- $hasDiscord := and (ne (toString ($cfg.discord).enabled) "false") ($cfg.discord).botToken }} -{{- $hasSlack := and ($cfg.slack).enabled (or ($cfg.slack).botToken ($cfg.slack).appToken) }} +{{- $hasSlack := and ($cfg.slack).enabled (or ($cfg.slack).botToken ($cfg.slack).appToken) (not ($cfg.slack).existingSecret) }} {{- $hasStt := and ($cfg.stt).enabled ($cfg.stt).apiKey }} {{- $hasGateway := and ($cfg.gateway).enabled ($cfg.gateway).token }} {{- if or $hasDiscord $hasSlack $hasStt $hasGateway }} @@ -20,10 +20,10 @@ data: {{- if $hasDiscord }} discord-bot-token: {{ $cfg.discord.botToken | b64enc | quote }} {{- end }} - {{- if and ($cfg.slack).enabled ($cfg.slack).botToken }} + {{- if and ($cfg.slack).enabled ($cfg.slack).botToken (not ($cfg.slack).existingSecret) }} slack-bot-token: {{ $cfg.slack.botToken | b64enc | quote }} {{- end }} - {{- if and ($cfg.slack).enabled ($cfg.slack).appToken }} + {{- if and ($cfg.slack).enabled ($cfg.slack).appToken (not ($cfg.slack).existingSecret) }} slack-app-token: {{ $cfg.slack.appToken | b64enc | quote }} {{- end }} {{- if $hasStt }} diff --git a/charts/openab/tests/slack-existing-secret_test.yaml b/charts/openab/tests/slack-existing-secret_test.yaml new file mode 100644 index 000000000..e17b6a641 --- /dev/null +++ b/charts/openab/tests/slack-existing-secret_test.yaml @@ -0,0 +1,132 @@ +suite: Slack existingSecret support +templates: + - templates/secret.yaml + - templates/deployment.yaml + +tests: + # ── Secret rendering ────────────────────────────────────────────────────── + + - it: omits slack-bot-token and slack-app-token from chart Secret when existingSecret is set (slack-only agent renders no Secret at all) + template: templates/secret.yaml + set: + agents.kiro.discord.enabled: true + agents.kiro.discord.botToken: "discord-token" + agents.kiro.slack.enabled: true + agents.kiro.slack.botToken: "xoxb-ignored" + agents.kiro.slack.appToken: "xapp-ignored" + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - notExists: + path: data["slack-bot-token"] + - notExists: + path: data["slack-app-token"] + + - it: skips chart Secret entirely for slack-only agent with existingSecret + template: templates/secret.yaml + set: + agents.kiro.slack.enabled: true + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - hasDocuments: + count: 0 + + - it: still creates chart Secret for non-slack tokens when existingSecret is set + template: templates/secret.yaml + set: + agents.kiro.discord.enabled: true + agents.kiro.discord.botToken: "discord-token" + agents.kiro.slack.enabled: true + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - matchRegex: + path: data["discord-bot-token"] + pattern: '.+' + - notExists: + path: data["slack-bot-token"] + - notExists: + path: data["slack-app-token"] + + - it: renders chart-managed Slack secret keys when existingSecret is unset + template: templates/secret.yaml + set: + agents.kiro.slack.enabled: true + agents.kiro.slack.botToken: "xoxb-test" + agents.kiro.slack.appToken: "xapp-test" + asserts: + - matchRegex: + path: data["slack-bot-token"] + pattern: '.+' + - matchRegex: + path: data["slack-app-token"] + pattern: '.+' + + # ── Deployment env-var rendering ────────────────────────────────────────── + + - it: references existingSecret name in SLACK_BOT_TOKEN secretKeyRef + template: templates/deployment.yaml + set: + agents.kiro.slack.enabled: true + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_BOT_TOKEN + valueFrom: + secretKeyRef: + name: my-slack-creds + key: slack-bot-token + + - it: references existingSecret name in SLACK_APP_TOKEN secretKeyRef + template: templates/deployment.yaml + set: + agents.kiro.slack.enabled: true + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_APP_TOKEN + valueFrom: + secretKeyRef: + name: my-slack-creds + key: slack-app-token + + - it: falls back to chart-managed secret name when existingSecret is unset + template: templates/deployment.yaml + set: + agents.kiro.slack.enabled: true + agents.kiro.slack.botToken: "xoxb-test" + agents.kiro.slack.appToken: "xapp-test" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_BOT_TOKEN + valueFrom: + secretKeyRef: + name: RELEASE-NAME-openab-kiro + key: slack-bot-token + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_APP_TOKEN + valueFrom: + secretKeyRef: + name: RELEASE-NAME-openab-kiro + key: slack-app-token + + - it: omits SLACK env vars when slack is disabled even if existingSecret is set + template: templates/deployment.yaml + set: + agents.kiro.slack.enabled: false + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - notContains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_BOT_TOKEN + valueFrom: + secretKeyRef: + name: my-slack-creds + key: slack-bot-token diff --git a/charts/openab/values.yaml b/charts/openab/values.yaml index 50b659159..43395157e 100644 --- a/charts/openab/values.yaml +++ b/charts/openab/values.yaml @@ -189,6 +189,14 @@ agents: enabled: false botToken: "" # Bot User OAuth Token (xoxb-...) appToken: "" # App-Level Token (xapp-...) for Socket Mode + # Use a pre-existing K8s Secret for slack-bot-token and slack-app-token + # instead of having the chart create one from botToken/appToken. + # Set to the Secret name; the Secret must contain keys: + # slack-bot-token → Bot User OAuth Token (xoxb-...) + # slack-app-token → App-Level Token (xapp-...) + # When set, botToken and appToken above are ignored. Recommended path for + # External Secrets Operator / Vault / SealedSecrets deployments. + existingSecret: "" # allowAllChannels/allowAllUsers: same auto-infer logic as discord allowedChannels: [] # empty + no allowAllChannels → allow all (auto-inferred) allowedUsers: [] # empty + no allowAllUsers → allow all (auto-inferred) From 95e04d56011eb13788ea1300b65ebccca37477d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B6=85=E6=B8=A1=E6=B3=95=E5=B8=AB?= Date: Fri, 22 May 2026 12:59:39 +0000 Subject: [PATCH 2/2] =?UTF-8?q?fix(helm):=20address=20review=20nits=20?= =?UTF-8?q?=E2=80=94=20trim=20existingSecret,=20add=20mixed-adapter=20and?= =?UTF-8?q?=20multi-agent=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Pipe existingSecret through | trim in openab.slackSecretName helper to handle whitespace-only values gracefully - Add mixed-adapter deployment test verifying Discord refs chart-managed Secret while Slack refs existingSecret in the same Deployment - Add multi-agent scoping test confirming agent A's existingSecret does not affect agent B's inline token resolution --- charts/openab/templates/_helpers.tpl | 4 +- .../tests/slack-existing-secret_test.yaml | 64 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/charts/openab/templates/_helpers.tpl b/charts/openab/templates/_helpers.tpl index 80600ff04..dea2e17bf 100644 --- a/charts/openab/templates/_helpers.tpl +++ b/charts/openab/templates/_helpers.tpl @@ -49,8 +49,8 @@ app.kubernetes.io/component: {{ .agent }} If existingSecret is set, reference it; otherwise fall back to the chart-managed agent secret. Call with: dict "ctx" $ "agent" $name "cfg" $cfg */}} {{- define "openab.slackSecretName" -}} -{{- if and .cfg.slack .cfg.slack.existingSecret -}} -{{- .cfg.slack.existingSecret -}} +{{- if and .cfg.slack (.cfg.slack.existingSecret | default "" | trim) -}} +{{- .cfg.slack.existingSecret | trim -}} {{- else -}} {{- include "openab.agentFullname" . -}} {{- end -}} diff --git a/charts/openab/tests/slack-existing-secret_test.yaml b/charts/openab/tests/slack-existing-secret_test.yaml index e17b6a641..b48a02095 100644 --- a/charts/openab/tests/slack-existing-secret_test.yaml +++ b/charts/openab/tests/slack-existing-secret_test.yaml @@ -130,3 +130,67 @@ tests: secretKeyRef: name: my-slack-creds key: slack-bot-token + + # ── Mixed-adapter deployment ────────────────────────────────────────────── + + - it: renders Discord from chart-managed Secret and Slack from existingSecret in same Deployment + template: templates/deployment.yaml + set: + agents.kiro.discord.enabled: true + agents.kiro.discord.botToken: "disc-token" + agents.kiro.slack.enabled: true + agents.kiro.slack.existingSecret: "my-slack-creds" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: DISCORD_BOT_TOKEN + valueFrom: + secretKeyRef: + name: RELEASE-NAME-openab-kiro + key: discord-bot-token + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_BOT_TOKEN + valueFrom: + secretKeyRef: + name: my-slack-creds + key: slack-bot-token + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_APP_TOKEN + valueFrom: + secretKeyRef: + name: my-slack-creds + key: slack-app-token + + # ── Multi-agent scoping ─────────────────────────────────────────────────── + + - it: agent with existingSecret does not affect another agent using inline tokens + template: templates/deployment.yaml + documentIndex: 1 + set: + agents.alpha.slack.enabled: true + agents.alpha.slack.existingSecret: "alpha-ext-creds" + agents.beta.slack.enabled: true + agents.beta.slack.botToken: "xoxb-beta" + agents.beta.slack.appToken: "xapp-beta" + asserts: + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_BOT_TOKEN + valueFrom: + secretKeyRef: + name: RELEASE-NAME-openab-beta + key: slack-bot-token + - contains: + path: spec.template.spec.containers[0].env + content: + name: SLACK_APP_TOKEN + valueFrom: + secretKeyRef: + name: RELEASE-NAME-openab-beta + key: slack-app-token