d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
683 lines
26 KiB
Smarty
683 lines
26 KiB
Smarty
{{/*
|
|
Expand the name of the chart.
|
|
*/}}
|
|
{{- define "sim.name" -}}
|
|
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Create a default fully qualified app name.
|
|
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
|
If release name contains chart name it will be used as a full name.
|
|
*/}}
|
|
{{- define "sim.fullname" -}}
|
|
{{- if .Values.fullnameOverride }}
|
|
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
|
{{- else }}
|
|
{{- $name := default .Chart.Name .Values.nameOverride }}
|
|
{{- if contains $name .Release.Name }}
|
|
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
|
{{- else }}
|
|
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Create chart name and version as used by the chart label.
|
|
*/}}
|
|
{{- define "sim.chart" -}}
|
|
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Common labels
|
|
*/}}
|
|
{{- define "sim.labels" -}}
|
|
helm.sh/chart: {{ include "sim.chart" . }}
|
|
{{ include "sim.selectorLabels" . }}
|
|
{{- if .Chart.AppVersion }}
|
|
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
|
{{- end }}
|
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
{{- with .Values.global.commonLabels }}
|
|
{{ toYaml . }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Selector labels
|
|
*/}}
|
|
{{- define "sim.selectorLabels" -}}
|
|
app.kubernetes.io/name: {{ include "sim.name" . }}
|
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
App specific labels
|
|
*/}}
|
|
{{- define "sim.app.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: app
|
|
{{- end }}
|
|
|
|
{{/*
|
|
App selector labels
|
|
*/}}
|
|
{{- define "sim.app.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: app
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Realtime specific labels
|
|
*/}}
|
|
{{- define "sim.realtime.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: realtime
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Realtime selector labels
|
|
*/}}
|
|
{{- define "sim.realtime.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: realtime
|
|
{{- end }}
|
|
|
|
{{/*
|
|
PostgreSQL specific labels
|
|
*/}}
|
|
{{- define "sim.postgresql.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: postgresql
|
|
{{- end }}
|
|
|
|
{{/*
|
|
PostgreSQL selector labels
|
|
*/}}
|
|
{{- define "sim.postgresql.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: postgresql
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Ollama specific labels
|
|
*/}}
|
|
{{- define "sim.ollama.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: ollama
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Ollama selector labels
|
|
*/}}
|
|
{{- define "sim.ollama.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: ollama
|
|
{{- end }}
|
|
|
|
{{/*
|
|
PII (Presidio) specific labels
|
|
*/}}
|
|
{{- define "sim.pii.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: pii
|
|
{{- end }}
|
|
|
|
{{/*
|
|
PII (Presidio) selector labels
|
|
*/}}
|
|
{{- define "sim.pii.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: pii
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Migrations specific labels
|
|
*/}}
|
|
{{- define "sim.migrations.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: migrations
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot specific labels
|
|
*/}}
|
|
{{- define "sim.copilot.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: copilot
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot selector labels
|
|
*/}}
|
|
{{- define "sim.copilot.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: copilot
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot PostgreSQL specific labels
|
|
*/}}
|
|
{{- define "sim.copilotPostgresql.labels" -}}
|
|
{{ include "sim.labels" . }}
|
|
app.kubernetes.io/component: copilot-postgresql
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot PostgreSQL selector labels
|
|
*/}}
|
|
{{- define "sim.copilotPostgresql.selectorLabels" -}}
|
|
{{ include "sim.selectorLabels" . }}
|
|
app.kubernetes.io/component: copilot-postgresql
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Create the name of the service account to use
|
|
*/}}
|
|
{{- define "sim.serviceAccountName" -}}
|
|
{{- if .Values.serviceAccount.create }}
|
|
{{- default (include "sim.fullname" .) .Values.serviceAccount.name }}
|
|
{{- else }}
|
|
{{- default "default" .Values.serviceAccount.name }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Create image name with optional registry and digest pinning.
|
|
Accepts a context dict with:
|
|
imageRoot — the image object (repository, optional tag, optional digest, pullPolicy)
|
|
global — .Values.global (for imageRegistry and useRegistryForAllImages)
|
|
chartAppVersion — .Chart.AppVersion (used as default tag when imageRoot.tag is empty)
|
|
|
|
Resolution order:
|
|
1. If imageRoot.digest is set, render "<registry?/>repo@<digest>"
|
|
2. Else render "<registry?/>repo:<tag>" where tag defaults to chartAppVersion
|
|
Usage: {{ include "sim.image" (dict "imageRoot" .Values.app.image "global" .Values.global "chartAppVersion" .Chart.AppVersion) }}
|
|
*/}}
|
|
{{- define "sim.image" -}}
|
|
{{- $imageRoot := .imageRoot -}}
|
|
{{- $global := .global -}}
|
|
{{- $repository := $imageRoot.repository -}}
|
|
{{- $digest := $imageRoot.digest | default "" -}}
|
|
{{- $tag := $imageRoot.tag | default "" | toString -}}
|
|
{{- if and (eq $tag "") (eq $digest "") -}}
|
|
{{- $tag = .chartAppVersion | default "" | toString -}}
|
|
{{- end -}}
|
|
{{- $registry := "" -}}
|
|
{{- if and $global $global.imageRegistry -}}
|
|
{{- if or (hasPrefix "simstudioai/" $repository) $global.useRegistryForAllImages -}}
|
|
{{- $registry = $global.imageRegistry -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- $repoPath := $repository -}}
|
|
{{- if $registry -}}
|
|
{{- $repoPath = printf "%s/%s" $registry $repository -}}
|
|
{{- end -}}
|
|
{{- if ne $digest "" -}}
|
|
{{- printf "%s@%s" $repoPath $digest }}
|
|
{{- else -}}
|
|
{{- $resolvedTag := required (printf "sim.image: no tag or digest resolvable for %q (set imageRoot.tag, imageRoot.digest, or Chart.AppVersion)" $repository) $tag -}}
|
|
{{- printf "%s:%s" $repoPath $resolvedTag }}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Database URL for internal PostgreSQL
|
|
*/}}
|
|
{{- define "sim.databaseUrl" -}}
|
|
{{- if .Values.postgresql.enabled }}
|
|
{{- $host := printf "%s-postgresql" (include "sim.fullname" .) }}
|
|
{{- $port := .Values.postgresql.service.port }}
|
|
{{- $username := .Values.postgresql.auth.username }}
|
|
{{- $database := .Values.postgresql.auth.database }}
|
|
{{- $sslMode := ternary "require" "disable" .Values.postgresql.tls.enabled }}
|
|
{{- printf "postgresql://%s:$(POSTGRES_PASSWORD)@%s:%v/%s?sslmode=%s" $username $host $port $database $sslMode }}
|
|
{{- else if .Values.externalDatabase.enabled }}
|
|
{{- $host := .Values.externalDatabase.host }}
|
|
{{- $port := .Values.externalDatabase.port }}
|
|
{{- $username := .Values.externalDatabase.username }}
|
|
{{- $database := .Values.externalDatabase.database }}
|
|
{{- $sslMode := .Values.externalDatabase.sslMode }}
|
|
{{- printf "postgresql://%s:$(EXTERNAL_DB_PASSWORD)@%s:%v/%s?sslmode=%s" $username $host $port $database $sslMode }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Validate required secrets and reject default placeholder values
|
|
Skip validation when using existing secrets or External Secrets Operator
|
|
*/}}
|
|
{{- define "sim.validateSecrets" -}}
|
|
{{- $useExistingAppSecret := and .Values.app.secrets .Values.app.secrets.existingSecret .Values.app.secrets.existingSecret.enabled }}
|
|
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
|
|
{{- $useExistingPostgresSecret := and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled }}
|
|
{{- $useExistingExternalDbSecret := and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled }}
|
|
{{- /* App secrets validation - skip if using existing secret or ESO */ -}}
|
|
{{- if not (or $useExistingAppSecret $useExternalSecrets) }}
|
|
{{- if and .Values.app.enabled (not .Values.app.env.BETTER_AUTH_SECRET) }}
|
|
{{- fail "app.env.BETTER_AUTH_SECRET is required for production deployment" }}
|
|
{{- end }}
|
|
{{- if and .Values.app.enabled (eq .Values.app.env.BETTER_AUTH_SECRET "CHANGE-ME-32-CHAR-SECRET-FOR-PRODUCTION-USE") }}
|
|
{{- fail "app.env.BETTER_AUTH_SECRET must not use the default placeholder value. Generate a secure secret with: openssl rand -hex 32" }}
|
|
{{- end }}
|
|
{{- if and .Values.app.enabled (not .Values.app.env.ENCRYPTION_KEY) }}
|
|
{{- fail "app.env.ENCRYPTION_KEY is required for production deployment" }}
|
|
{{- end }}
|
|
{{- if and .Values.app.enabled (eq .Values.app.env.ENCRYPTION_KEY "CHANGE-ME-32-CHAR-ENCRYPTION-KEY-FOR-PROD") }}
|
|
{{- fail "app.env.ENCRYPTION_KEY must not use the default placeholder value. Generate a secure key with: openssl rand -hex 32" }}
|
|
{{- end }}
|
|
{{- if and .Values.app.enabled (not .Values.app.env.INTERNAL_API_SECRET) }}
|
|
{{- fail "app.env.INTERNAL_API_SECRET is required for production deployment (shared auth between sim-app and sim-realtime pods). Generate one with: openssl rand -hex 32" }}
|
|
{{- end }}
|
|
{{- if and .Values.realtime.enabled (eq .Values.realtime.env.BETTER_AUTH_SECRET "CHANGE-ME-32-CHAR-SECRET-FOR-PRODUCTION-USE") }}
|
|
{{- fail "realtime.env.BETTER_AUTH_SECRET must not use the default placeholder value. Generate a secure secret with: openssl rand -hex 32" }}
|
|
{{- end }}
|
|
{{- /* CRON_SECRET is required when cronjobs are enabled — pods reference it via secretKeyRef and would fail to start if the key is missing from the Secret. */ -}}
|
|
{{- if and .Values.cronjobs.enabled (not .Values.app.env.CRON_SECRET) }}
|
|
{{- fail "app.env.CRON_SECRET is required when cronjobs.enabled=true (every cron pod authenticates with this token). Generate one with: openssl rand -hex 32, or set cronjobs.enabled=false." }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- /* PostgreSQL password validation - skip if using existing secret or ESO */ -}}
|
|
{{- if not (or $useExistingPostgresSecret $useExternalSecrets) }}
|
|
{{- if and .Values.postgresql.enabled (not .Values.postgresql.auth.password) }}
|
|
{{- fail "postgresql.auth.password is required when using internal PostgreSQL" }}
|
|
{{- end }}
|
|
{{- if and .Values.postgresql.enabled (eq .Values.postgresql.auth.password "CHANGE-ME-SECURE-PASSWORD") }}
|
|
{{- fail "postgresql.auth.password must not use the default placeholder value. Set a secure password for production" }}
|
|
{{- end }}
|
|
{{- if and .Values.postgresql.enabled .Values.postgresql.auth.password (not (regexMatch "^[a-zA-Z0-9._-]+$" .Values.postgresql.auth.password)) }}
|
|
{{- fail "postgresql.auth.password must only contain alphanumeric characters, hyphens, underscores, or periods to ensure DATABASE_URL compatibility. Generate with: openssl rand -base64 16 | tr -d '/+='" }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- /* External database password validation - skip if using existing secret or ESO */ -}}
|
|
{{- if not (or $useExistingExternalDbSecret $useExternalSecrets) }}
|
|
{{- if and .Values.externalDatabase.enabled (not .Values.externalDatabase.password) }}
|
|
{{- fail "externalDatabase.password is required when using external database" }}
|
|
{{- end }}
|
|
{{- if and .Values.externalDatabase.enabled .Values.externalDatabase.password (not (regexMatch "^[a-zA-Z0-9._-]+$" .Values.externalDatabase.password)) }}
|
|
{{- fail "externalDatabase.password must only contain alphanumeric characters, hyphens, underscores, or periods to ensure DATABASE_URL compatibility." }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- /* ESO coverage validation - every key set in app.env / realtime.env must be mapped in externalSecrets.remoteRefs.app */ -}}
|
|
{{- include "sim.validateExternalSecretCoverage" . }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Validate that every key set in app.env / realtime.env is also mapped in
|
|
externalSecrets.remoteRefs.app when ESO is enabled. When ESO is on, the
|
|
chart-managed Secret is not rendered — anything not mapped via ESO would
|
|
be silently missing at runtime.
|
|
|
|
Chart-computed keys (DATABASE_URL, SOCKET_SERVER_URL, OLLAMA_URL, PII_URL)
|
|
are exempt because they're inlined on the container, not sourced from the
|
|
Secret.
|
|
|
|
Fail-fast is only safe for ESO because we can introspect remoteRefs at
|
|
template time. For existingSecret we cannot read the user's pre-created
|
|
Secret, so coverage there is documented (values.yaml + README) rather
|
|
than enforced.
|
|
*/}}
|
|
{{- define "sim.validateExternalSecretCoverage" -}}
|
|
{{- if and .Values.externalSecrets .Values.externalSecrets.enabled -}}
|
|
{{- $remoteRefs := default (dict) (default (dict) .Values.externalSecrets.remoteRefs).app -}}
|
|
{{- $chartComputed := list "DATABASE_URL" "SOCKET_SERVER_URL" "OLLAMA_URL" "PII_URL" -}}
|
|
{{- $appEnv := default (dict) .Values.app.env -}}
|
|
{{/*
|
|
Required-key coverage: these are non-optional at runtime. With ESO enabled
|
|
the chart-managed Secret is not rendered, so a missing key surfaces as a
|
|
runtime CrashLoopBackOff with cryptic env errors. Fail at template time
|
|
if the key is neither set in app.env nor mapped via remoteRefs.app.
|
|
*/}}
|
|
{{- if .Values.app.enabled -}}
|
|
{{- $required := list "BETTER_AUTH_SECRET" "ENCRYPTION_KEY" "INTERNAL_API_SECRET" -}}
|
|
{{- if .Values.cronjobs.enabled -}}
|
|
{{- $required = append $required "CRON_SECRET" -}}
|
|
{{- end -}}
|
|
{{- range $key := $required -}}
|
|
{{- $inEnv := index $appEnv $key -}}
|
|
{{- $mapped := index $remoteRefs $key -}}
|
|
{{- if and (or (not $inEnv) (eq (toString $inEnv) "") (eq (toString $inEnv) "<nil>")) (not $mapped) -}}
|
|
{{- fail (printf "Required key '%s' is missing: externalSecrets.enabled=true but the key is neither set in app.env nor mapped in externalSecrets.remoteRefs.app. Map it via externalSecrets.remoteRefs.app.%s='path/in/store' so it is synced into the app Secret." $key $key) }}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- if .Values.app.enabled -}}
|
|
{{- range $key, $value := default (dict) .Values.app.env -}}
|
|
{{- if not (has $key $chartComputed) -}}
|
|
{{- if and (ne (toString $value) "") (ne (toString $value) "<nil>") -}}
|
|
{{- $mapped := index $remoteRefs $key -}}
|
|
{{- if not $mapped -}}
|
|
{{- fail (printf "Key '%s' is set in app.env but externalSecrets.enabled=true and externalSecrets.remoteRefs.app.%s is not configured. When ESO is enabled the chart-managed app Secret is not rendered, so the container would start with no value. Either map it via externalSecrets.remoteRefs.app.%s='path/in/store' or remove it from app.env." $key $key $key) }}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- if .Values.realtime.enabled -}}
|
|
{{- range $key, $value := default (dict) .Values.realtime.env -}}
|
|
{{- if not (has $key $chartComputed) -}}
|
|
{{- if and (ne (toString $value) "") (ne (toString $value) "<nil>") -}}
|
|
{{- $mapped := index $remoteRefs $key -}}
|
|
{{- if not $mapped -}}
|
|
{{- fail (printf "Key '%s' is set in realtime.env but externalSecrets.enabled=true and externalSecrets.remoteRefs.app.%s is not configured. Either map it via externalSecrets.remoteRefs.app.%s='path/in/store' or remove it from realtime.env." $key $key $key) }}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Get the app secrets name
|
|
Returns the name of the secret containing app credentials (auth, encryption keys)
|
|
*/}}
|
|
{{- define "sim.appSecretName" -}}
|
|
{{- if and .Values.app.secrets .Values.app.secrets.existingSecret .Values.app.secrets.existingSecret.enabled -}}
|
|
{{- .Values.app.secrets.existingSecret.name -}}
|
|
{{- else -}}
|
|
{{- printf "%s-app-secrets" (include "sim.fullname" .) -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Get the PostgreSQL secret name
|
|
Returns the name of the secret containing PostgreSQL password
|
|
*/}}
|
|
{{- define "sim.postgresqlSecretName" -}}
|
|
{{- if and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled -}}
|
|
{{- .Values.postgresql.auth.existingSecret.name -}}
|
|
{{- else -}}
|
|
{{- printf "%s-postgresql-secret" (include "sim.fullname" .) -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Get the PostgreSQL password key name
|
|
Returns the key name in the secret that contains the password
|
|
*/}}
|
|
{{- define "sim.postgresqlPasswordKey" -}}
|
|
{{- if and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled -}}
|
|
{{- .Values.postgresql.auth.existingSecret.passwordKey | default "POSTGRES_PASSWORD" -}}
|
|
{{- else -}}
|
|
{{- print "POSTGRES_PASSWORD" -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Get the external database secret name
|
|
Returns the name of the secret containing external database password
|
|
*/}}
|
|
{{- define "sim.externalDbSecretName" -}}
|
|
{{- if and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled -}}
|
|
{{- .Values.externalDatabase.existingSecret.name -}}
|
|
{{- else -}}
|
|
{{- printf "%s-external-db-secret" (include "sim.fullname" .) -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Get the external database password key name
|
|
Returns the key name in the secret that contains the password
|
|
*/}}
|
|
{{- define "sim.externalDbPasswordKey" -}}
|
|
{{- if and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled -}}
|
|
{{- .Values.externalDatabase.existingSecret.passwordKey | default "EXTERNAL_DB_PASSWORD" -}}
|
|
{{- else -}}
|
|
{{- print "EXTERNAL_DB_PASSWORD" -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Check if app secrets should be created by the chart
|
|
Returns true if we should create the app secrets (not using existing or ESO)
|
|
*/}}
|
|
{{- define "sim.createAppSecrets" -}}
|
|
{{- $useExistingAppSecret := and .Values.app.secrets .Values.app.secrets.existingSecret .Values.app.secrets.existingSecret.enabled }}
|
|
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
|
|
{{- if not (or $useExistingAppSecret $useExternalSecrets) -}}
|
|
true
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Check if PostgreSQL secret should be created by the chart
|
|
Returns true if we should create the PostgreSQL secret (not using existing or ESO)
|
|
*/}}
|
|
{{- define "sim.createPostgresqlSecret" -}}
|
|
{{- $useExistingSecret := and .Values.postgresql.auth.existingSecret .Values.postgresql.auth.existingSecret.enabled }}
|
|
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
|
|
{{- if not (or $useExistingSecret $useExternalSecrets) -}}
|
|
true
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Check if external database secret should be created by the chart
|
|
Returns true if we should create the external database secret (not using existing or ESO)
|
|
*/}}
|
|
{{- define "sim.createExternalDbSecret" -}}
|
|
{{- $useExistingSecret := and .Values.externalDatabase.existingSecret .Values.externalDatabase.existingSecret.enabled }}
|
|
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled }}
|
|
{{- if not (or $useExistingSecret $useExternalSecrets) -}}
|
|
true
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Ollama URL
|
|
*/}}
|
|
{{- define "sim.ollamaUrl" -}}
|
|
{{- if .Values.ollama.enabled }}
|
|
{{- $serviceName := printf "%s-ollama" (include "sim.fullname" .) }}
|
|
{{- $port := .Values.ollama.service.port }}
|
|
{{- printf "http://%s:%v" $serviceName $port }}
|
|
{{- else }}
|
|
{{- .Values.app.env.OLLAMA_URL | default "http://localhost:11434" }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
PII (Presidio) service URL
|
|
*/}}
|
|
{{- define "sim.piiUrl" -}}
|
|
{{- if .Values.pii.enabled }}
|
|
{{- $serviceName := printf "%s-pii" (include "sim.fullname" .) }}
|
|
{{- $port := .Values.pii.service.port }}
|
|
{{- printf "http://%s:%v" $serviceName $port }}
|
|
{{- else }}
|
|
{{- .Values.app.env.PII_URL | default "http://localhost:5001" }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Socket Server URL (internal)
|
|
*/}}
|
|
{{- define "sim.socketServerUrl" -}}
|
|
{{- if .Values.realtime.enabled }}
|
|
{{- $serviceName := printf "%s-realtime" (include "sim.fullname" .) }}
|
|
{{- $port := .Values.realtime.service.port }}
|
|
{{- printf "http://%s:%v" $serviceName $port }}
|
|
{{- else }}
|
|
{{- .Values.app.env.SOCKET_SERVER_URL | default "http://localhost:3002" }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Resource limits and requests
|
|
*/}}
|
|
{{- define "sim.resources" -}}
|
|
{{- if .resources }}
|
|
resources:
|
|
{{- if .resources.limits }}
|
|
limits:
|
|
{{- toYaml .resources.limits | nindent 4 }}
|
|
{{- end }}
|
|
{{- if .resources.requests }}
|
|
requests:
|
|
{{- toYaml .resources.requests | nindent 4 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Pod-level security context with Pod Security Standards "restricted" defaults.
|
|
User-supplied `.podSecurityContext` values override defaults (user wins).
|
|
Usage: {{ include "sim.podSecurityContext" .Values.app | nindent 6 }}
|
|
*/}}
|
|
{{- define "sim.podSecurityContext" -}}
|
|
{{- $defaults := dict "runAsNonRoot" true "runAsUser" 1001 "runAsGroup" 1001 "fsGroup" 1001 "seccompProfile" (dict "type" "RuntimeDefault") -}}
|
|
{{- $user := default (dict) .podSecurityContext -}}
|
|
{{- $merged := mergeOverwrite (deepCopy $defaults) $user -}}
|
|
securityContext:
|
|
{{- toYaml $merged | nindent 2 }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Container-level security context with Pod Security Standards "restricted" defaults.
|
|
User-supplied `.securityContext` values override defaults (user wins).
|
|
`readOnlyRootFilesystem` is intentionally NOT defaulted — set it per-workload in values
|
|
when the container can tolerate a read-only root (Next.js writes to `.next/cache`,
|
|
Postgres writes to `/var/lib/postgresql/data`, so they're left writable by default).
|
|
Usage: {{ include "sim.containerSecurityContext" .Values.app | nindent 10 }}
|
|
*/}}
|
|
{{- define "sim.containerSecurityContext" -}}
|
|
{{- $defaults := dict "runAsNonRoot" true "allowPrivilegeEscalation" false "capabilities" (dict "drop" (list "ALL")) "seccompProfile" (dict "type" "RuntimeDefault") -}}
|
|
{{- $user := default (dict) .securityContext -}}
|
|
{{- $merged := mergeOverwrite (deepCopy $defaults) $user -}}
|
|
securityContext:
|
|
{{- toYaml $merged | nindent 2 }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Backwards-compatible alias for container security context.
|
|
*/}}
|
|
{{- define "sim.securityContext" -}}
|
|
{{- include "sim.containerSecurityContext" . }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Node selector
|
|
*/}}
|
|
{{- define "sim.nodeSelector" -}}
|
|
{{- if .nodeSelector }}
|
|
nodeSelector:
|
|
{{- toYaml .nodeSelector | nindent 2 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Tolerations
|
|
*/}}
|
|
{{- define "sim.tolerations" -}}
|
|
{{- if .tolerations }}
|
|
tolerations:
|
|
{{- toYaml .tolerations | nindent 2 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Affinity
|
|
*/}}
|
|
{{- define "sim.affinity" -}}
|
|
{{- if .affinity }}
|
|
affinity:
|
|
{{- toYaml .affinity | nindent 2 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Topology spread constraints — spreads pods across failure domains.
|
|
Pass the per-component spec (.Values.app, .Values.realtime, ...). Users supply
|
|
the full constraint list including labelSelector; pattern mirrors affinity.
|
|
Usage: {{ include "sim.topologySpreadConstraints" .Values.app | nindent 6 }}
|
|
*/}}
|
|
{{- define "sim.topologySpreadConstraints" -}}
|
|
{{- if .topologySpreadConstraints }}
|
|
topologySpreadConstraints:
|
|
{{- toYaml .topologySpreadConstraints | nindent 2 }}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot environment secret name
|
|
*/}}
|
|
{{- define "sim.copilot.envSecretName" -}}
|
|
{{- if and .Values.copilot.server.secret.name (ne .Values.copilot.server.secret.name "") -}}
|
|
{{- .Values.copilot.server.secret.name -}}
|
|
{{- else -}}
|
|
{{- printf "%s-copilot-env" (include "sim.fullname" .) -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot database secret name
|
|
*/}}
|
|
{{- define "sim.copilot.databaseSecretName" -}}
|
|
{{- if .Values.copilot.postgresql.enabled -}}
|
|
{{- printf "%s-copilot-postgresql-secret" (include "sim.fullname" .) -}}
|
|
{{- else if and .Values.copilot.database.existingSecretName (ne .Values.copilot.database.existingSecretName "") -}}
|
|
{{- .Values.copilot.database.existingSecretName -}}
|
|
{{- else -}}
|
|
{{- printf "%s-copilot-database-secret" (include "sim.fullname" .) -}}
|
|
{{- end -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Copilot database secret key
|
|
*/}}
|
|
{{- define "sim.copilot.databaseSecretKey" -}}
|
|
{{- default "DATABASE_URL" .Values.copilot.database.secretKey -}}
|
|
{{- end }}
|
|
|
|
{{/*
|
|
Validate Copilot configuration
|
|
*/}}
|
|
{{- define "sim.copilot.validate" -}}
|
|
{{- if .Values.copilot.enabled -}}
|
|
{{- if and (not .Values.copilot.server.secret.create) (or (not .Values.copilot.server.secret.name) (eq .Values.copilot.server.secret.name "")) -}}
|
|
{{- fail "copilot.server.secret.name must be provided when copilot.server.secret.create=false" -}}
|
|
{{- end -}}
|
|
{{- if .Values.copilot.server.secret.create -}}
|
|
{{- $env := .Values.copilot.server.env -}}
|
|
{{- $useExternalSecrets := and .Values.externalSecrets .Values.externalSecrets.enabled -}}
|
|
{{- $remoteRefs := default (dict) (default (dict) .Values.externalSecrets.remoteRefs).copilot -}}
|
|
{{- $required := list "AGENT_API_DB_ENCRYPTION_KEY" "INTERNAL_API_SECRET" "LICENSE_KEY" "SIM_BASE_URL" "SIM_AGENT_API_KEY" "REDIS_URL" -}}
|
|
{{- range $key := $required -}}
|
|
{{- $inEnv := and $env (index $env $key) (ne (index $env $key) "") -}}
|
|
{{- $mapped := index $remoteRefs $key -}}
|
|
{{- if not (or $inEnv (and $useExternalSecrets $mapped)) -}}
|
|
{{- if $useExternalSecrets -}}
|
|
{{- fail (printf "Required key '%s' is missing: externalSecrets.enabled=true but the key is neither set in copilot.server.env nor mapped in externalSecrets.remoteRefs.copilot. Map it via externalSecrets.remoteRefs.copilot.%s='path/in/store' so it is synced into the copilot Secret." $key $key) -}}
|
|
{{- else -}}
|
|
{{- fail (printf "copilot.server.env.%s is required when copilot is enabled" $key) -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- if $useExternalSecrets -}}
|
|
{{- range $key, $value := $env -}}
|
|
{{- if and (ne (toString $value) "") (ne (toString $value) "<nil>") -}}
|
|
{{- if not (index $remoteRefs $key) -}}
|
|
{{- fail (printf "Key '%s' is set in copilot.server.env but externalSecrets.enabled=true and externalSecrets.remoteRefs.copilot.%s is not configured. When ESO is enabled the chart-managed copilot Secret is not rendered, so the container would start with no value. Either map it via externalSecrets.remoteRefs.copilot.%s='path/in/store' or remove it from copilot.server.env." $key $key $key) -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- $hasOpenAI := or (and $env (ne (default "" (index $env "OPENAI_API_KEY_1")) "")) (and $useExternalSecrets (index $remoteRefs "OPENAI_API_KEY_1")) -}}
|
|
{{- $hasAnthropic := or (and $env (ne (default "" (index $env "ANTHROPIC_API_KEY_1")) "")) (and $useExternalSecrets (index $remoteRefs "ANTHROPIC_API_KEY_1")) -}}
|
|
{{- if not (or $hasOpenAI $hasAnthropic) -}}
|
|
{{- fail "Set at least one of copilot.server.env.OPENAI_API_KEY_1 or copilot.server.env.ANTHROPIC_API_KEY_1 (or map one via externalSecrets.remoteRefs.copilot when externalSecrets.enabled=true)" -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- if .Values.copilot.postgresql.enabled -}}
|
|
{{- if or (not .Values.copilot.postgresql.auth.password) (eq .Values.copilot.postgresql.auth.password "") -}}
|
|
{{- fail "copilot.postgresql.auth.password is required when copilot.postgresql.enabled=true" -}}
|
|
{{- end -}}
|
|
{{- else -}}
|
|
{{- if and (or (not .Values.copilot.database.existingSecretName) (eq .Values.copilot.database.existingSecretName "")) (or (not .Values.copilot.database.url) (eq .Values.copilot.database.url "")) -}}
|
|
{{- fail "Provide copilot.database.existingSecretName or copilot.database.url when copilot.postgresql.enabled=false" -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
{{- end }} |