Wire bp-openclaw to the per-tenant Keycloak realm (OIDC SSO) and the per-tenant NewAPI (OpenAI-compatible LLM endpoint, NOT direct OpenAI), delivering C3 of umbrella epic #915. Chart changes (bp-openclaw 0.1.0 → 0.2.0): - Add canonical `oidc.{issuerURL,clientId,clientSecret.{name,key}}` block. - Add canonical `llm.{baseURL,apiKey.{name,key},defaultModel}` block. - Controller Deployment now emits OIDC_*, LLM_*, OPENAI_API_{BASE,KEY}, LLM_DEFAULT_MODEL envs (legacy KEYCLOAK_*/NEWAPI_BASE_URL_DEFAULT retained for back-compat with current controller image). - Per-user pods carry OPENAI_API_BASE / OPENAI_API_KEY / LLM_DEFAULT_MODEL alongside the identity-blind NEWAPI_BASE_URL / NEWAPI_KEY (ADR-0003 §3.3 unchanged). - Legacy `keycloak.*` / `newapi.*` keys remain accepted as fallbacks; helpers prefer canonical blocks but fall back to the legacy alias when the canonical block is unset (or still at placeholder). - assertNoPlaceholders guard updated to check resolved canonical values. - render-toggles.sh smoke test extended: asserts both canonical and legacy code-paths render and that all expected envs reach the rendered Deployment. Orchestrator changes (catalyst-api smeTenantBPOpenClaw template): - Emit per-tenant `oidc.issuerURL` = https://keycloak.<sub>.<parent>/realms/sme-<sub> - Emit per-tenant `oidc.clientId` = openclaw, secret from openclaw-oidc-client-secret/OIDC_CLIENT_SECRET (rendered by bp-keycloak's post-install hook). - Emit per-tenant `llm.baseURL` = https://api.<sub>.<parent>/v1 (alice's own NewAPI ingress, NOT the otech-wide newapi.<otech-fqdn>); apiKey from openclaw-newapi-controller-token/NEWAPI_KEY. - Emit `llm.defaultModel: qwen3.6` — NewAPI uses this to select the backing channel; C4 of #915 wires Qwen3.6@BankDhofar at tenant-create. - Legacy keycloak/newapi blocks still emitted for back-compat with bp-openclaw < 0.2.0. Tests: - New TestRenderSMETenantOverlay_OpenClawOIDCAndLLMBlocks asserts the rendered HelmRelease contains the canonical oidc + llm blocks with per-tenant values, and that llm.baseURL is the per-tenant api.<sub>.<parent>/v1 (NOT the otech-wide newapi). - bp-openclaw render-toggles.sh extended (Case 2b/2c). Co-authored-by: alierenbaysal <alierenbaysal@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
98 lines
3.9 KiB
YAML
98 lines
3.9 KiB
YAML
{{- if .Values.controller.enabled }}
|
|
{{- $tenantNs := include "bp-openclaw.tenantNamespace" . -}}
|
|
# ─── Per-user pod template (consumed by the controller) ──────────────────
|
|
# This ConfigMap holds the pod-spec template the controller renders per
|
|
# session. The controller reads it from /etc/openclaw/pod-template.yaml,
|
|
# substitutes ${USER_UUID} and ${SECRET_NAME}, and submits the resulting
|
|
# Pod to the K8s api-server.
|
|
#
|
|
# Substitution variables (filled by the controller at session-start):
|
|
# ${USER_UUID} — the SME user UUID (= JWT `sub` claim)
|
|
# ${SECRET_NAME} — `newapi-key-${USER_UUID}` (per ADR-0003 §3.3)
|
|
#
|
|
# Per [A] of #795: the runtime image reads ONLY two env vars
|
|
# (NEWAPI_BASE_URL + NEWAPI_KEY). It carries NO Keycloak code, NO key-
|
|
# management code, NO knowledge of the SME tenant model. Identity-blind
|
|
# by construction.
|
|
apiVersion: v1
|
|
kind: ConfigMap
|
|
metadata:
|
|
name: {{ include "bp-openclaw.podTemplateConfigMapName" . }}
|
|
labels:
|
|
{{- include "bp-openclaw.labels" . | nindent 4 }}
|
|
data:
|
|
pod-template.yaml: |
|
|
apiVersion: v1
|
|
kind: Pod
|
|
metadata:
|
|
# The controller post-processes this name to be unique-per-session
|
|
# (e.g. openclaw-user-${USER_UUID}-${SESSION_ID}).
|
|
name: openclaw-user-${USER_UUID}
|
|
namespace: {{ $tenantNs }}
|
|
labels:
|
|
app.kubernetes.io/name: {{ include "bp-openclaw.name" . }}
|
|
app.kubernetes.io/component: per-user-pod
|
|
catalyst.openova.io/blueprint: bp-openclaw
|
|
catalyst.openova.io/openclaw-user: ${USER_UUID}
|
|
# Ownership label — the controller's RBAC scope (Role rules 2/3
|
|
# in controller-rbac.yaml) is enforced at the application layer
|
|
# by selecting only pods that carry this label.
|
|
spec:
|
|
restartPolicy: Never
|
|
automountServiceAccountToken: false
|
|
securityContext:
|
|
{{- toYaml .Values.perUserPod.securityContext | nindent 8 }}
|
|
containers:
|
|
- name: runtime
|
|
image: "{{ .Values.perUserPod.image.repository }}:{{ .Values.perUserPod.image.tag }}"
|
|
imagePullPolicy: {{ .Values.perUserPod.image.pullPolicy }}
|
|
env:
|
|
# NewAPI gateway — per-user Secret (ADR-0003 §3.3). The
|
|
# runtime is identity-blind: it reads only these env vars.
|
|
- name: NEWAPI_BASE_URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: ${SECRET_NAME}
|
|
key: base-url
|
|
- name: NEWAPI_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: ${SECRET_NAME}
|
|
key: api-key
|
|
# OpenAI-compatible aliases (any pre-built OpenAI SDK in the
|
|
# runtime image picks these up without code changes).
|
|
- name: OPENAI_API_BASE
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: ${SECRET_NAME}
|
|
key: base-url
|
|
- name: OPENAI_API_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: ${SECRET_NAME}
|
|
key: api-key
|
|
# Default model identifier sent by the runtime when the
|
|
# client doesn't override. NewAPI uses the model name to
|
|
# select a channel; for #915 DoD this is "qwen3.6" and C4
|
|
# wires the BankDhofar channel routing.
|
|
- name: LLM_DEFAULT_MODEL
|
|
value: {{ include "bp-openclaw.llm.defaultModel" . | quote }}
|
|
{{- range .Values.perUserPod.extraEnv }}
|
|
- {{- toYaml . | nindent 14 }}
|
|
{{- end }}
|
|
resources:
|
|
{{- toYaml .Values.perUserPod.resources | nindent 12 }}
|
|
volumeMounts:
|
|
- name: workspace
|
|
mountPath: /workspace
|
|
- name: tmp
|
|
mountPath: /tmp
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir:
|
|
sizeLimit: 1Gi
|
|
- name: tmp
|
|
emptyDir:
|
|
sizeLimit: 256Mi
|
|
{{- end }}
|