feat(testing): support independent gateway and supervisor image overrides (#3341)

* feat(testing): normalize configurable test images

Signed-off-by: Bobbins228 <mcampbel@redhat.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>

* feat(helm): add global image overrides

Signed-off-by: Bobbins228 <mcampbel@redhat.com>

* feat(helm): support image registry overrides

Signed-off-by: Bobbins228 <mcampbel@redhat.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>

* refactor(helm): simplify image configuration

Signed-off-by: Bobbins228 <mcampbel@redhat.com>

* fix(e2e): avoid reloading reused kind sandbox image

Signed-off-by: Bobbins228 <mcampbel@redhat.com>

* fix(helm): default sandbox image to nvcr.io/nvidia/base/ubuntu:24.04

Signed-off-by: Kris Hicks <khicks@nvidia.com>

---------

Signed-off-by: Bobbins228 <mcampbel@redhat.com>
Signed-off-by: Kris Hicks <khicks@nvidia.com>
Co-authored-by: Kris Hicks <khicks@nvidia.com>
This commit is contained in:
Mark Campbell
2026-09-23 23:35:30 +00:00
committed by GitHub
co-authored by Kris Hicks
parent 490055b426
commit 679b190677
22 changed files with 641 additions and 156 deletions
+37 -3
View File
@@ -359,9 +359,40 @@ TAG=0.0.115
skopeo inspect "docker://ghcr.io/nvidia/openshell/gateway:${TAG}"
```
`IMAGE_TAG` sets only the gateway/supervisor image; the CLI under test is always
built from your branch. To validate against images from your exact commit
instead, build and push them and point `OPENSHELL_REGISTRY`/`IMAGE_TAG` at them.
`IMAGE_TAG` sets the default tag for the gateway/supervisor image pair; the CLI
under test is always built from your branch. To validate against images from
your exact commit instead, build and push them and point
`OPENSHELL_REGISTRY`/`IMAGE_TAG` at them.
Test wrappers accept independent image overrides:
```shell
GATEWAY_IMAGE=registry.example.com/custom/gateway:test \
SUPERVISOR_IMAGE=registry.example.com/custom/supervisor:test \
SANDBOX_IMAGE=registry.example.com/custom/sandbox:test \
mise run e2e:kubernetes
```
`GATEWAY_IMAGE` applies to the Kubernetes gateway container. `SUPERVISOR_IMAGE`
applies to the trusted supervisor image selected by the Kubernetes, Docker, and
Podman wrappers. `SANDBOX_IMAGE` applies to the trusted workload-side runtime
image that stages the `openshell-sandbox` binary. A repository-only value
inherits `IMAGE_TAG`; a value with an explicit tag or `@sha256:` digest is used
as-is. When these variables are unset, the existing `OPENSHELL_REGISTRY` plus
`IMAGE_TAG` behavior is retained.
The Docker and Podman wrappers continue to give
`OPENSHELL_DOCKER_SUPERVISOR_IMAGE` and `OPENSHELL_SUPERVISOR_IMAGE` precedence
over `SUPERVISOR_IMAGE`.
Digest-pinned Kubernetes overrides require disabling local image builds, because
Docker cannot tag a locally built image with a digest reference:
```shell
OPENSHELL_E2E_KUBE_BUILD_IMAGES=0 \
GATEWAY_IMAGE=registry.example.com/custom/gateway@sha256:<digest> \
SUPERVISOR_IMAGE=registry.example.com/custom/supervisor@sha256:<digest> \
mise run e2e:kubernetes
```
Available task variants:
@@ -386,6 +417,9 @@ Kubernetes e2e environment variables:
| `OPENSHELL_E2E_KUBERNETES_FEATURES` | Cargo feature flags (default: `e2e,e2e-host-gateway,e2e-kubernetes`) |
| `IMAGE_TAG` | Gateway/supervisor image tag (default: `latest` for existing clusters) |
| `OPENSHELL_REGISTRY` | Image registry prefix (default: `ghcr.io/nvidia/openshell`) |
| `GATEWAY_IMAGE` | Kubernetes gateway image repository or complete tagged/digest-pinned image reference; digests require `OPENSHELL_E2E_KUBE_BUILD_IMAGES=0` |
| `SUPERVISOR_IMAGE` | Gateway/supervisor image repository or complete tagged/digest-pinned image reference; Kubernetes digests require `OPENSHELL_E2E_KUBE_BUILD_IMAGES=0` |
| `SANDBOX_IMAGE` | Trusted sandbox runtime image repository or complete tagged/digest-pinned image reference |
Run a single test directly with cargo:
+22 -11
View File
@@ -209,6 +209,14 @@ discovery endpoint or its TLS CA.
| certManager.serverIpAddresses | list | `["127.0.0.1"]` | IP SANs on the cert-manager-issued server certificate. |
| certManager.serverIssuerRef | object | `{"group":"","kind":"","name":""}` | Override the issuerRef for the external server Certificate (e.g. a real LetsEncrypt/ACME ClusterIssuer for a publicly-trusted cert on an external hostname). When set, the chart creates a second server certificate from this issuer with only the hostnames in serverDnsNames; the internal server certificate is always signed by the chart's own CA. Leave name empty to use the chart CA for all server certificates (default). Requires certManager.enabled=true. |
| fullnameOverride | string | `""` | Override the full generated resource name. |
| gateway.image.digest | string | `""` | Gateway image digest. When set, this takes precedence over tag. |
| gateway.image.pullPolicy | string | `nil` | Gateway image pull policy. Empty uses global.image.pullPolicy. |
| gateway.image.registry | string | `""` | Gateway image registry. Empty uses global.image.registry. |
| gateway.image.repository | string | `"openshell/gateway"` | Gateway image repository. |
| gateway.image.tag | string | `""` | Gateway image tag. Defaults to the chart appVersion when empty. |
| global.image.pullPolicy | string | `"IfNotPresent"` | Shared OpenShell image pull policy. Individual image pull policies take precedence. |
| global.image.registry | string | `"ghcr.io/nvidia"` | Shared OpenShell image registry. Individual image registries take precedence. |
| global.image.tag | string | `""` | Shared OpenShell image tag. Defaults to the chart appVersion when empty. |
| grpcRoute.backendTLSPolicy.caCertificateConfigMapName | string | `""` | Name of the ConfigMap containing the CA certificate (key: ca.crt) used to validate the gateway pod's TLS certificate. Defaults to `<fullname>-backend-ca` when empty. The certgen hook auto-creates this: with pkiInitJob (default), immediately on install/upgrade; with cert-manager, the hook polls for pkiInitJob.timeoutSeconds seconds waiting for cert-manager to issue the server certificate, then creates the ConfigMap. A single install usually succeeds; if cert-manager takes longer, increase pkiInitJob.timeoutSeconds. By default (pkiInitJob.failOnTimeout=true), the install fails if the timeout is reached; set failOnTimeout=false to allow the install to succeed and run `helm upgrade` after the certificate is issued. |
| grpcRoute.backendTLSPolicy.enabled | bool | `false` | Create a BackendTLSPolicy resource for end-to-end TLS between the Gateway proxy and the OpenShell gateway pod. The traffic flow is: client → HTTPS → Gateway (terminate) → TLS (re-encrypt) → gateway pod. Requires server.disableTls=false and server.tls.enableMtls=false. The certgen hook auto-creates the backend CA ConfigMap. |
| grpcRoute.backendTLSPolicy.hostname | string | `""` | Hostname the Gateway proxy validates against the backend's TLS certificate SAN. Defaults to the service FQDN (`<fullname>.<namespace>.svc.cluster.local`) when empty, which matches the SAN included by both cert-manager and the pkiInitJob. |
@@ -222,9 +230,6 @@ discovery endpoint or its TLS CA.
| grpcRoute.gateway.name | string | `""` | Name of the Gateway resource. Defaults to the chart fullname. |
| grpcRoute.gateway.namespace | string | `""` | Namespace of the Gateway referenced by the GRPCRoute parentRef. Defaults to the release namespace. |
| grpcRoute.hostnames | list | `[]` | Hostnames the GRPCRoute matches on. Leave empty to match all hosts. |
| image.pullPolicy | string | `"IfNotPresent"` | Gateway image pull policy. |
| image.repository | string | `"ghcr.io/nvidia/openshell/gateway"` | Gateway image repository. |
| image.tag | string | `""` | Gateway image tag. Defaults to the chart appVersion when empty. |
| imagePullSecrets | list | `[]` | Image pull secrets attached to gateway and helper pods. |
| nameOverride | string | `"openshell"` | Override the chart name used in generated resource names. |
| networkPolicy.enabled | bool | `true` | Restrict SSH ingress on sandbox pods to the gateway. In managed mode, the driver applies the equivalent policy to each workspace namespace. |
@@ -254,9 +259,15 @@ discovery endpoint or its TLS CA.
| probes.startup.timeoutSeconds | int | `1` | Startup probe timeout, in seconds. |
| replicaCount | int | `1` | Number of OpenShell gateway replicas. Values greater than 1 require server.externalDbSecret because the default SQLite backend is per pod. |
| resources | object | `{}` | Gateway pod resource requests and limits. |
| sandboxRuntime.image.pullPolicy | string | `""` | Sandbox runtime image pull policy. Defaults to the gateway image pull policy when empty. |
| sandboxRuntime.image.repository | string | `"ghcr.io/nvidia/openshell/sandbox"` | Sandbox runtime image repository. Changing it uses the effective gateway image tag unless tag is also set. |
| sandboxRuntime.image.tag | string | `""` | Sandbox runtime image tag override. Empty uses the version pinned into the gateway unless repository is changed. |
| sandbox.image.digest | string | `""` | Sandbox image digest. When set, this takes precedence over tag. |
| sandbox.image.pullPolicy | string | `nil` | Sandbox image pull policy. Leave unset to use the Kubernetes image default. |
| sandbox.image.repository | string | `"nvcr.io/nvidia/base/ubuntu"` | Default standalone sandbox image repository. |
| sandbox.image.tag | string | `"24.04"` | Sandbox image tag. Defaults to latest when empty. |
| sandboxRuntime.image.digest | string | `""` | Sandbox runtime image digest. When set, this takes precedence over tag. |
| sandboxRuntime.image.pullPolicy | string | `nil` | Sandbox runtime image pull policy. Empty uses global.image.pullPolicy. |
| sandboxRuntime.image.registry | string | `""` | Sandbox runtime image registry. Empty uses global.image.registry. |
| sandboxRuntime.image.repository | string | `"openshell/sandbox"` | Sandbox runtime image repository. |
| sandboxRuntime.image.tag | string | `""` | Sandbox runtime image tag. Defaults to the chart appVersion when empty. |
| sandboxServiceAccount.annotations | object | `{}` | Annotations to add to the generated sandbox service account. |
| sandboxServiceAccount.create | bool | `true` | Create a service account for sandbox pods. |
| sandboxServiceAccount.name | string | `""` | Existing service account name for sandbox pods when sandboxServiceAccount.create is false. |
@@ -314,8 +325,6 @@ discovery endpoint or its TLS CA.
| server.policyValidationFailureMode | string | `"fail_closed"` | Posture when a candidate sandbox policy fails validation. `fail_closed` deactivates the previous policy; `retain_last_valid` keeps it active. |
| server.providerTokenGrants.spiffe.enabled | bool | `false` | Mount the SPIFFE Workload API socket into gateway and sandbox pods for dynamic provider token grants. |
| server.providerTokenGrants.spiffe.workloadApiSocketPath | string | `"/spiffe-workload-api/spire-agent.sock"` | Path to the SPIFFE Workload API socket mounted into gateway and sandbox pods. |
| server.sandboxImage | string | `"nvcr.io/nvidia/base/ubuntu:24.04"` | Default sandbox image used when requests do not specify one. |
| server.sandboxImagePullPolicy | string | `nil` | Pull policy for sandbox pods. Leave unset to use the Kubernetes image default (Always for :latest, IfNotPresent otherwise). Prefer always, if_not_present, or never; the chart also accepts legacy Kubernetes spellings Always, IfNotPresent, and Never. |
| server.sandboxImagePullSecrets | list | `[]` | Image pull secrets attached to sandbox pods. Referenced Secrets must exist in the sandbox namespace. |
| server.sandboxJwt.gatewayId | string | `""` | Stable gateway identity embedded in iss/aud of every minted token. Defaults to the release name so HA replicas share identity. |
| server.sandboxJwt.k8sSaTokenTtlSecs | int | `3600` | Lifetime (seconds) of the projected ServiceAccount token kubelet writes into each sandbox pod for the IssueSandboxToken bootstrap exchange. Kubelet enforces a minimum of 600s; the driver clamps values outside [600, 86400]. Default 3600 — generous, since the supervisor consumes the token within seconds of pod start. |
@@ -337,9 +346,11 @@ discovery endpoint or its TLS CA.
| serviceAccount.annotations | object | `{}` | Annotations to add to the generated service account. |
| serviceAccount.create | bool | `true` | Create a service account for the gateway. |
| serviceAccount.name | string | `""` | Existing service account name to use when serviceAccount.create is false. |
| supervisor.image.pullPolicy | string | `nil` | Sandbox supervisor pull policy. Leave unset to use the Kubernetes image default. Prefer always, if_not_present, or never; the chart also accepts legacy Kubernetes spellings Always, IfNotPresent, and Never. |
| supervisor.image.repository | string | `"ghcr.io/nvidia/openshell/supervisor"` | Supervisor image repository. Changing it uses the effective gateway image tag unless tag is also set. |
| supervisor.image.tag | string | `""` | Supervisor image tag override. Empty uses the version pinned into the gateway unless repository is changed. |
| supervisor.image.digest | string | `""` | Supervisor image digest. When set, this takes precedence over tag. |
| supervisor.image.pullPolicy | string | `nil` | Supervisor image pull policy. Empty uses global.image.pullPolicy. Prefer always, if_not_present, or never; the chart also accepts legacy Kubernetes spellings Always, IfNotPresent, and Never. |
| supervisor.image.registry | string | `""` | Supervisor image registry. Empty uses global.image.registry. |
| supervisor.image.repository | string | `"openshell/supervisor"` | Supervisor image repository. |
| supervisor.image.tag | string | `""` | Supervisor image tag. Defaults to the chart appVersion when empty. |
| supervisor.sandboxRuntime.boundaryPort | int | `5500` | Workload boundary TLS listener port. |
| supervisor.sandboxRuntime.networkPolicyEnforced | bool | `false` | Required operator acknowledgement that the cluster CNI enforces NetworkPolicy. |
| tolerations | list | `[]` | Tolerations for the gateway pod. |
@@ -28,8 +28,9 @@
# `allowUnauthenticatedUsers` only promotes the already cert-verified caller to a
# dev principal at the app layer (mtls_auth is unsupported with the Kubernetes
# driver). Both are required together; the client certificate is the access gate.
image:
pullPolicy: Always
gateway:
image:
pullPolicy: Always
supervisor:
image:
@@ -3,7 +3,6 @@
# Merge with values.yaml for Skaffold-driven local image builds (see skaffold.yaml).
server:
sandboxImagePullPolicy: if_not_present
otlp:
endpoint: http://openshell-collector.observability.svc.cluster.local:4317
# Comment out to enforce mTLS (uses PKI secrets generated by pkiInitJob).
@@ -11,6 +10,10 @@ server:
auth:
allowUnauthenticatedUsers: true
sandbox:
image:
pullPolicy: if_not_present
supervisor:
image:
pullPolicy: if_not_present
+5 -2
View File
@@ -139,8 +139,11 @@ deploy:
# To test multi-replica external PostgreSQL behavior:
#- ci/values-high-availability.yaml
setValueTemplates:
image.repository: '{{.IMAGE_REPO_openshell_gateway}}'
image.tag: '{{.IMAGE_TAG_openshell_gateway}}'
# Skaffold supplies complete image repositories. Clear the chart's
# shared registry so these values are used verbatim.
global.image.registry: ''
gateway.image.repository: '{{.IMAGE_REPO_openshell_gateway}}'
gateway.image.tag: '{{.IMAGE_TAG_openshell_gateway}}'
supervisor.image.repository: '{{.IMAGE_REPO_openshell_supervisor}}'
supervisor.image.tag: '{{.IMAGE_TAG_openshell_supervisor}}'
sandboxRuntime.image.repository: '{{.IMAGE_REPO_openshell_sandbox}}'
@@ -43,7 +43,7 @@ spec:
securityContext:
{{- toYaml .Values.securityContext | nindent 8 }}
image: {{ include "openshell.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
imagePullPolicy: {{ .Values.gateway.image.pullPolicy | default .Values.global.image.pullPolicy }}
args:
- --config
- /etc/openshell/gateway.toml
+43 -32
View File
@@ -86,12 +86,27 @@ default to enabled so upgrades with --reuse-values preserve the old topology.
{{- if $enabled -}}true{{- end -}}
{{- end }}
{{/*
Gateway image reference. Uses image.tag when set; falls back to .Chart.AppVersion
so a released chart automatically pulls the matching image without extra overrides.
*/}}
{{/* Gateway image reference. A digest takes precedence over a tag. */}}
{{- define "openshell.image" -}}
{{- printf "%s:%s" .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) }}
{{- $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. */}}
@@ -102,20 +117,23 @@ ghcr.io/nvidia/openshell/sandbox
{{/* Whether Helm must propagate a sandbox runtime image override. */}}
{{- define "openshell.sandboxRuntimeImageOverrideEnabled" -}}
{{- $defaultRepository := include "openshell.defaultSandboxRuntimeRepository" . -}}
{{- $repository := .Values.sandboxRuntime.image.repository | default $defaultRepository -}}
{{- if or (ne $repository $defaultRepository) .Values.sandboxRuntime.image.tag -}}true{{- end -}}
{{- $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" -}}
{{- $repository := .Values.sandboxRuntime.image.repository | default (include "openshell.defaultSandboxRuntimeRepository" .) -}}
{{- $tag := .Values.sandboxRuntime.image.tag | default .Values.image.tag | default .Chart.AppVersion -}}
{{- printf "%s:%s" $repository $tag }}
{{- end }}
{{/* Official supervisor repository used by the gateway's built-in default. */}}
{{- define "openshell.defaultSupervisorRepository" -}}
ghcr.io/nvidia/openshell/supervisor
{{- $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 }}
{{/*
@@ -133,24 +151,17 @@ true
{{- end -}}
{{- end -}}
{{/*
Whether Helm must propagate a supervisor image override into gateway.toml.
The chart's documented repository and empty tag are the gateway-owned default.
*/}}
{{- define "openshell.supervisorImageOverrideEnabled" -}}
{{- $defaultRepository := include "openshell.defaultSupervisorRepository" . -}}
{{- $repository := .Values.supervisor.image.repository | default $defaultRepository -}}
{{- if or (ne $repository $defaultRepository) .Values.supervisor.image.tag -}}true{{- end -}}
{{- end }}
{{/*
Supervisor image override. A tag-only override uses the official repository;
a repository-only override uses the effective gateway image tag.
*/}}
{{/* Supervisor image override. */}}
{{- define "openshell.supervisorImage" -}}
{{- $repository := .Values.supervisor.image.repository | default (include "openshell.defaultSupervisorRepository" .) -}}
{{- $tag := .Values.supervisor.image.tag | default .Values.image.tag | default .Chart.AppVersion -}}
{{- printf "%s:%s" $repository $tag }}
{{- $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 }}
{{/*
+2 -2
View File
@@ -86,7 +86,7 @@ spec:
containers:
- name: certgen
image: {{ include "openshell.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
imagePullPolicy: {{ .Values.gateway.image.pullPolicy | default .Values.global.image.pullPolicy }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
@@ -157,7 +157,7 @@ spec:
containers:
- name: certgen
image: {{ include "openshell.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
imagePullPolicy: {{ .Values.gateway.image.pullPolicy | default .Values.global.image.pullPolicy }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
@@ -140,13 +140,11 @@ data:
[openshell.drivers.kubernetes]
allow_driver_config = {{ .Values.server.drivers.kubernetes.allowDriverConfig }}
namespace = {{ include "openshell.sandboxNamespace" . | quote }}
default_image = {{ .Values.server.sandboxImage | quote }}
default_image = {{ include "openshell.sandboxImage" . | quote }}
{{- if include "openshell.sandboxRuntimeImageOverrideEnabled" . }}
sandbox_runtime_image = {{ include "openshell.sandboxRuntimeImage" . | quote }}
{{- end }}
{{- if include "openshell.supervisorImageOverrideEnabled" . }}
supervisor_image = {{ include "openshell.supervisorImage" . | quote }}
{{- end }}
{{- if .Values.server.hostGatewayIP }}
host_gateway_ip = {{ .Values.server.hostGatewayIP | quote }}
{{- end }}
@@ -191,8 +189,8 @@ data:
{{- if .Values.server.providerTokenGrants.spiffe.enabled }}
provider_spiffe_workload_api_socket_path = {{ .Values.server.providerTokenGrants.spiffe.workloadApiSocketPath | quote }}
{{- end }}
{{- if .Values.server.sandboxImagePullPolicy }}
image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" .Values.server.sandboxImagePullPolicy | quote }}
{{- if .Values.sandbox.image.pullPolicy }}
image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" .Values.sandbox.image.pullPolicy | quote }}
{{- end }}
{{- $sandboxImagePullSecretNames := list -}}
{{- range .Values.server.sandboxImagePullSecrets }}
@@ -212,11 +210,11 @@ data:
{{- if .Values.server.defaultRuntimeClassName }}
default_runtime_class_name = {{ .Values.server.defaultRuntimeClassName | quote }}
{{- end }}
{{- if .Values.supervisor.image.pullPolicy }}
supervisor_image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" .Values.supervisor.image.pullPolicy | quote }}
{{- if (.Values.supervisor.image.pullPolicy | default .Values.global.image.pullPolicy) }}
supervisor_image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" (.Values.supervisor.image.pullPolicy | default .Values.global.image.pullPolicy) | quote }}
{{- end }}
{{- if .Values.sandboxRuntime.image.pullPolicy }}
sandbox_runtime_image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" .Values.sandboxRuntime.image.pullPolicy | quote }}
{{- if (.Values.sandboxRuntime.image.pullPolicy | default .Values.global.image.pullPolicy) }}
sandbox_runtime_image_pull_policy = {{ include "openshell.canonicalImagePullPolicy" (.Values.sandboxRuntime.image.pullPolicy | default .Values.global.image.pullPolicy) | quote }}
{{- end }}
[openshell.drivers.kubernetes.resource_admission]
@@ -82,6 +82,103 @@ tests:
path: data["gateway.toml"]
pattern: '(?ms)\[openshell\.gateway\][^\[]*?compute_driver\s*=\s*"kubernetes"'
- it: preserves a complete tagged gateway image reference
template: templates/statefulset.yaml
set:
gateway.image.registry: registry.example
gateway.image.repository: gateway
gateway.image.tag: branch
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: registry.example/gateway:branch
- it: defaults a repository-only gateway image to the chart appVersion
template: templates/statefulset.yaml
set:
gateway.image.registry: registry.example
gateway.image.repository: gateway
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: registry.example/gateway:0.0.0
- it: applies global image values to gateway, supervisor, and sandbox runtime
template: templates/gateway-config.yaml
set:
global.image.registry: registry.example.com
global.image.tag: v0.1.0
global.image.pullPolicy: IfNotPresent
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: 'supervisor_image\s*=\s*"registry\.example\.com/openshell/supervisor:v0\.1\.0"'
- matchRegex:
path: data["gateway.toml"]
pattern: 'sandbox_runtime_image\s*=\s*"registry\.example\.com/openshell/sandbox:v0\.1\.0"'
- it: lets an individual registry override the global registry
template: templates/statefulset.yaml
set:
global.image.registry: registry.example.com
gateway.image.registry: mirror.example.com
gateway.image.repository: custom/gateway
global.image.tag: v0.1.0
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: mirror.example.com/custom/gateway:v0.1.0
- it: applies the global registry to the gateway image
template: templates/statefulset.yaml
set:
global.image.registry: registry.example.com
global.image.tag: v0.1.0
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: registry.example.com/openshell/gateway:v0.1.0
- it: defaults a repository-only sandbox image to latest
template: templates/gateway-config.yaml
set:
sandbox.image.repository: registry.example/sandbox
sandbox.image.tag: ""
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: 'default_image\s*=\s*"registry\.example/sandbox:latest"'
- it: gives a gateway digest precedence over its tag
template: templates/statefulset.yaml
set:
gateway.image.registry: registry.example
gateway.image.repository: gateway
gateway.image.tag: ignored
gateway.image.digest: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
asserts:
- equal:
path: spec.template.spec.containers[0].image
value: registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- it: gives supervisor and sandbox digests precedence over their tags
template: templates/gateway-config.yaml
set:
supervisor.image.registry: registry.example
supervisor.image.repository: supervisor
supervisor.image.tag: ignored
supervisor.image.digest: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
sandbox.image.repository: registry.example/sandbox
sandbox.image.tag: ignored
sandbox.image.digest: sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: 'registry\.example/supervisor@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
- matchRegex:
path: data["gateway.toml"]
pattern: 'default_image\s*=\s*"registry\.example/sandbox@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"'
# Regression for Drew's P2: a ConfigMap-only mutation in `helm upgrade`
# must roll the StatefulSet, otherwise pods keep running with stale config.
- it: annotates the StatefulSet pod template with a ConfigMap checksum
@@ -224,17 +321,17 @@ tests:
path: data["gateway.toml"]
pattern: '(?ms)\[openshell\.gateway\][^\[]*?grpc_endpoint'
- it: omits pull policies by default so Kubernetes applies its own defaults
- it: applies the global pull policy by default
template: templates/gateway-config.yaml
asserts:
- notMatchRegex:
- matchRegex:
path: data["gateway.toml"]
pattern: '(?m)^\s*(image_pull_policy|supervisor_image_pull_policy)\s*='
pattern: '(?ms)supervisor_image_pull_policy\s*=\s*"if_not_present".*?sandbox_runtime_image_pull_policy\s*=\s*"if_not_present"'
- it: renders canonical image pull policies in the Kubernetes driver table
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: if_not_present
sandbox.image.pullPolicy: if_not_present
supervisor.image.pullPolicy: never
asserts:
- matchRegex:
@@ -244,7 +341,7 @@ tests:
- it: translates legacy Kubernetes pull policy values to canonical gateway values
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: Always
sandbox.image.pullPolicy: Always
supervisor.image.pullPolicy: IfNotPresent
asserts:
- matchRegex:
@@ -254,7 +351,7 @@ tests:
- it: rejects unsupported sandbox image pull policies
template: templates/statefulset.yaml
set:
server.sandboxImagePullPolicy: Sometimes
sandbox.image.pullPolicy: Sometimes
asserts:
- failedTemplate:
errorMessage: 'image pull policy "Sometimes" must be one of: always, if_not_present, never, Always, IfNotPresent, Never'
@@ -344,12 +441,12 @@ tests:
path: data["gateway.toml"]
pattern: 'proxy_ca_bundle\s*='
- it: uses the gateway built-in supervisor image by default
- it: renders the default supervisor image
template: templates/gateway-config.yaml
asserts:
- notMatchRegex:
- matchRegex:
path: data["gateway.toml"]
pattern: 'supervisor_image\s*='
pattern: 'supervisor_image\s*=\s*"ghcr\.io/nvidia/openshell/supervisor:0\.0\.0"'
- it: uses the gateway built-in sandbox runtime image by default
template: templates/gateway-config.yaml
@@ -361,10 +458,12 @@ tests:
- it: renders independent sandbox runtime and supervisor image overrides
template: templates/gateway-config.yaml
set:
sandboxRuntime.image.repository: registry.example.com/openshell/sandbox
sandboxRuntime.image.registry: registry.example.com
sandboxRuntime.image.repository: openshell/sandbox
sandboxRuntime.image.tag: sandbox-build
sandboxRuntime.image.pullPolicy: Always
supervisor.image.repository: registry.example.com/openshell/supervisor
supervisor.image.registry: registry.example.com
supervisor.image.repository: openshell/supervisor
supervisor.image.tag: supervisor-build
asserts:
- matchRegex:
@@ -377,6 +476,18 @@ tests:
path: data["gateway.toml"]
pattern: 'supervisor_image\s*=\s*"registry\.example\.com/openshell/supervisor:supervisor-build"'
- it: gives a sandbox runtime digest precedence over its tag
template: templates/gateway-config.yaml
set:
sandboxRuntime.image.registry: registry.example.com
sandboxRuntime.image.repository: openshell/sandbox
sandboxRuntime.image.tag: ignored
sandboxRuntime.image.digest: sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: 'sandbox_runtime_image\s*=\s*"registry\.example\.com/openshell/sandbox@sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"'
- it: renders a supervisor tag override with the official repository
template: templates/gateway-config.yaml
set:
@@ -386,20 +497,21 @@ tests:
path: data["gateway.toml"]
pattern: 'supervisor_image\s*=\s*"ghcr\.io/nvidia/openshell/supervisor:1\.2\.3"'
- it: renders a supervisor repository override with the effective gateway tag
- it: renders a supervisor repository override with the default chart tag
template: templates/gateway-config.yaml
set:
image.tag: gateway-build
supervisor.image.repository: registry.example.com/openshell/supervisor
supervisor.image.registry: registry.example.com
supervisor.image.repository: openshell/supervisor
asserts:
- matchRegex:
path: data["gateway.toml"]
pattern: 'supervisor_image\s*=\s*"registry\.example\.com/openshell/supervisor:gateway-build"'
pattern: 'supervisor_image\s*=\s*"registry\.example\.com/openshell/supervisor:0\.0\.0"'
- it: renders complete supervisor repository and tag overrides
template: templates/gateway-config.yaml
set:
supervisor.image.repository: registry.example.com/openshell/supervisor
supervisor.image.registry: registry.example.com
supervisor.image.repository: openshell/supervisor
supervisor.image.tag: supervisor-build
asserts:
- matchRegex:
@@ -1058,7 +1170,7 @@ tests:
- it: accepts Always pull policy spelling for sandbox and supervisor
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: Always
sandbox.image.pullPolicy: Always
supervisor.image.pullPolicy: Always
asserts:
- matchRegex:
@@ -1068,7 +1180,7 @@ tests:
- it: accepts IfNotPresent pull policy spelling for sandbox and supervisor
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: IfNotPresent
sandbox.image.pullPolicy: IfNotPresent
supervisor.image.pullPolicy: IfNotPresent
asserts:
- matchRegex:
@@ -1078,7 +1190,7 @@ tests:
- it: accepts Never pull policy spelling for sandbox and supervisor
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: Never
sandbox.image.pullPolicy: Never
supervisor.image.pullPolicy: Never
asserts:
- matchRegex:
@@ -1088,7 +1200,7 @@ tests:
- it: accepts canonical lowercase pull policies for sandbox and supervisor
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: always
sandbox.image.pullPolicy: always
supervisor.image.pullPolicy: always
asserts:
- matchRegex:
@@ -1098,7 +1210,7 @@ tests:
- it: accepts canonical if_not_present pull policies for sandbox and supervisor
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: if_not_present
sandbox.image.pullPolicy: if_not_present
supervisor.image.pullPolicy: if_not_present
asserts:
- matchRegex:
@@ -1108,7 +1220,7 @@ tests:
- it: accepts canonical never pull policies for sandbox and supervisor
template: templates/gateway-config.yaml
set:
server.sandboxImagePullPolicy: never
sandbox.image.pullPolicy: never
supervisor.image.pullPolicy: never
asserts:
- matchRegex:
+51 -25
View File
@@ -7,6 +7,15 @@
# server.externalDbSecret because the default SQLite backend is per pod.
replicaCount: 1
global:
image:
# -- Shared OpenShell image registry. Individual image registries take precedence.
registry: ghcr.io/nvidia
# -- Shared OpenShell image tag. Defaults to the chart appVersion when empty.
tag: ""
# -- Shared OpenShell image pull policy. Individual image pull policies take precedence.
pullPolicy: IfNotPresent
workload:
# -- Gateway workload controller kind. Use `statefulset` for the default
# SQLite database, or `deployment` when server.externalDbSecret points at an
@@ -18,41 +27,65 @@ workload:
# StatefulSet identity or storage semantics.
allowMultiReplicaStatefulSet: false
image:
# -- Gateway image repository.
repository: ghcr.io/nvidia/openshell/gateway
# -- Gateway image pull policy.
pullPolicy: IfNotPresent
# -- Gateway image tag. Defaults to the chart appVersion when empty.
tag: ""
gateway:
image:
# -- Gateway image registry. Empty uses global.image.registry.
registry: ""
# -- Gateway image repository.
repository: openshell/gateway
# -- Gateway image pull policy. Empty uses global.image.pullPolicy.
pullPolicy: null
# -- Gateway image tag. Defaults to the chart appVersion when empty.
tag: ""
# -- Gateway image digest. When set, this takes precedence over tag.
digest: ""
# Trusted workload-side runtime image.
sandboxRuntime:
image:
# -- Sandbox runtime image repository. Changing it uses the effective gateway image tag unless tag is also set.
repository: ghcr.io/nvidia/openshell/sandbox
# -- Sandbox runtime image pull policy. Defaults to the gateway image pull policy when empty.
pullPolicy: ""
# -- Sandbox runtime image tag override. Empty uses the version pinned into the gateway unless repository is changed.
# -- Sandbox runtime image registry. Empty uses global.image.registry.
registry: ""
# -- Sandbox runtime image repository.
repository: openshell/sandbox
# -- Sandbox runtime image pull policy. Empty uses global.image.pullPolicy.
pullPolicy: null
# -- Sandbox runtime image tag. Defaults to the chart appVersion when empty.
tag: ""
# -- Sandbox runtime image digest. When set, this takes precedence over tag.
digest: ""
# Trusted control-side runtime image.
supervisor:
image:
# -- Supervisor image repository. Changing it uses the effective gateway image tag unless tag is also set.
repository: ghcr.io/nvidia/openshell/supervisor
# -- Sandbox supervisor pull policy. Leave unset to use the Kubernetes
# image default. Prefer always, if_not_present, or never; the chart also
# accepts legacy Kubernetes spellings Always, IfNotPresent, and Never.
# -- Supervisor image registry. Empty uses global.image.registry.
registry: ""
# -- Supervisor image repository.
repository: openshell/supervisor
# -- Supervisor image pull policy. Empty uses global.image.pullPolicy.
# Prefer always, if_not_present, or never; the chart also accepts legacy
# Kubernetes spellings Always, IfNotPresent, and Never.
pullPolicy: null
# -- Supervisor image tag override. Empty uses the version pinned into the gateway unless repository is changed.
# -- Supervisor image tag. Defaults to the chart appVersion when empty.
tag: ""
# -- Supervisor image digest. When set, this takes precedence over tag.
digest: ""
sandboxRuntime:
# -- Required operator acknowledgement that the cluster CNI enforces NetworkPolicy.
networkPolicyEnforced: false
# -- Workload boundary TLS listener port.
boundaryPort: 5500
sandbox:
image:
# -- Default standalone sandbox image repository.
repository: nvcr.io/nvidia/base/ubuntu
# -- Sandbox image tag. Defaults to latest when empty.
tag: "24.04"
# -- Sandbox image digest. When set, this takes precedence over tag.
digest: ""
# -- Sandbox image pull policy. Leave unset to use the Kubernetes image default.
pullPolicy: null
# -- Operator-owned corporate forward proxy for policy-approved TLS egress
# from Kubernetes sandboxes. The workload cannot select or override it.
upstreamProxy:
@@ -230,13 +263,6 @@ server:
# from this Secret instead of using dbUrl. The Secret must contain a
# `uri` key, e.g. postgresql://user:pass@host:5432/dbname.
externalDbSecret: ""
# -- Default sandbox image used when requests do not specify one.
sandboxImage: "nvcr.io/nvidia/base/ubuntu:24.04"
# -- Pull policy for sandbox pods. Leave unset to use the Kubernetes image
# default (Always for :latest, IfNotPresent otherwise). Prefer always,
# if_not_present, or never; the chart also accepts legacy Kubernetes spellings
# Always, IfNotPresent, and Never.
sandboxImagePullPolicy: null
# -- Image pull secrets attached to sandbox pods. Referenced Secrets must exist
# in the sandbox namespace.
sandboxImagePullSecrets: []
+17 -7
View File
@@ -24,14 +24,24 @@ spec:
createNamespace: true
valuesContent: |-
chartChecksum: __CHART_CHECKSUM__
image:
repository: ghcr.io/nvidia/openshell/gateway
tag: latest
pullPolicy: __IMAGE_PULL_POLICY__
global:
image:
registry: ghcr.io/nvidia
gateway:
image:
repository: openshell/gateway
tag: latest
pullPolicy: __IMAGE_PULL_POLICY__
supervisor:
image:
repository: openshell/supervisor
tag: latest
sandbox:
image:
repository: nvcr.io/nvidia/base/ubuntu
tag: "24.04"
pullPolicy: __SANDBOX_IMAGE_PULL_POLICY__
server:
sandboxImage: nvcr.io/nvidia/base/ubuntu:24.04
sandboxImagePullPolicy: __SANDBOX_IMAGE_PULL_POLICY__
supervisorImage: ghcr.io/nvidia/openshell/supervisor:latest
dbUrl: __DB_URL__
hostGatewayIP: __HOST_GATEWAY_IP__
disableTls: __DISABLE_TLS__
+6 -3
View File
@@ -52,7 +52,7 @@ The chart does not install or upgrade the cluster-scoped Agent Sandbox CRDs or
controller.
<Note>
**Air-gapped clusters:** mirror the manifest above and the `registry.k8s.io/agent-sandbox/agent-sandbox-controller` image referenced inside it to your internal registry, then point the manifest's image reference at your mirror before applying. You will also need to mirror the OpenShell gateway and sandbox images — see the chart's `image.repository` value for the gateway and `server.sandboxImage` / `server.supervisorImage` for the sandbox runtime.
**Air-gapped clusters:** mirror the manifest above and the `registry.k8s.io/agent-sandbox/agent-sandbox-controller` image referenced inside it to your internal registry, then point the manifest's image reference at your mirror before applying. Mirror the OpenShell gateway, supervisor, and trusted sandbox runtime images, then set `global.image.registry` to the mirror. Configure the separate default workload sandbox image with `sandbox.image` when needed.
</Note>
Confirm the controller pod is running before proceeding:
@@ -219,7 +219,8 @@ The most commonly changed values are:
| Value | Purpose |
|---|---|
| `image.repository` / `image.tag` | Gateway container image. Defaults to `ghcr.io/nvidia/openshell/gateway:latest`. |
| `global.image.registry` / `global.image.tag` / `global.image.pullPolicy` | Shared registry, tag, and pull policy for the gateway, supervisor, and trusted sandbox runtime. Individual image settings take precedence. |
| `gateway.image.registry` / `gateway.image.repository` / `gateway.image.tag` / `gateway.image.digest` | Gateway container image. Defaults to `ghcr.io/nvidia/openshell/gateway:latest`. |
| `replicaCount` | Number of gateway replicas. Values above `1` require shared PostgreSQL through `server.externalDbSecret`. |
| `workload.kind` | Gateway workload controller. Use `statefulset` for SQLite or `deployment` with `server.externalDbSecret`. |
| `workload.allowMultiReplicaStatefulSet` | Allow `replicaCount > 1` with `workload.kind=statefulset`. Prefer Deployment for external database-backed multi-replica gateways. |
@@ -227,7 +228,9 @@ The most commonly changed values are:
| `workspaceResources.enabled` | Create namespace-scoped sandbox prerequisites from the gateway chart. Disable when installing the workspace chart separately. |
| `server.externalDbSecret` | Secret containing a PostgreSQL connection URI in the `uri` key. Use when the database is managed outside the chart. |
| `server.telemetryEnabled` | Enable anonymous OpenShell telemetry from the gateway and its sandbox supervisors. Set to `false` to opt out. |
| `server.sandboxImage` | Default sandbox image used when a sandbox does not specify one. |
| `sandbox.image.repository` / `sandbox.image.tag` / `sandbox.image.digest` | Default sandbox image used when a sandbox does not specify one. |
| `sandboxRuntime.image.registry` / `sandboxRuntime.image.repository` / `sandboxRuntime.image.tag` / `sandboxRuntime.image.digest` | Trusted workload-side image that provides the `openshell-sandbox` binary. A digest takes precedence over the tag. |
| `supervisor.image.registry` / `supervisor.image.repository` / `supervisor.image.tag` / `supervisor.image.digest` | Trusted control-side supervisor image. |
| `server.sandboxImagePullSecrets` | Image pull secrets attached to sandbox pods. Referenced Secrets must exist in the sandbox namespace. |
| `server.grpcEndpoint` | Endpoint that sandbox supervisors use to call back to the gateway. Must be reachable from inside the cluster. |
| `server.disableTls` | Run the gateway over plaintext HTTP. Use only behind a trusted transport. |
+5 -5
View File
@@ -461,16 +461,16 @@ For maintainer-level implementation details, refer to the [Kubernetes driver REA
| `compute_driver = "kubernetes"` | Not applicable | Select the Kubernetes compute driver. |
| `[openshell.drivers.kubernetes].namespace` | `server.sandboxNamespace` | Set the namespace for sandbox resources. The Helm chart defaults to the release namespace when left empty. |
| `service_account_name` | `sandboxServiceAccount.name` | Set the Kubernetes service account assigned to sandbox pods and accepted by the Kubernetes driver's TokenReview bootstrap path. The Helm chart creates a dedicated sandbox service account by default. |
| `default_image` | `server.sandboxImage` | Set the default sandbox image. |
| `image_pull_policy` | `server.sandboxImagePullPolicy` | Set the Kubernetes image pull policy for sandbox pods. |
| `default_image` | `sandbox.image.repository` / `sandbox.image.tag` / `sandbox.image.digest` | Set the default sandbox image. |
| `image_pull_policy` | `sandbox.image.pullPolicy` | Set the canonical sandbox pull policy: `always`, `if_not_present`, or `never`. `newer` is Podman-only. |
| `image_pull_secrets` | `server.sandboxImagePullSecrets` | Attach Kubernetes image-pull Secrets to sandbox pods. Managed mode creates an immutable copy of each Secret from the configured source namespace for every sandbox runtime generation. In shared and operator modes, the Secrets must already exist in the sandbox namespace. |
| `[managed_ssh_ingress]` | `networkPolicy.enabled` | In managed mode, create an SSH ingress policy in every workspace namespace. Helm configures the gateway namespace and pod selector automatically. Operator mode leaves namespace policy management to the platform operator. |
| `grpc_endpoint` | `server.grpcEndpoint` | Set the gateway endpoint reachable from sandbox pods. |
| `client_tls_secret_name` | `server.tls.clientTlsSecretName` | Name the Kubernetes Secret holding sandbox client TLS materials. Shared mode mounts it directly; managed and operator modes stage its contents into each supervisor bootstrap Secret. |
| `sandbox_runtime_image` | `sandboxRuntime.image.repository` / `sandboxRuntime.image.tag` | Override the image that provides `openshell-sandbox`. The default repository with an empty tag uses the version pinned into the gateway. |
| `sandbox_runtime_image` | `sandboxRuntime.image.registry` / `sandboxRuntime.image.repository` / `sandboxRuntime.image.tag` / `sandboxRuntime.image.digest` | Override the trusted workload-side image that provides `openshell-sandbox`. Individual image values take precedence over `global.image`; a digest takes precedence over the tag. |
| `sandbox_runtime_image_pull_policy` | `sandboxRuntime.image.pullPolicy` | Set the Kubernetes image pull policy for the sandbox runtime image. |
| `supervisor_image` | `supervisor.image.repository` / `supervisor.image.tag` | Override the image that provides `openshell-supervisor`. The default repository with an empty tag uses the version pinned into the gateway. |
| `supervisor_image_pull_policy` | `supervisor.image.pullPolicy` | Set the Kubernetes image pull policy for the supervisor image. |
| `supervisor_image` | `supervisor.image.registry` / `supervisor.image.repository` / `supervisor.image.tag` / `supervisor.image.digest` | Override the image that provides `openshell-supervisor`. Individual image values take precedence over `global.image`; a digest takes precedence over the tag. |
| `supervisor_image_pull_policy` | `supervisor.image.pullPolicy` | Set the canonical supervisor pull policy: `always`, `if_not_present`, or `never`. `newer` is Podman-only. |
| `sandbox_runtime.network_policy_enforced` | `supervisor.sandboxRuntime.networkPolicyEnforced` | Acknowledge that the cluster CNI enforces ingress and egress `NetworkPolicy` in sandbox namespaces. This must be `true`. |
| `sandbox_runtime.boundary_port` | `supervisor.sandboxRuntime.boundaryPort` | Set the non-privileged TLS port used between the paired supervisor and sandbox Pods. |
| `https_proxy` | `upstreamProxy.url` | Set the operator-owned `http://host:port` or `https://host:port` corporate forward proxy used for policy-approved TLS CONNECT egress. |
+4 -2
View File
@@ -91,8 +91,10 @@ To override the default image references, use Helm values:
| Helm value | Purpose |
|---|---|
| `image.repository` / `image.tag` | Override the gateway image reference. |
| `server.sandboxImage` | Override the default sandbox image. |
| `global.image.registry` / `global.image.tag` / `global.image.pullPolicy` | Override shared image settings for the gateway, supervisor, and trusted sandbox runtime. |
| `gateway.image.registry` / `gateway.image.repository` / `gateway.image.tag` / `gateway.image.digest` | Override the gateway image reference. |
| `supervisor.image.registry` / `supervisor.image.repository` / `supervisor.image.tag` / `supervisor.image.digest` | Override the supervisor image reference. |
| `sandbox.image.repository` / `sandbox.image.tag` / `sandbox.image.digest` | Override the default workload sandbox image. |
## Kernel Requirements
+3 -2
View File
@@ -38,8 +38,9 @@ on local Docker Desktop, or via `--add-host ...:host-gateway` on local Linux.
The generated policy uses `protocol: mcp`, inserts the conformance runner's spec revision into the endpoint allowlist, and sets `mcp.allow_all_known_mcp_methods: true` so omitted rule methods use the endpoint MCP method profile. OpenShell enforces that allowlist on each non-initialize request using `MCP-Protocol-Version`, with `2025-03-26` as the missing-header fallback. The conformance runner selects the revision used by its client and server; OpenShell's request-version check does not yet provide complete revision-specific message parsing or response validation. The policy keeps OpenShell deny-by-default at the network boundary while allowing the upstream scenarios to exercise MCP behavior. The policy body lives in `policy-template.yaml`; the wrapper renders its MCP revision, host, port, and path placeholders from the upstream server URL.
For local runs, the wrapper builds `openshell/supervisor:dev` automatically
when no supervisor image override is set. Set `OPENSHELL_DOCKER_SUPERVISOR_IMAGE`
or `OPENSHELL_SUPERVISOR_IMAGE` to use a prebuilt pullable image instead.
when no supervisor image override is set. Set `SUPERVISOR_IMAGE` to use a
prebuilt pullable image instead. The legacy `OPENSHELL_DOCKER_SUPERVISOR_IMAGE`
and `OPENSHELL_SUPERVISOR_IMAGE` overrides remain supported and take precedence.
The pinned upstream checkout includes reference-client fixture drift that is
tracked in `modelcontextprotocol/conformance#345`. The wrapper patches the
+82
View File
@@ -9,6 +9,88 @@
# Keep an explicit override so telemetry-specific tests can opt back in.
export OPENSHELL_TELEMETRY_ENABLED="${OPENSHELL_TELEMETRY_ENABLED:-false}"
# Resolve a test image override. Repository-only values inherit the caller's
# tag, while tagged and digest-pinned references are already complete.
e2e_image_reference_is_complete() {
local image=$1
local last_component="${image##*/}"
[[ "${image}" == *@* || "${last_component}" == *:* ]]
}
e2e_image_reference_has_digest() {
[[ "$1" == *@* ]]
}
e2e_resolve_image_reference() {
local image=$1
local tag=$2
if e2e_image_reference_is_complete "${image}"; then
printf '%s\n' "${image}"
else
printf '%s:%s\n' "${image%/}" "${tag}"
fi
}
e2e_image_reference_repository() {
local image=$1
local repository="${image%%@*}"
local last_component="${repository##*/}"
if [[ "${last_component}" == *:* ]]; then
repository="${repository%:*}"
fi
printf '%s\n' "${repository}"
}
# Return the registry portion of an image repository. Docker treats the first
# path component as a registry when it contains a dot or colon, or is
# `localhost`; otherwise the image uses the configured/default registry.
e2e_image_reference_registry() {
local repository
local first_component
repository="$(e2e_image_reference_repository "$1")"
first_component="${repository%%/*}"
if [[ "${repository}" == */* ]] \
&& { [[ "${first_component}" == *.* ]] || [[ "${first_component}" == *:* ]] || [[ "${first_component}" == "localhost" ]]; }; then
printf '%s\n' "${first_component}"
fi
}
# Return the repository path without its registry, suitable for Helm's
# <component>.image.repository values.
e2e_image_reference_repository_path() {
local repository
local registry
repository="$(e2e_image_reference_repository "$1")"
registry="$(e2e_image_reference_registry "$1")"
if [ -n "${registry}" ]; then
printf '%s\n' "${repository#"${registry}"/}"
else
printf '%s\n' "${repository}"
fi
}
e2e_image_reference_tag() {
local image=$1
local repository="${image%%@*}"
local last_component="${repository##*/}"
if [[ "${image}" == *@* || "${last_component}" != *:* ]]; then
return 0
fi
printf '%s\n' "${last_component##*:}"
}
e2e_image_reference_digest() {
if [[ "$1" == *@* ]]; then
printf '%s\n' "${1#*@}"
fi
}
e2e_cargo_target_dir() {
local root=$1
shift
+20 -1
View File
@@ -17,6 +17,11 @@
# Sandbox image overrides:
# OPENSHELL_E2E_DOCKER_SANDBOX_IMAGE=...
# OPENSHELL_E2E_DOCKER_SANDBOX_IMAGE_PULL_POLICY=always|if_not_present|never
# SANDBOX_IMAGE=... (trusted sandbox runtime override)
# Supervisor image overrides:
# SUPERVISOR_IMAGE=... (common test-wrapper override)
# OPENSHELL_SUPERVISOR_IMAGE=... (existing compatibility override)
# OPENSHELL_DOCKER_SUPERVISOR_IMAGE=... (Docker-specific override)
#
# The default sandbox image uses a mutable tag. This wrapper refreshes it
# before starting the gateway, while the Docker driver defaults to
@@ -323,6 +328,16 @@ resolve_docker_supervisor_image() {
return 0
fi
if [ -n "${SUPERVISOR_IMAGE:-}" ]; then
if [ -n "${CI:-}" ] && [ -z "${IMAGE_TAG:-}" ] \
&& ! e2e_image_reference_is_complete "${SUPERVISOR_IMAGE}"; then
echo "ERROR: IMAGE_TAG must be set in CI when SUPERVISOR_IMAGE is repository-only." >&2
exit 2
fi
printf '%s\n' "$(e2e_resolve_image_reference "${SUPERVISOR_IMAGE}" "${IMAGE_TAG:-dev}")"
return 0
fi
if [ -n "${CI:-}" ]; then
if [ -z "${IMAGE_TAG:-}" ]; then
echo "ERROR: IMAGE_TAG must be set in CI when no Docker supervisor image override is provided." >&2
@@ -347,6 +362,10 @@ resolve_docker_sandbox_runtime_image() {
printf '%s\n' "${OPENSHELL_SANDBOX_RUNTIME_IMAGE}"
return 0
fi
if [ -n "${SANDBOX_IMAGE:-}" ]; then
printf '%s\n' "$(e2e_resolve_image_reference "${SANDBOX_IMAGE}" "${IMAGE_TAG:-dev}")"
return 0
fi
if [ -n "${CI:-}" ]; then
if [ -z "${IMAGE_TAG:-}" ]; then
@@ -443,7 +462,7 @@ ensure_docker_supervisor_image() {
fi
echo "ERROR: supervisor image '${image}' is not available." >&2
echo " Build it, push it, or set OPENSHELL_SUPERVISOR_IMAGE to a pullable image." >&2
echo " Build it, push it, or set SUPERVISOR_IMAGE/OPENSHELL_SUPERVISOR_IMAGE to a pullable image." >&2
exit 2
}
+44 -24
View File
@@ -652,12 +652,9 @@ run_scenario() {
--namespace "${NAMESPACE}" --create-namespace \
"${helm_values_args[@]}" \
--set "fullnameOverride=openshell" \
--set "image.repository=${REGISTRY_VALUE}/gateway" \
--set "image.tag=${IMAGE_TAG_VALUE}" \
--set "sandboxRuntime.image.repository=${REGISTRY_VALUE}/sandbox" \
--set "sandboxRuntime.image.tag=${IMAGE_TAG_VALUE}" \
--set "supervisor.image.repository=${REGISTRY_VALUE}/supervisor" \
--set "supervisor.image.tag=${IMAGE_TAG_VALUE}" \
"${GATEWAY_HELM_IMAGE_ARGS[@]}" \
"${SUPERVISOR_HELM_IMAGE_ARGS[@]}" \
"${SANDBOX_RUNTIME_HELM_IMAGE_ARGS[@]}" \
"${helm_post_renderer_args[@]}" \
"$@" \
--wait --timeout 5m
@@ -940,6 +937,14 @@ else
IMAGE_TAG_VALUE="${IMAGE_TAG:-latest}"
fi
REGISTRY_VALUE="${REGISTRY_VALUE%/}"
GATEWAY_IMAGE="$(e2e_resolve_image_reference "${GATEWAY_IMAGE:-${REGISTRY_VALUE}/gateway}" "${IMAGE_TAG_VALUE}")"
SUPERVISOR_IMAGE="$(e2e_resolve_image_reference "${SUPERVISOR_IMAGE:-${REGISTRY_VALUE}/supervisor}" "${IMAGE_TAG_VALUE}")"
SANDBOX_RUNTIME_IMAGE="$(e2e_resolve_image_reference "${SANDBOX_IMAGE:-${REGISTRY_VALUE}/sandbox}" "${IMAGE_TAG_VALUE}")"
BUILD_GATEWAY_IMAGE="${REGISTRY_VALUE}/gateway:${IMAGE_TAG_VALUE}"
BUILD_SUPERVISOR_IMAGE="${REGISTRY_VALUE}/supervisor:${IMAGE_TAG_VALUE}"
GATEWAY_HELM_IMAGE_ARGS=(--set-string "gateway.image.registry=$(e2e_image_reference_registry "${GATEWAY_IMAGE}")" --set-string "gateway.image.repository=$(e2e_image_reference_repository_path "${GATEWAY_IMAGE}")" --set-string "gateway.image.tag=$(e2e_image_reference_tag "${GATEWAY_IMAGE}")" --set-string "gateway.image.digest=$(e2e_image_reference_digest "${GATEWAY_IMAGE}")")
SUPERVISOR_HELM_IMAGE_ARGS=(--set-string "supervisor.image.registry=$(e2e_image_reference_registry "${SUPERVISOR_IMAGE}")" --set-string "supervisor.image.repository=$(e2e_image_reference_repository_path "${SUPERVISOR_IMAGE}")" --set-string "supervisor.image.tag=$(e2e_image_reference_tag "${SUPERVISOR_IMAGE}")" --set-string "supervisor.image.digest=$(e2e_image_reference_digest "${SUPERVISOR_IMAGE}")")
SANDBOX_RUNTIME_HELM_IMAGE_ARGS=(--set-string "sandboxRuntime.image.registry=$(e2e_image_reference_registry "${SANDBOX_RUNTIME_IMAGE}")" --set-string "sandboxRuntime.image.repository=$(e2e_image_reference_repository_path "${SANDBOX_RUNTIME_IMAGE}")" --set-string "sandboxRuntime.image.tag=$(e2e_image_reference_tag "${SANDBOX_RUNTIME_IMAGE}")" --set-string "sandboxRuntime.image.digest=$(e2e_image_reference_digest "${SANDBOX_RUNTIME_IMAGE}")")
# Resolve a host-gateway IP that sandbox pods can dial to reach test fixtures
# running on the developer/CI host (HTTP fixtures bound to 0.0.0.0 plus sibling
@@ -1032,7 +1037,7 @@ elif [[ "${KUBE_CONTEXT}" == k3d-* ]] && command -v k3d >/dev/null 2>&1; then
fi
if [ "${OPENSHELL_E2E_KUBE_BUILD_IMAGES}" = "1" ]; then
require_cmd docker
echo "Building local Kubernetes e2e images (${REGISTRY_VALUE}/{gateway,sandbox,supervisor}:${IMAGE_TAG_VALUE})..."
echo "Building local Kubernetes e2e images (${BUILD_GATEWAY_IMAGE}, ${BUILD_SUPERVISOR_IMAGE})..."
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" = "1" ]; then
if [ "$(uname -s)" != "Linux" ]; then
echo "ERROR: external Kubernetes driver image composition currently requires a Linux build host." >&2
@@ -1061,9 +1066,9 @@ if [ "${OPENSHELL_E2E_KUBE_BUILD_IMAGES}" = "1" ]; then
cp "${external_driver}" "${external_stage}/openshell-driver-kubernetes"
docker build \
--build-arg "TARGETARCH=${external_arch}" \
--build-arg "SUPERVISOR_IMAGE=${REGISTRY_VALUE}/supervisor:${IMAGE_TAG_VALUE}" \
--build-arg "SUPERVISOR_IMAGE=${BUILD_SUPERVISOR_IMAGE}" \
--build-arg "SANDBOX_RUNTIME_IMAGE=${REGISTRY_VALUE}/sandbox:${IMAGE_TAG_VALUE}" \
--tag "${REGISTRY_VALUE}/gateway:${IMAGE_TAG_VALUE}" \
--tag "${BUILD_GATEWAY_IMAGE}" \
--file "${ROOT}/e2e/docker/Dockerfile.external-kubernetes-gateway" \
"${ROOT}"
else
@@ -1071,7 +1076,11 @@ if [ "${OPENSHELL_E2E_KUBE_BUILD_IMAGES}" = "1" ]; then
bash "${ROOT}/tasks/scripts/docker-build-image.sh" gateway
fi
sandbox_image="${REGISTRY_VALUE}/sandbox:${IMAGE_TAG_VALUE}"
supervisor_image="${REGISTRY_VALUE}/supervisor:${IMAGE_TAG_VALUE}"
if [ "${GATEWAY_IMAGE}" != "${BUILD_GATEWAY_IMAGE}" ]; then
if e2e_image_reference_has_digest "${GATEWAY_IMAGE}"; then echo "ERROR: digest-pinned GATEWAY_IMAGE requires OPENSHELL_E2E_KUBE_BUILD_IMAGES=0" >&2; exit 2; fi
docker tag "${BUILD_GATEWAY_IMAGE}" "${GATEWAY_IMAGE}"
fi
supervisor_image="${BUILD_SUPERVISOR_IMAGE}"
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" != "1" ] \
|| ! docker image inspect "${sandbox_image}" >/dev/null 2>&1; then
CONTAINER_ENGINE=docker IMAGE_REGISTRY="${REGISTRY_VALUE}" IMAGE_TAG="${IMAGE_TAG_VALUE}" \
@@ -1080,6 +1089,13 @@ if [ "${OPENSHELL_E2E_KUBE_BUILD_IMAGES}" = "1" ]; then
reuse_sandbox_image=1
echo "Reusing existing sandbox image ${sandbox_image}"
fi
if [ "${SANDBOX_RUNTIME_IMAGE}" != "${sandbox_image}" ]; then
if e2e_image_reference_has_digest "${SANDBOX_RUNTIME_IMAGE}"; then
echo "ERROR: digest-pinned SANDBOX_IMAGE requires OPENSHELL_E2E_KUBE_BUILD_IMAGES=0" >&2
exit 2
fi
docker tag "${sandbox_image}" "${SANDBOX_RUNTIME_IMAGE}"
fi
if [ "${OPENSHELL_E2E_EXTERNAL_COMPUTE_DRIVER:-0}" != "1" ] \
|| ! docker image inspect "${supervisor_image}" >/dev/null 2>&1; then
CONTAINER_ENGINE=docker IMAGE_REGISTRY="${REGISTRY_VALUE}" IMAGE_TAG="${IMAGE_TAG_VALUE}" \
@@ -1088,13 +1104,18 @@ if [ "${OPENSHELL_E2E_KUBE_BUILD_IMAGES}" = "1" ]; then
reuse_supervisor_image=1
echo "Reusing existing supervisor image ${supervisor_image}"
fi
if [ "${SUPERVISOR_IMAGE}" != "${BUILD_SUPERVISOR_IMAGE}" ]; then
if e2e_image_reference_has_digest "${SUPERVISOR_IMAGE}"; then echo "ERROR: digest-pinned SUPERVISOR_IMAGE requires OPENSHELL_E2E_KUBE_BUILD_IMAGES=0" >&2; exit 2; fi
docker tag "${BUILD_SUPERVISOR_IMAGE}" "${SUPERVISOR_IMAGE}"
fi
fi
if [ -n "${import_cluster_name}" ]; then
for image in \
"${REGISTRY_VALUE}/gateway:${IMAGE_TAG_VALUE}" \
"${GATEWAY_IMAGE}" \
"${REGISTRY_VALUE}/sandbox:${IMAGE_TAG_VALUE}" \
"${REGISTRY_VALUE}/supervisor:${IMAGE_TAG_VALUE}"; do
"${SUPERVISOR_IMAGE}" \
"${SANDBOX_RUNTIME_IMAGE}"; do
if docker image inspect "${image}" >/dev/null 2>&1; then
echo "Importing ${image} into k3d cluster ${import_cluster_name}..."
k3d image import "${image}" --cluster "${import_cluster_name}" \
@@ -1105,16 +1126,18 @@ elif [ "${OPENSHELL_E2E_KUBE_BUILD_IMAGES}" = "1" ] \
&& [[ "${KUBE_CONTEXT}" == kind-* ]] \
&& command -v kind >/dev/null 2>&1; then
kind_cluster_name="${KUBE_CONTEXT#kind-}"
kind_images=("${REGISTRY_VALUE}/gateway:${IMAGE_TAG_VALUE}")
kind_images=("${GATEWAY_IMAGE}")
# The CI workflow loads its published sandbox archive before invoking this
# wrapper. Only load a sandbox image here when this script rebuilt it.
if [ "${reuse_sandbox_image}" != "1" ]; then
kind_images+=("${REGISTRY_VALUE}/sandbox:${IMAGE_TAG_VALUE}")
# wrapper. Load a replacement only when this script rebuilt or retagged it.
if [ "${reuse_sandbox_image}" != "1" ] \
|| [ "${SANDBOX_RUNTIME_IMAGE}" != "${sandbox_image}" ]; then
kind_images+=("${SANDBOX_RUNTIME_IMAGE}")
fi
# The CI workflow loads its published supervisor archive before invoking this
# wrapper. Only load a supervisor image here when this script rebuilt it.
if [ "${reuse_supervisor_image}" != "1" ]; then
kind_images+=("${REGISTRY_VALUE}/supervisor:${IMAGE_TAG_VALUE}")
if [ "${reuse_supervisor_image}" != "1" ] \
|| [ "${SUPERVISOR_IMAGE}" != "${BUILD_SUPERVISOR_IMAGE}" ]; then
kind_images+=("${SUPERVISOR_IMAGE}")
fi
for image in "${kind_images[@]}"; do
echo "Loading ${image} into kind cluster ${kind_cluster_name}..."
@@ -1361,12 +1384,9 @@ else
--namespace "${NAMESPACE}" --create-namespace \
"${helm_values_args[@]}" \
--set "fullnameOverride=openshell" \
--set "image.repository=${REGISTRY_VALUE}/gateway" \
--set "image.tag=${IMAGE_TAG_VALUE}" \
--set "sandboxRuntime.image.repository=${REGISTRY_VALUE}/sandbox" \
--set "sandboxRuntime.image.tag=${IMAGE_TAG_VALUE}" \
--set "supervisor.image.repository=${REGISTRY_VALUE}/supervisor" \
--set "supervisor.image.tag=${IMAGE_TAG_VALUE}" \
"${GATEWAY_HELM_IMAGE_ARGS[@]}" \
"${SUPERVISOR_HELM_IMAGE_ARGS[@]}" \
"${SANDBOX_RUNTIME_HELM_IMAGE_ARGS[@]}" \
"${helm_extra_args[@]}" \
"${helm_post_renderer_args[@]}" \
--wait --timeout 5m
+30 -2
View File
@@ -14,6 +14,11 @@
# HTTPS endpoint-only mode is intentionally unsupported here. Use a named
# gateway config when mTLS materials are needed.
#
# Supervisor image overrides:
# SUPERVISOR_IMAGE=... (common test-wrapper override)
# OPENSHELL_SUPERVISOR_IMAGE=... (existing compatibility override)
# SANDBOX_IMAGE=... (trusted sandbox runtime override)
#
# Set OPENSHELL_E2E_PODMAN_STOP_TIMEOUT_SECS to override the managed gateway's
# Podman sandbox stop timeout. The harness default is intentionally shorter
# than the production driver default to keep CI teardown bounded.
@@ -346,6 +351,16 @@ resolve_podman_supervisor_image() {
return 0
fi
if [ -n "${SUPERVISOR_IMAGE:-}" ]; then
if [ -n "${CI:-}" ] && [ -z "${IMAGE_TAG:-}" ] \
&& ! e2e_image_reference_is_complete "${SUPERVISOR_IMAGE}"; then
echo "ERROR: IMAGE_TAG must be set in CI when SUPERVISOR_IMAGE is repository-only." >&2
exit 2
fi
printf '%s\n' "$(e2e_resolve_image_reference "${SUPERVISOR_IMAGE}" "${IMAGE_TAG:-dev}")"
return 0
fi
if [ -n "${CI:-}" ]; then
if [ -z "${IMAGE_TAG:-}" ]; then
echo "ERROR: IMAGE_TAG must be set in CI when no Podman supervisor image override is provided." >&2
@@ -365,6 +380,10 @@ resolve_podman_sandbox_runtime_image() {
printf '%s\n' "${OPENSHELL_SANDBOX_RUNTIME_IMAGE}"
return 0
fi
if [ -n "${SANDBOX_IMAGE:-}" ]; then
printf '%s\n' "$(e2e_resolve_image_reference "${SANDBOX_IMAGE}" "${IMAGE_TAG:-dev}")"
return 0
fi
if [ -n "${CI:-}" ]; then
if [ -z "${IMAGE_TAG:-}" ]; then
@@ -387,6 +406,11 @@ ensure_podman_supervisor_image() {
local dockerfile=${OPENSHELL_E2E_SUPERVISOR_DOCKERFILE:-${ROOT}/deploy/docker/Dockerfile.supervisor}
local context="${WORKDIR}/supervisor-image" arch
case "${image}" in
*@*)
echo "ERROR: supplied supervisor binaries cannot be built to a digest-pinned image reference: ${image}" >&2
echo " Use a tagged image reference when building from OPENSHELL_E2E_SUPERVISOR_BIN." >&2
exit 2
;;
*:dev|*:latest)
echo "ERROR: supplied supervisor binaries require a unique versioned image tag, not ${image}." >&2
exit 2
@@ -470,7 +494,7 @@ ensure_podman_supervisor_image() {
fi
echo "ERROR: supervisor image '${image}' is not available." >&2
echo " Build it, push it, or set OPENSHELL_SUPERVISOR_IMAGE to a pullable image." >&2
echo " Build it, push it, or set SUPERVISOR_IMAGE/OPENSHELL_SUPERVISOR_IMAGE to a pullable image." >&2
exit 2
}
@@ -577,7 +601,11 @@ fi
# isolated XDG store where this image was built. Address the local image by its
# immutable manifest digest so policy=missing cannot resolve a mutable tag or
# contact a registry for a different artifact.
SUPERVISOR_IMAGE_REPOSITORY="${SUPERVISOR_IMAGE%:*}"
SUPERVISOR_IMAGE_REPOSITORY="${SUPERVISOR_IMAGE%%@*}"
last_component="${SUPERVISOR_IMAGE_REPOSITORY##*/}"
if [[ "${last_component}" == *:* ]]; then
SUPERVISOR_IMAGE_REPOSITORY="${SUPERVISOR_IMAGE_REPOSITORY%:*}"
fi
SUPERVISOR_RUNTIME_IMAGE="${SUPERVISOR_IMAGE_REPOSITORY}@${SUPERVISOR_IMAGE_DIGEST}"
if ! [[ "${SUPERVISOR_RUNTIME_IMAGE}" =~ ^[^@]+@sha256:[0-9a-f]{64}$ ]]; then
echo "ERROR: supervisor runtime image is not digest-pinned: ${SUPERVISOR_RUNTIME_IMAGE}" >&2
+114
View File
@@ -0,0 +1,114 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# shellcheck source=e2e/support/gateway-common.sh
source "${ROOT}/e2e/support/gateway-common.sh"
assert_resolves() {
local description=$1
local expected=$2
shift 2
local actual
actual="$(e2e_resolve_image_reference "$@")"
if [ "${actual}" != "${expected}" ]; then
echo "FAIL: ${description}: expected '${expected}', got '${actual}'" >&2
exit 1
fi
}
assert_resolves "repository inherits tag" \
"registry.example/gateway:test" \
"registry.example/gateway" test
assert_resolves "repository trims trailing slash" \
"registry.example/gateway:test" \
"registry.example/gateway/" test
assert_resolves "tagged reference is unchanged" \
"registry.example/gateway:branch" \
"registry.example/gateway:branch" test
assert_resolves "digest reference is unchanged" \
"registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
"registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" test
assert_resolves "registry port inherits tag" \
"localhost:5000/openshell/gateway:test" \
"localhost:5000/openshell/gateway" test
if e2e_image_reference_is_complete "registry.example/gateway:branch" \
&& e2e_image_reference_is_complete "registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
&& ! e2e_image_reference_is_complete "registry.example/gateway" \
&& e2e_image_reference_has_digest "registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
&& ! e2e_image_reference_has_digest "registry.example/gateway:branch"; then
:
else
echo "FAIL: image reference detection" >&2
exit 1
fi
assert_reference_part() {
local description=$1
local expected=$2
local actual=$3
if [ "${actual}" != "${expected}" ]; then
echo "FAIL: ${description}: expected '${expected}', got '${actual}'" >&2
exit 1
fi
}
assert_reference_part "repository strips tag" \
"registry.example/gateway" \
"$(e2e_image_reference_repository "registry.example/gateway:branch")"
assert_reference_part "repository preserves registry port" \
"localhost:5000/openshell/gateway" \
"$(e2e_image_reference_repository "localhost:5000/openshell/gateway:test")"
assert_reference_part "registry extracts hostname" \
"registry.example" \
"$(e2e_image_reference_registry "registry.example/openshell/gateway:branch")"
assert_reference_part "registry extracts hostname and port" \
"localhost:5000" \
"$(e2e_image_reference_registry "localhost:5000/openshell/gateway:test")"
assert_reference_part "repository path excludes registry" \
"openshell/gateway" \
"$(e2e_image_reference_repository_path "registry.example/openshell/gateway:branch")"
assert_reference_part "tag extracts tag" \
"branch" \
"$(e2e_image_reference_tag "registry.example/gateway:branch")"
assert_reference_part "digest extracts digest" \
"sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" \
"$(e2e_image_reference_digest "registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")"
assert_reference_part "digest has no tag" \
"" \
"$(e2e_image_reference_tag "registry.example/gateway@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")"
assert_helm_image_translation() {
local description=$1
local image=$2
local expected_registry=$3
local expected_repository=$4
local expected_tag=$5
local expected_digest=$6
assert_reference_part "${description} registry" "${expected_registry}" \
"$(e2e_image_reference_registry "${image}")"
assert_reference_part "${description} repository" "${expected_repository}" \
"$(e2e_image_reference_repository_path "${image}")"
assert_reference_part "${description} tag" "${expected_tag}" \
"$(e2e_image_reference_tag "${image}")"
assert_reference_part "${description} digest" "${expected_digest}" \
"$(e2e_image_reference_digest "${image}")"
}
# The Kubernetes wrapper passes these three fields directly to Helm. Exercise
# a mixed tagged/digest-pinned set so each independently configurable image is
# translated without embedding a complete reference in image.repository.
assert_helm_image_translation "gateway" \
"registry.example/gateway:branch" "registry.example" "gateway" "branch" ""
assert_helm_image_translation "supervisor" \
"localhost:5000/openshell/supervisor:test" "localhost:5000" "openshell/supervisor" "test" ""
assert_helm_image_translation "sandbox" \
"registry.example/sandbox@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" \
"registry.example" "sandbox" "" "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
echo "E2E image override tests passed."
+7
View File
@@ -13,6 +13,7 @@ depends = [
"test:install-sh",
"test:build-env",
"test:gateway-pull-policy",
"test:e2e-image-overrides",
"test:gateway-config",
"test:e2e-parity",
"test:packaging-assets",
@@ -50,6 +51,12 @@ run = "tasks/scripts/test-gateway-pull-policy.sh"
run_windows = "echo Skipping test:gateway-pull-policy: Unix gateway scripts do not apply on Windows."
hide = true
["test:e2e-image-overrides"]
description = "Test E2E gateway and supervisor image override resolution"
run = "tasks/scripts/test-e2e-image-overrides.sh"
run_windows = "echo Skipping test:e2e-image-overrides: Unix E2E wrappers do not apply on Windows."
hide = true
["test:packaging-assets"]
description = "Run static packaging asset tests"
run = "tasks/scripts/test-packaging-assets.sh"