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..dea2e17bf 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 | default "" | trim) -}} +{{- .cfg.slack.existingSecret | trim -}} +{{- 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..b48a02095 --- /dev/null +++ b/charts/openab/tests/slack-existing-secret_test.yaml @@ -0,0 +1,196 @@ +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 + + # ── 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 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)