Files

632 lines
31 KiB
Smarty

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
{{/*
Expand the name of the chart.
*/}}
{{- define "openshell.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "openshell.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 }}
{{/*
Name of the Gateway API Gateway referenced by the GRPCRoute. Preserve explicit
names unchanged because Gateway names may be longer than label values.
*/}}
{{- define "openshell.grpcRouteGatewayName" -}}
{{- default (include "openshell.fullname" .) .Values.grpcRoute.gateway.name -}}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "openshell.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "openshell.labels" -}}
helm.sh/chart: {{ include "openshell.chart" . }}
{{ include "openshell.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "openshell.selectorLabels" -}}
app.kubernetes.io/name: {{ include "openshell.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Pod labels for the certgen hook Jobs. They keep the release instance label but
do not match openshell.selectorLabels, so gateway selectors (the workload,
Services, HorizontalPodAutoscaler, anti-affinity, and PodDisruptionBudgets)
never select hook pods.
*/}}
{{- define "openshell.certgenPodLabels" -}}
app.kubernetes.io/name: {{ printf "%s-certgen" (include "openshell.name" . | trunc 55 | trimSuffix "-") }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/component: certgen
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "openshell.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "openshell.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Create the name of the service account assigned to sandbox pods
*/}}
{{- define "openshell.sandboxServiceAccountName" -}}
{{- if .Values.sandboxServiceAccount.create }}
{{- default (printf "%s-sandbox" (include "openshell.fullname" .) | trunc 63 | trimSuffix "-") .Values.sandboxServiceAccount.name }}
{{- else }}
{{- default "default" .Values.sandboxServiceAccount.name }}
{{- end }}
{{- end }}
{{/*
Whether this chart owns workspace-scoped resources. Missing legacy values
default to enabled so upgrades with --reuse-values preserve the old topology.
*/}}
{{- define "openshell.workspaceResourcesEnabled" -}}
{{- $workspaceResources := .Values.workspaceResources | default dict -}}
{{- $enabled := true -}}
{{- if hasKey $workspaceResources "enabled" -}}
{{- $enabled = get $workspaceResources "enabled" -}}
{{- end -}}
{{- if $enabled -}}true{{- end -}}
{{- end }}
{{/*
Whether this chart owns gateway RBAC objects. Missing legacy values default to
enabled so upgrades with --reuse-values preserve the old topology.
*/}}
{{- define "openshell.rbacCreate" -}}
{{- $rbac := .Values.rbac | default dict -}}
{{- $create := true -}}
{{- if hasKey $rbac "create" -}}
{{- $create = get $rbac "create" -}}
{{- end -}}
{{- if $create -}}true{{- end -}}
{{- end }}
{{/*
The rbac.clusterScoped values map, tolerating missing legacy values.
*/}}
{{- define "openshell.clusterScopedRbacValues" -}}
{{- $rbac := .Values.rbac | default dict -}}
{{- $clusterScoped := dict -}}
{{- if hasKey $rbac "clusterScoped" -}}
{{- $clusterScoped = get $rbac "clusterScoped" | default dict -}}
{{- end -}}
{{- toYaml $clusterScoped -}}
{{- end }}
{{/*
Whether this chart owns the cluster-scoped ClusterRole and ClusterRoleBinding.
Disable for a namespace-admin install where a cluster-admin applies them
separately. Missing legacy values default to enabled.
*/}}
{{- define "openshell.clusterRbacCreate" -}}
{{- if include "openshell.rbacCreate" . -}}
{{- $clusterScoped := include "openshell.clusterScopedRbacValues" . | fromYaml -}}
{{- $create := true -}}
{{- if hasKey $clusterScoped "create" -}}
{{- $create = get $clusterScoped "create" -}}
{{- end -}}
{{- if $create -}}true{{- end -}}
{{- end -}}
{{- end }}
{{/*
Name of the gateway ClusterRole. The release namespace is part of the default
name so multiple releases on one cluster do not collide.
*/}}
{{- define "openshell.clusterRoleName" -}}
{{- $clusterScoped := include "openshell.clusterScopedRbacValues" . | fromYaml -}}
{{- $default := printf "%s-node-reader-%s" (include "openshell.fullname" .) .Release.Namespace -}}
{{- default $default (get $clusterScoped "clusterRoleName") -}}
{{- end }}
{{/*
Name of the gateway ClusterRoleBinding.
*/}}
{{- define "openshell.clusterRoleBindingName" -}}
{{- $clusterScoped := include "openshell.clusterScopedRbacValues" . | fromYaml -}}
{{- $default := printf "%s-node-reader-%s" (include "openshell.fullname" .) .Release.Namespace -}}
{{- default $default (get $clusterScoped "clusterRoleBindingName") -}}
{{- end }}
{{/* Gateway image reference. A digest takes precedence over a tag. */}}
{{- define "openshell.image" -}}
{{- $image := .Values.gateway.image -}}
{{- $global := .Values.global.image -}}
{{- $registry := $image.registry | default $global.registry -}}
{{- $repository := ternary (printf "%s/%s" $registry $image.repository) $image.repository (ne $registry "") -}}
{{- if $image.digest -}}
{{- printf "%s@%s" $repository $image.digest -}}
{{- else -}}
{{- printf "%s:%s" $repository ($image.tag | default $global.tag | default .Chart.AppVersion) -}}
{{- end }}
{{- end }}
{{/* Sandbox image reference. A digest takes precedence over a tag. */}}
{{- define "openshell.sandboxImage" -}}
{{- $image := .Values.sandbox.image -}}
{{- if $image.digest -}}
{{- printf "%s@%s" $image.repository $image.digest -}}
{{- else -}}
{{- printf "%s:%s" $image.repository ($image.tag | default "latest") -}}
{{- end }}
{{- end }}
{{/* Official sandbox runtime repository used by the gateway's built-in default. */}}
{{- define "openshell.defaultSandboxRuntimeRepository" -}}
ghcr.io/nvidia/openshell/sandbox
{{- end }}
{{/* Whether Helm must propagate a sandbox runtime image override. */}}
{{- define "openshell.sandboxRuntimeImageOverrideEnabled" -}}
{{- $defaultRepository := include "openshell.defaultSandboxRuntimeRepository" . -}}
{{- $global := .Values.global.image -}}
{{- $registry := .Values.sandboxRuntime.image.registry | default $global.registry -}}
{{- $repository := ternary (printf "%s/%s" $registry .Values.sandboxRuntime.image.repository) .Values.sandboxRuntime.image.repository (ne $registry "") -}}
{{- if or (ne $repository $defaultRepository) .Values.sandboxRuntime.image.tag .Values.sandboxRuntime.image.digest .Values.global.image.tag -}}true{{- end -}}
{{- end }}
{{/* Sandbox runtime image override. */}}
{{- define "openshell.sandboxRuntimeImage" -}}
{{- $global := .Values.global.image -}}
{{- $registry := .Values.sandboxRuntime.image.registry | default $global.registry -}}
{{- $repository := ternary (printf "%s/%s" $registry .Values.sandboxRuntime.image.repository) .Values.sandboxRuntime.image.repository (ne $registry "") -}}
{{- if .Values.sandboxRuntime.image.digest -}}
{{- printf "%s@%s" $repository .Values.sandboxRuntime.image.digest -}}
{{- else -}}
{{- $tag := .Values.sandboxRuntime.image.tag | default $global.tag | default .Chart.AppVersion -}}
{{- printf "%s:%s" $repository $tag -}}
{{- end -}}
{{- end }}
{{/*
Whether the gateway listener should verify client certificates (mTLS).
An explicit empty server.tls.clientCaSecretName disables client-CA wiring in
both gateway.toml and the workload, overriding built-in PKI and cert-manager
defaults.
*/}}
{{- define "openshell.gatewayClientCaEnabled" -}}
{{- if .Values.server.disableTls -}}
{{- else if not .Values.server.tls.enableMtls -}}
{{- else if eq .Values.server.tls.clientCaSecretName "" -}}
{{- else -}}
true
{{- end -}}
{{- end -}}
{{/* Supervisor image override. */}}
{{- define "openshell.supervisorImage" -}}
{{- $global := .Values.global.image -}}
{{- $registry := .Values.supervisor.image.registry | default $global.registry -}}
{{- $repository := ternary (printf "%s/%s" $registry .Values.supervisor.image.repository) .Values.supervisor.image.repository (ne $registry "") -}}
{{- if .Values.supervisor.image.digest -}}
{{- printf "%s@%s" $repository .Values.supervisor.image.digest -}}
{{- else -}}
{{- $tag := .Values.supervisor.image.tag | default $global.tag | default .Chart.AppVersion -}}
{{- printf "%s:%s" $repository $tag -}}
{{- end }}
{{- end }}
{{/*
Namespaced Issuer (selfSigned) for cert-manager CA bootstrap.
*/}}
{{- define "openshell.issuerSelfSigned" -}}
{{- printf "%s-selfsigned" (include "openshell.fullname" .) | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Namespace where sandbox pods are created. An explicit
.Values.server.sandboxNamespace is used verbatim. Otherwise it defaults to
.Release.Namespace so `helm install -n my-ns` works without extra overrides.
*/}}
{{- define "openshell.sandboxNamespace" -}}
{{- .Values.server.sandboxNamespace | default .Release.Namespace -}}
{{- end }}
{{/*
Secrets in the sandbox namespace whose contents the Kubernetes driver stages
into per-generation Secrets in workspace namespaces, as a JSON array. Empty in
shared workspace mode.
*/}}
{{- define "openshell.workspaceSecretSourceNames" -}}
{{- $kubernetesConfig := include "openshell.effectiveKubernetesConfig" . | fromYaml -}}
{{- $workspaceMode := get $kubernetesConfig "workspace_mode" | default "shared" -}}
{{- $names := list -}}
{{- if and (ne $workspaceMode "shared") (not .Values.server.disableTls) -}}
{{- $names = append $names .Values.server.tls.clientTlsSecretName -}}
{{- end -}}
{{- if eq $workspaceMode "managed" -}}
{{- range (get $kubernetesConfig "image_pull_secrets" | default list) -}}
{{- if . -}}
{{- $names = append $names . -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- uniq $names | toJson -}}
{{- end }}
{{/*
Namespace where Kubernetes Secret-backed provider credentials live.
*/}}
{{- define "openshell.credentialKubernetesSecretsNamespace" -}}
{{- $gatewayConfig := .Values.gatewayConfig | default dict -}}
{{- $config := get $gatewayConfig "openshell.credential_drivers.kubernetes-secrets" | default dict -}}
{{- $legacy := .Values.server.credentialDrivers.kubernetesSecrets | default dict -}}
{{- get $config "namespace" | default (get $legacy "namespace") | default .Release.Namespace -}}
{{- end }}
{{/* Whether a credential driver is enabled in the generic gateway config. */}}
{{- define "openshell.credentialDriverEnabled" -}}
{{- $root := index . 0 -}}
{{- $driver := index . 1 -}}
{{- $gatewayConfig := $root.Values.gatewayConfig | default dict -}}
{{- $gateway := get $gatewayConfig "openshell.gateway" | default dict -}}
{{- $configuredDrivers := get $gateway "credential_drivers" -}}
{{- if and (hasKey $gateway "credential_drivers") (ne $configuredDrivers nil) -}}
{{- if has $driver ($configuredDrivers | default list) -}}true{{- end -}}
{{- else if eq $driver "kubernetes-secrets" -}}
{{- if $root.Values.server.credentialDrivers.kubernetesSecrets.enabled -}}true{{- end -}}
{{- else if eq $driver "vault" -}}
{{- if $root.Values.server.credentialDrivers.vault.enabled -}}true{{- end -}}
{{- end -}}
{{- end }}
{{/*
Name of the Secret holding the default credential storage key-encryption key.
When server.credentialStorage.existingSecret is set, returns that name instead
of the chart-generated name (for GitOps / helm-template workflows).
*/}}
{{- define "openshell.credentialStorageKeyEncryptionKeySecretName" -}}
{{- if .Values.server.credentialStorage.existingSecret -}}
{{- .Values.server.credentialStorage.existingSecret -}}
{{- else -}}
{{- printf "%s-credential-storage-key-encryption-key" (include "openshell.fullname" .) | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end }}
{{/*
Key inside the default credential storage key-encryption key Secret.
*/}}
{{- define "openshell.credentialStorageKeyEncryptionKeySecretKey" -}}
key-encryption-key
{{- end }}
{{/*
Gateway environment variable used to pass the default credential storage key-encryption key.
*/}}
{{- define "openshell.credentialStorageKeyEncryptionKeyEnvName" -}}
OPENSHELL_GATEWAY_CREDENTIAL_KEY_ENCRYPTION_KEY
{{- end }}
{{/*
Name of the Secret holding gateway-minted sandbox JWT signing material.
*/}}
{{- define "openshell.sandboxJwtSecretName" -}}
{{- .Values.server.sandboxJwt.signingSecretName | default (printf "%s-jwt-keys" (include "openshell.fullname" .)) -}}
{{- end }}
{{- define "openshell.peerServiceName" -}}
{{- printf "%s-peer" (include "openshell.fullname" .) -}}
{{- end }}
{{/*
gRPC endpoint sandbox pods use to call back into the gateway. An explicit
.Values.server.grpcEndpoint is used verbatim. Otherwise it is derived from
the in-cluster Service DNS, release namespace, service port, and disableTls
flag — so the default value works for any release name or namespace without
override.
*/}}
{{- define "openshell.grpcEndpoint" -}}
{{- if .Values.server.grpcEndpoint -}}
{{- .Values.server.grpcEndpoint -}}
{{- else -}}
{{- $scheme := ternary "http" "https" (default false .Values.server.disableTls) -}}
{{- printf "%s://%s.%s.svc.cluster.local:%d" $scheme (include "openshell.fullname" .) .Release.Namespace (int .Values.service.port) -}}
{{- end -}}
{{- end }}
{{/*
Default server certificate DNS SANs derived from the release name and namespace.
Returns a YAML list. Append extra SANs from values with range loops.
*/}}
{{- define "openshell.defaultServerDnsNames" -}}
{{- $name := include "openshell.fullname" . -}}
{{- $ns := .Release.Namespace -}}
{{- list $name
(printf "%s.%s.svc" $name $ns)
(printf "%s.%s.svc.cluster.local" $name $ns)
"localhost"
(printf "%s.localhost" $name)
(printf "*.%s.localhost" $name)
"host.docker.internal"
"host.containers.internal"
| toYaml }}
{{- end }}
{{/*
Name of the ConfigMap holding the backend CA for BackendTLSPolicy validation.
*/}}
{{- define "openshell.backendCaConfigMapName" -}}
{{- .Values.grpcRoute.backendTLSPolicy.caCertificateConfigMapName | default (printf "%s-backend-ca" (include "openshell.fullname" .)) -}}
{{- end }}
{{/*
Gateway workload kind. StatefulSet is the default because the default SQLite
database requires persistent per-pod storage.
*/}}
{{- define "openshell.workloadKind" -}}
{{- $workload := .Values.workload | default dict -}}
{{- if not (kindIs "map" $workload) -}}
{{- fail "workload must be a map with kind and allowMultiReplicaStatefulSet fields." -}}
{{- end -}}
{{- default "statefulset" (get $workload "kind") | lower -}}
{{- end }}
{{/*
Translate chart image pull policy values to the canonical gateway vocabulary.
The Kubernetes spellings remain accepted so existing values files continue to
work across the schema-v2 chart upgrade.
*/}}
{{- define "openshell.canonicalImagePullPolicy" -}}
{{- $policy := printf "%v" . -}}
{{- if eq $policy "Always" -}}
always
{{- else if eq $policy "IfNotPresent" -}}
if_not_present
{{- else if eq $policy "Never" -}}
never
{{- else if has $policy (list "always" "if_not_present" "never") -}}
{{- $policy -}}
{{- else -}}
{{- fail (printf "image pull policy %q must be one of: always, if_not_present, never, Always, IfNotPresent, Never" $policy) -}}
{{- end -}}
{{- end }}
{{/*
Render a sandbox UID/GID chart value as an integer, or nothing when unset.
Takes a dict with `name` (the values key, for errors) and `value`. The bounds
match openshell_policy::MIN_SANDBOX_UID..=MAX_SANDBOX_UID. Helm parses YAML
numbers as float64, so the integer conversion also avoids `2e+09` rendering.
Booleans are rejected because they would otherwise convert to 1 or 0.
*/}}
{{- define "openshell.sandboxId" -}}
{{- if not (or (kindIs "invalid" .value) (eq (toString .value) "")) -}}
{{- $id := int64 .value -}}
{{- if or (kindIs "bool" .value) (ne (float64 .value) (float64 $id)) (lt $id 1) (gt $id 4294967294) -}}
{{- fail (printf "%s must be an integer between 1 and 4294967294" .name) -}}
{{- end -}}
{{- $id -}}
{{- end -}}
{{- end }}
{{/*
Validate a non-empty, user-provided Kubernetes Secret name. Secret data never
passes through Helm values into gateway.toml; only this reference is rendered.
*/}}
{{- define "openshell.validateSecretReference" -}}
{{- $path := index . 0 -}}
{{- $name := index . 1 -}}
{{- if and (ne $name nil) (ne $name "") -}}
{{- if not (kindIs "string" $name) -}}
{{- fail (printf "%s must be a Kubernetes Secret name, got %s" $path (kindOf $name)) -}}
{{- end -}}
{{- if gt (len $name) 253 -}}
{{- fail (printf "%s must be no more than 253 characters" $path) -}}
{{- end -}}
{{- if not (regexMatch "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$" $name) -}}
{{- fail (printf "%s must be a valid Kubernetes Secret name" $path) -}}
{{- end -}}
{{- end -}}
{{- end }}
{{/*
Return the effective Kubernetes driver configuration as YAML. Schema-v2
gatewayConfig fields take precedence; deprecated 0.1.x aliases fill only
absent fields so every chart consumer observes the same configuration.
*/}}
{{- define "openshell.effectiveKubernetesConfig" -}}
{{- $gatewayConfig := deepCopy (.Values.gatewayConfig | default dict) -}}
{{- $kubernetes := get $gatewayConfig "openshell.drivers.kubernetes" | default dict -}}
{{- if hasKey $gatewayConfig "openshell.drivers.kubernetes.resource_admission" -}}
{{- $_ := set $kubernetes "resource_admission" (get $gatewayConfig "openshell.drivers.kubernetes.resource_admission") -}}
{{- end -}}
{{- $legacySandbox := .Values.sandboxRuntime.image | default dict -}}
{{- if and (include "openshell.sandboxRuntimeImageOverrideEnabled" .) (not (hasKey $kubernetes "sandbox_runtime_image")) -}}
{{- $_ := set $kubernetes "sandbox_runtime_image" (include "openshell.sandboxRuntimeImage" .) -}}
{{- end -}}
{{- $legacySandboxImage := .Values.sandbox.image | default dict -}}
{{- $legacySandboxPullPolicy := get $legacySandboxImage "pullPolicy" -}}
{{- if and $legacySandboxPullPolicy (not (hasKey $kubernetes "image_pull_policy")) -}}
{{- $_ := set $kubernetes "image_pull_policy" (include "openshell.canonicalImagePullPolicy" $legacySandboxPullPolicy) -}}
{{- end -}}
{{- $legacySandboxRuntimePullPolicy := get $legacySandbox "pullPolicy" | default .Values.global.image.pullPolicy -}}
{{- if and $legacySandboxRuntimePullPolicy (not (hasKey $kubernetes "sandbox_runtime_image_pull_policy")) -}}
{{- $_ := set $kubernetes "sandbox_runtime_image_pull_policy" (include "openshell.canonicalImagePullPolicy" $legacySandboxRuntimePullPolicy) -}}
{{- end -}}
{{- $legacySupervisor := .Values.supervisor.image | default dict -}}
{{- $legacySupervisorImage := include "openshell.supervisorImage" . -}}
{{- $defaultSupervisorImage := printf "ghcr.io/nvidia/openshell/supervisor:%s" .Chart.AppVersion -}}
{{- if and (ne $legacySupervisorImage $defaultSupervisorImage) (not (hasKey $kubernetes "supervisor_image")) -}}
{{- $_ := set $kubernetes "supervisor_image" $legacySupervisorImage -}}
{{- end -}}
{{- $legacySupervisorPullPolicy := get $legacySupervisor "pullPolicy" | default .Values.global.image.pullPolicy -}}
{{- if and $legacySupervisorPullPolicy (not (hasKey $kubernetes "supervisor_image_pull_policy")) -}}
{{- $_ := set $kubernetes "supervisor_image_pull_policy" (include "openshell.canonicalImagePullPolicy" $legacySupervisorPullPolicy) -}}
{{- end -}}
{{- $legacyRuntime := .Values.supervisor.sandboxRuntime | default dict -}}
{{- $runtimeConfig := get $kubernetes "sandbox_runtime" | default dict -}}
{{- if and (ne (int (get $legacyRuntime "boundaryPort" | default 5500)) 5500) (not (hasKey $runtimeConfig "boundary_port")) -}}
{{- $_ := set $runtimeConfig "boundary_port" (int (get $legacyRuntime "boundaryPort")) -}}
{{- end -}}
{{- $_ := set $kubernetes "sandbox_runtime" $runtimeConfig -}}
{{- $legacyProxy := .Values.upstreamProxy | default dict -}}
{{- range $legacyKey, $runtimeKey := dict "url" "https_proxy" "noProxy" "no_proxy" "authAllowInsecure" "proxy_auth_allow_insecure" "connectByHostname" "proxy_connect_by_hostname" -}}
{{- if and (get $legacyProxy $legacyKey) (not (hasKey $kubernetes $runtimeKey)) -}}
{{- $_ := set $kubernetes $runtimeKey (get $legacyProxy $legacyKey) -}}
{{- end -}}
{{- end -}}
{{- $legacyProxyAuth := get $legacyProxy "authSecret" | default dict -}}
{{- if and (get $legacyProxyAuth "name") (not (hasKey $kubernetes "proxy_auth_secret_name")) -}}{{- $_ := set $kubernetes "proxy_auth_secret_name" (get $legacyProxyAuth "name") -}}{{- end -}}
{{- if and (get $legacyProxyAuth "key") (not (hasKey $kubernetes "proxy_auth_secret_key")) -}}{{- $_ := set $kubernetes "proxy_auth_secret_key" (get $legacyProxyAuth "key") -}}{{- end -}}
{{- $legacyProxyCa := get $legacyProxy "caBundle" | default dict -}}
{{- if and (get $legacyProxyCa "configMapName") (not (hasKey $kubernetes "proxy_ca_bundle")) -}}
{{- $_ := set $kubernetes "proxy_ca_bundle" "/etc/openshell-tls/proxy-ca/ca.crt" -}}
{{- end -}}
{{- $legacyKubernetes := .Values.server.drivers.kubernetes | default dict -}}
{{- $legacyServer := .Values.server | default dict -}}
{{- if not (hasKey $kubernetes "allow_driver_config") -}}{{- $_ := set $kubernetes "allow_driver_config" (get $legacyKubernetes "allowDriverConfig" | default false) -}}{{- end -}}
{{- if and (ne (get $legacyKubernetes "workspaceMode" | default "shared") "shared") (not (hasKey $kubernetes "workspace_mode")) -}}
{{- $_ := set $kubernetes "workspace_mode" (get $legacyKubernetes "workspaceMode") -}}
{{- end -}}
{{- range $legacyKey, $runtimeKey := dict "operatorNamespaceLabel" "operator_namespace_label" "operatorNamespaceFile" "operator_namespace_file" -}}
{{- if and (get $legacyKubernetes $legacyKey) (not (hasKey $kubernetes $runtimeKey)) -}}
{{- $_ := set $kubernetes $runtimeKey (get $legacyKubernetes $legacyKey) -}}
{{- end -}}
{{- end -}}
{{- if not (hasKey $kubernetes "resource_admission") -}}
{{- $legacyAdmission := get $legacyKubernetes "resourceAdmission" | default dict -}}
{{- $admission := dict -}}
{{- if hasKey $legacyAdmission "enabled" -}}{{- $_ := set $admission "enabled" (get $legacyAdmission "enabled") -}}{{- end -}}
{{- if and (hasKey $legacyAdmission "requiredLabels") (ne (get $legacyAdmission "requiredLabels") nil) -}}{{- $_ := set $admission "required_labels" (deepCopy (get $legacyAdmission "requiredLabels")) -}}{{- end -}}
{{- $_ := set $kubernetes "resource_admission" $admission -}}
{{- end -}}
{{- if and (get $legacyServer "enableUserNamespaces") (not (hasKey $kubernetes "enable_user_namespaces")) -}}{{- $_ := set $kubernetes "enable_user_namespaces" true -}}{{- end -}}
{{- if and (get $legacyServer "hostGatewayIP") (not (hasKey $kubernetes "host_gateway_ip")) -}}{{- $_ := set $kubernetes "host_gateway_ip" (get $legacyServer "hostGatewayIP") -}}{{- end -}}
{{/* Namespace and ServiceAccount refer to chart-created resources. */}}
{{- $_ := set $kubernetes "namespace" (include "openshell.sandboxNamespace" .) -}}
{{- if not (hasKey $kubernetes "default_image") -}}{{- $_ := set $kubernetes "default_image" (include "openshell.sandboxImage" .) -}}{{- end -}}
{{- if not (hasKey $kubernetes "gateway_id") -}}{{- $_ := set $kubernetes "gateway_id" (get (.Values.server.sandboxJwt | default dict) "gatewayId" | default (include "openshell.fullname" .)) -}}{{- end -}}
{{- if not (hasKey $kubernetes "grpc_endpoint") -}}{{- $_ := set $kubernetes "grpc_endpoint" (include "openshell.grpcEndpoint" .) -}}{{- end -}}
{{- $_ := set $kubernetes "service_account_name" (include "openshell.sandboxServiceAccountName" .) -}}
{{- if not (hasKey $kubernetes "sa_token_ttl_secs") -}}{{- $_ := set $kubernetes "sa_token_ttl_secs" (get (.Values.server.sandboxJwt | default dict) "k8sSaTokenTtlSecs" | default 3600) -}}{{- end -}}
{{- if not (hasKey $kubernetes "image_pull_secrets") -}}
{{- $imagePullSecrets := list -}}{{- range (get $legacyServer "sandboxImagePullSecrets" | default list) }}{{- if .name }}{{- $imagePullSecrets = append $imagePullSecrets .name }}{{- end }}{{- end -}}
{{- if $imagePullSecrets }}{{- $_ := set $kubernetes "image_pull_secrets" $imagePullSecrets -}}{{- end -}}
{{- end -}}
{{- range $legacyKey, $runtimeKey := dict "workspaceDefaultStorageSize" "workspace_default_storage_size" "workspaceStorageClass" "workspace_storage_class" "defaultRuntimeClassName" "default_runtime_class_name" -}}
{{- if and (get $legacyServer $legacyKey) (not (hasKey $kubernetes $runtimeKey)) -}}{{- $_ := set $kubernetes $runtimeKey (get $legacyServer $legacyKey) -}}{{- end -}}
{{- end -}}
{{- if not (hasKey $kubernetes "sandbox_uid") -}}
{{- $sandboxUid := include "openshell.sandboxId" (dict "name" "server.sandboxUid" "value" (get $legacyServer "sandboxUid")) -}}
{{- if ne $sandboxUid "" -}}{{- $_ := set $kubernetes "sandbox_uid" (int64 $sandboxUid) -}}{{- end -}}
{{- end -}}
{{- if not (hasKey $kubernetes "sandbox_gid") -}}
{{- $sandboxGid := include "openshell.sandboxId" (dict "name" "server.sandboxGid" "value" (get $legacyServer "sandboxGid")) -}}
{{- if ne $sandboxGid "" -}}{{- $_ := set $kubernetes "sandbox_gid" (int64 $sandboxGid) -}}{{- end -}}
{{- end -}}
{{- $legacySpiffe := .Values.server.providerTokenGrants.spiffe | default dict -}}
{{- if and (get $legacySpiffe "enabled") (not (hasKey $kubernetes "provider_spiffe_workload_api_socket_path")) -}}
{{- $_ := set $kubernetes "provider_spiffe_workload_api_socket_path" (get $legacySpiffe "workloadApiSocketPath") -}}
{{- end -}}
{{/* Keep the rendered default configuration stable without making defaults look
like user-supplied schema-v2 fields during compatibility resolution. */}}
{{- if not (hasKey $kubernetes "workspace_mode") -}}{{- $_ := set $kubernetes "workspace_mode" "shared" -}}{{- end -}}
{{- if not (hasKey $kubernetes "supervisor_image") -}}{{- $_ := set $kubernetes "supervisor_image" (include "openshell.supervisorImage" .) -}}{{- end -}}
{{- if not (hasKey $runtimeConfig "boundary_port") -}}{{- $_ := set $runtimeConfig "boundary_port" 5500 -}}{{- end -}}
{{- $_ := set $kubernetes "sandbox_runtime" $runtimeConfig -}}
{{- toYaml $kubernetes -}}
{{- end }}
{{/*
Validate chart values that Helm would otherwise accept silently.
*/}}
{{- define "openshell.validateValues" -}}
{{- $workloadKind := include "openshell.workloadKind" . -}}
{{- $workload := .Values.workload | default dict -}}
{{- $maxReplicas := int (include "openshell.maxReplicas" .) -}}
{{- $maxReplicasSource := include "openshell.maxReplicasSource" . -}}
{{- if and (hasKey .Values "postgres") (kindIs "map" .Values.postgres) (hasKey .Values.postgres "enabled") -}}
{{- fail "postgres.enabled was removed; the OpenShell chart no longer deploys PostgreSQL. Provision PostgreSQL separately and set server.externalDbSecret to a Secret containing a PostgreSQL URI." -}}
{{- end -}}
{{- if and .Values.certManager.serverIssuerRef.name (not .Values.certManager.enabled) -}}
{{- fail "certManager.serverIssuerRef.name is set but certManager.enabled is false — the external server certificate, its Secret mount, and the gateway TLS configuration all require cert-manager to be enabled. Set certManager.enabled=true or remove certManager.serverIssuerRef.name." -}}
{{- end -}}
{{- if not (or (eq $workloadKind "statefulset") (eq $workloadKind "deployment")) -}}
{{- fail "workload.kind must be one of: statefulset, deployment." -}}
{{- end -}}
{{- if and (eq $workloadKind "deployment") (not .Values.server.externalDbSecret) -}}
{{- fail "workload.kind=deployment requires server.externalDbSecret; use workload.kind=statefulset for the default SQLite database." -}}
{{- end -}}
{{- include "openshell.validateAutoscaling" . -}}
{{- if and (gt $maxReplicas 1) (not .Values.server.externalDbSecret) -}}
{{- fail (printf "%s > 1 requires server.externalDbSecret; multiple gateway replicas cannot share the default per-pod SQLite database." $maxReplicasSource) -}}
{{- end -}}
{{- if and (eq $workloadKind "statefulset") (gt $maxReplicas 1) (not (get $workload "allowMultiReplicaStatefulSet" | default false)) -}}
{{- fail (printf "%s > 1 with workload.kind=statefulset requires workload.allowMultiReplicaStatefulSet=true; use workload.kind=deployment for external database-backed multi-replica gateways." $maxReplicasSource) -}}
{{- end -}}
{{- if and .Values.grpcRoute.enabled (dig "replicaRouting" "enabled" false .Values.grpcRoute) -}}
{{- if ne $workloadKind "statefulset" -}}
{{- fail "grpcRoute.replicaRouting.enabled requires workload.kind=statefulset so each replica has a stable name." -}}
{{- end -}}
{{- if gt $maxReplicas 15 -}}
{{- fail (printf "grpcRoute.replicaRouting.enabled supports %s of at most 15; a GRPCRoute holds at most 16 rules." $maxReplicasSource) -}}
{{- end -}}
{{- end -}}
{{- include "openshell.validateSecretReference" (list "server.externalDbSecret" .Values.server.externalDbSecret) -}}
{{- include "openshell.validateSecretReference" (list "server.credentialStorage.existingSecret" .Values.server.credentialStorage.existingSecret) -}}
{{- include "openshell.validateSecretReference" (list "server.sandboxJwt.signingSecretName" .Values.server.sandboxJwt.signingSecretName) -}}
{{- include "openshell.validateSecretReference" (list "server.tls.certSecretName" .Values.server.tls.certSecretName) -}}
{{- include "openshell.validateSecretReference" (list "upstreamProxy.authSecret.name" .Values.upstreamProxy.authSecret.name) -}}
{{- $gatewayConfig := .Values.gatewayConfig | default dict -}}
{{- $gateway := get $gatewayConfig "openshell.gateway" | default dict -}}
{{- $credentialDrivers := get $gateway "credential_drivers" -}}
{{- if and (hasKey $gateway "credential_drivers") (ne $credentialDrivers nil) -}}
{{- if eq (len $credentialDrivers) 0 -}}
{{- fail "gatewayConfig.openshell.gateway.credential_drivers must select exactly one backend or be omitted/null to use the chart default" -}}
{{- end -}}
{{- if gt (len $credentialDrivers) 1 -}}
{{- fail "gatewayConfig.openshell.gateway.credential_drivers may select only one backend" -}}
{{- end -}}
{{- end -}}
{{- $kubernetesConfig := include "openshell.effectiveKubernetesConfig" . | fromYaml -}}
{{- include "openshell.validateSecretReference" (list "gatewayConfig.openshell.drivers.kubernetes.proxy_auth_secret_name" (get $kubernetesConfig "proxy_auth_secret_name")) -}}
{{- $workspaceMode := get $kubernetesConfig "workspace_mode" | default "shared" -}}
{{- if not (has $workspaceMode (list "shared" "managed" "operator")) -}}
{{- fail "gatewayConfig.openshell.drivers.kubernetes.workspace_mode must be one of: shared, managed, operator." -}}
{{- end -}}
{{- if kindIs "invalid" .Values.server.tls.clientCaSecretName -}}
{{- fail "server.tls.clientCaSecretName cannot be null; omit the key to use the chart default (openshell-server-client-ca), or set to \"\" to disable client certificate verification for HTTPS-only mode" -}}
{{- end -}}
{{- $dataStorageSize := .Values.server.dataStorageSize -}}
{{- if and (eq $workloadKind "statefulset") $dataStorageSize (not (regexMatch "^[0-9]+(\\.[0-9]+)?([KMGTPE]i|[kMGTPE])$" (toString $dataStorageSize))) -}}
{{- fail "server.dataStorageSize must be a Kubernetes quantity with a unit, such as 20Gi" -}}
{{- end -}}
{{- end }}