mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-31 07:44:57 +08:00
* feat(kubernetes): add operator allowlist for sandbox pod annotations Add allowed_pod_annotations to the Kubernetes driver config. Sandbox template annotations must match an exact key or a <domain>/* entry, and the default empty list rejects all of them. Keys in the openshell.ai and agents.x-k8s.io domains are always rejected. Expose the setting through the server.allowedPodAnnotations Helm value. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com> * test(kubernetes): cover pod annotation rejection on sandbox create Assert the exact rejected annotation keys, exercise create_sandbox_inner with a disallowed annotation, and point the rejection error at the Helm gatewayConfig path. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com> * fix(kubernetes): drop Helm path from pod annotation rejection error The rejection error reaches API callers, not operators, so name only the gateway config key. Spell the Helm path in the runtimes docs with the quoted table key to avoid an invalid nested gatewayConfig rendering. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com> * test(kubernetes): condense pod annotation allowlist tests Fold the allowlist matching and validation cases into table-driven tests, reuse the json_struct helper for templates, and document why the openshell.ai and agents.x-k8s.io domains are reserved. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com> * feat(kubernetes): add operator allowlist for sandbox pod labels Sandbox template labels were copied onto workload pods unfiltered. Labels select pods for NetworkPolicy rules and Services, so a caller could place a sandbox under rules meant for other workloads or set OpenShell-owned labels. Add allowed_pod_labels, sharing the allowed_pod_annotations entry format, validation, and reserved openshell.ai / agents.x-k8s.io domains. Empty by default, which rejects every template label. Signed-off-by: Matthew Grossman <mgrossman@nvidia.com> --------- Signed-off-by: Matthew Grossman <mgrossman@nvidia.com>
1463 lines
53 KiB
YAML
1463 lines
53 KiB
YAML
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
suite: gateway TOML config shape
|
|
templates:
|
|
- templates/gateway-config.yaml
|
|
- templates/deployment.yaml
|
|
- templates/peer-role.yaml
|
|
- templates/peer-service.yaml
|
|
- templates/statefulset.yaml
|
|
release:
|
|
name: openshell
|
|
namespace: my-namespace
|
|
|
|
tests:
|
|
- it: disables the WebSocket tunnel unless explicitly enabled for an edge proxy
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^enable_websocket_tunnel\s*=\s*false$'
|
|
|
|
- it: enables the WebSocket tunnel when requested
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.enableWebsocketTunnel: true
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^enable_websocket_tunnel\s*=\s*true$'
|
|
|
|
- it: defaults to label admission with caller driver config disabled
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^allow_driver_config\s*=\s*false$'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\.resource_admission\]\s*enabled\s*=\s*true'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.drivers\.kubernetes\.resource_admission\.required_labels\]'
|
|
|
|
- it: preserves explicit admission opt-out without enabling driver config
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.drivers.kubernetes.resourceAdmission.enabled: false
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^allow_driver_config\s*=\s*false$'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\.resource_admission\]\s*enabled\s*=\s*false'
|
|
|
|
- it: renders replacement labels and literal workspace substitution
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.drivers.kubernetes.allowDriverConfig: true
|
|
server.drivers.kubernetes.resourceAdmission.requiredLabels:
|
|
platform.example.com/sandbox-attachable: "approved"
|
|
platform.example.com/team: '${workspace}'
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '"platform.example.com/sandbox-attachable" = "approved"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '"platform.example.com/team" = "\$\{workspace\}"'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'openshell.ai/sandbox-attachable'
|
|
|
|
- it: identifies the gateway by chart fullname by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^name\s*=\s*"openshell"$'
|
|
|
|
- it: renders an explicit gateway name
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.name: production-us-west
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^name\s*=\s*"production-us-west"$'
|
|
|
|
- it: renders schema version 2 and the Kubernetes compute driver selector
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\]\s*version\s*=\s*2'
|
|
- matchRegex:
|
|
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
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- exists:
|
|
path: spec.template.metadata.annotations["checksum/gateway-config"]
|
|
|
|
- it: mounts gateway.toml as a regular read-only subPath file
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: gateway-config
|
|
mountPath: /etc/openshell/gateway.toml
|
|
subPath: gateway.toml
|
|
readOnly: true
|
|
- contains:
|
|
path: spec.template.spec.volumes
|
|
content:
|
|
name: gateway-config
|
|
configMap:
|
|
name: openshell-config
|
|
|
|
- it: renders a StatefulSet by default
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- equal:
|
|
path: kind
|
|
value: StatefulSet
|
|
|
|
- it: treats a null workload map as the default StatefulSet
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
workload: null
|
|
asserts:
|
|
- equal:
|
|
path: kind
|
|
value: StatefulSet
|
|
|
|
- it: uses a stable gateway container name
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- equal:
|
|
path: spec.template.spec.containers[0].name
|
|
value: openshell-gateway
|
|
|
|
- it: enables anonymous telemetry by default
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_TELEMETRY_ENABLED
|
|
value: "true"
|
|
|
|
- it: disables anonymous telemetry when configured
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.telemetryEnabled: false
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_TELEMETRY_ENABLED
|
|
value: "false"
|
|
|
|
- it: renders OTLP tracing configuration when configured
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.otlp.endpoint: http://otel-collector.observability.svc:4317
|
|
server.otlp.serviceName: production-gateway
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\.otlp\].*?endpoint\s*=\s*"http://otel-collector\.observability\.svc:4317".*?service_name\s*=\s*"production-gateway"'
|
|
|
|
- it: omits OTLP tracing configuration by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.otlp\]'
|
|
|
|
- it: renders OIDC transport security settings
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.oidc.issuer: https://issuer.example.com
|
|
server.oidc.dangerouslyAllowInsecureHttp: false
|
|
server.oidc.jwksAllowedOrigins[0]: https://keys.example.com
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^dangerously_allow_insecure_http\s*=\s*false$'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^jwks_allowed_origins\s*=\s*\["https://keys.example.com"\]$'
|
|
|
|
- it: treats a null OTLP map as disabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.otlp: null
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.otlp\]'
|
|
|
|
- it: omits OCSF JSONL output by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.ocsf_log\]'
|
|
|
|
- it: treats a null OCSF log map as disabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.ocsfLog: null
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.ocsf_log\]'
|
|
|
|
- it: omits OCSF JSONL output when a path is set without enabling it
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.ocsfLog.path: /var/openshell/gateway-ocsf.jsonl
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.ocsf_log\]'
|
|
|
|
- it: writes OCSF JSONL to the default container path when enabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\.ocsf_log\].*?path\s*=\s*"/tmp/gateway-ocsf\.jsonl"'
|
|
|
|
- it: rejects enabled OCSF JSONL output without a path
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.path: ""
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.ocsfLog.path must be set when server.ocsfLog.enabled is true"
|
|
|
|
- it: renders OCSF JSONL output when configured
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.path: /var/openshell/gateway-ocsf.jsonl
|
|
server.ocsfLog.schemaVersion: "1.3"
|
|
server.ocsfLog.rotation: daily
|
|
server.ocsfLog.maxFiles: 14
|
|
server.ocsfLog.queueCapacity: 20000
|
|
server.ocsfLog.queueMaxBytes: 33554432
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\.ocsf_log\].*?max_files\s*=\s*14.*?path\s*=\s*"/var/openshell/gateway-ocsf\.jsonl".*?queue_capacity\s*=\s*20000.*?queue_max_bytes\s*=\s*33554432.*?rotation\s*=\s*"daily".*?schema_version\s*=\s*"1\.3"'
|
|
|
|
- it: omits OCSF retention when rotation is disabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.path: /var/openshell/gateway-ocsf.jsonl
|
|
server.ocsfLog.rotation: never
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\.ocsf_log\].*?rotation\s*=\s*"never"'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'max_files\s*='
|
|
|
|
- it: rejects an unsupported OCSF rotation mode
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.path: /var/openshell/gateway-ocsf.jsonl
|
|
server.ocsfLog.rotation: hourly
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.ocsfLog.rotation must be daily or never"
|
|
|
|
- it: rejects a zero OCSF queueCapacity
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.queueCapacity: 0
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.ocsfLog.queueCapacity and queueMaxBytes must be positive"
|
|
|
|
- it: rejects a zero OCSF queueMaxBytes
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.queueMaxBytes: 0
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.ocsfLog.queueCapacity and queueMaxBytes must be positive"
|
|
|
|
- it: rejects a zero OCSF maxFiles
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.maxFiles: 0
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.ocsfLog.maxFiles must be positive when rotation is daily"
|
|
|
|
- it: uses default OCSF limits when they are null
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.queueCapacity: null
|
|
server.ocsfLog.queueMaxBytes: null
|
|
server.ocsfLog.maxFiles: null
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\.ocsf_log\].*?max_files\s*=\s*7.*?queue_capacity\s*=\s*10000.*?queue_max_bytes\s*=\s*16777216'
|
|
|
|
- it: rejects an unsupported OCSF schema version
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.path: /var/openshell/gateway-ocsf.jsonl
|
|
server.ocsfLog.schemaVersion: "1.8"
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.ocsfLog.schemaVersion must be empty, 1.1, or 1.3"
|
|
|
|
- it: mounts extra gateway volumes for OCSF output in a Deployment
|
|
template: templates/deployment.yaml
|
|
set:
|
|
workload.kind: deployment
|
|
server.externalDbSecret: my-pg-secret
|
|
server.ocsfLog.enabled: true
|
|
server.ocsfLog.path: /var/log/openshell/gateway-ocsf.jsonl
|
|
server.extraVolumes:
|
|
- name: ocsf-log
|
|
persistentVolumeClaim:
|
|
claimName: openshell-ocsf-log
|
|
server.extraVolumeMounts:
|
|
- name: ocsf-log
|
|
mountPath: /var/log/openshell
|
|
subPathExpr: $(OPENSHELL_POD_NAME)
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.volumes
|
|
content:
|
|
name: ocsf-log
|
|
persistentVolumeClaim:
|
|
claimName: openshell-ocsf-log
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: ocsf-log
|
|
mountPath: /var/log/openshell
|
|
subPathExpr: $(OPENSHELL_POD_NAME)
|
|
|
|
- it: mounts the OIDC CA bundle when TLS is disabled
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.disableTls: true
|
|
server.oidc.issuer: https://issuer.example.com
|
|
server.oidc.caConfigMapName: openshell-oidc-ca
|
|
asserts:
|
|
- equal:
|
|
path: spec.template.spec.containers[0].volumeMounts[4].name
|
|
value: oidc-ca
|
|
- equal:
|
|
path: spec.template.spec.containers[0].volumeMounts[4].mountPath
|
|
value: /etc/openshell-tls/oidc-ca
|
|
- equal:
|
|
path: spec.template.spec.volumes[3].name
|
|
value: oidc-ca
|
|
- equal:
|
|
path: spec.template.spec.volumes[3].configMap.name
|
|
value: openshell-oidc-ca
|
|
|
|
# Regression for the P1 bug Drew flagged: grpc_endpoint MUST live in the
|
|
# Kubernetes driver table, not in [openshell.gateway]. The gateway-side
|
|
# schema has `deny_unknown_fields` and no `grpc_endpoint` field, so writing
|
|
# it at gateway scope makes `config_file::load` reject the default install.
|
|
- it: renders grpc_endpoint under [openshell.drivers.kubernetes], not [openshell.gateway]
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?grpc_endpoint'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\][^\[]*?grpc_endpoint'
|
|
|
|
- it: applies the global pull policy by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^supervisor_image_pull_policy\s*=\s*"if_not_present"$'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?m)^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:
|
|
sandbox.image.pullPolicy: if_not_present
|
|
supervisor.image.pullPolicy: never
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?image_pull_policy\s*=\s*"if_not_present".*?supervisor_image_pull_policy\s*=\s*"never"'
|
|
|
|
- it: translates legacy Kubernetes pull policy values to canonical gateway values
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: Always
|
|
supervisor.image.pullPolicy: IfNotPresent
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?image_pull_policy\s*=\s*"always".*?supervisor_image_pull_policy\s*=\s*"if_not_present"'
|
|
|
|
- it: rejects unsupported sandbox image pull policies
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: Sometimes
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: 'image pull policy "Sometimes" must be one of: always, if_not_present, never, Always, IfNotPresent, Never'
|
|
|
|
- it: rejects unsupported supervisor image pull policies
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
supervisor.image.pullPolicy: newer
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: 'image pull policy "newer" must be one of: always, if_not_present, never, Always, IfNotPresent, Never'
|
|
|
|
- it: renders driver-owned Kubernetes settings only in its driver table
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.hostGatewayIP: 10.0.0.1
|
|
server.enableUserNamespaces: true
|
|
supervisor.image.tag: test
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?client_tls_secret_name\s*=.*?default_image\s*=.*?enable_user_namespaces\s*=\s*true.*?host_gateway_ip\s*=\s*"10\.0\.0\.1".*?namespace\s*=\s*"my-namespace".*?sa_token_ttl_secs\s*=.*?service_account_name\s*=\s*"openshell-sandbox".*?supervisor_image\s*='
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\][^\[]*?(sandbox_namespace|default_image|supervisor_image|client_tls_secret_name|service_account_name|host_gateway_ip|enable_user_namespaces|sa_token_ttl_secs)\s*='
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'guest_tls_(ca|cert|key)\s*='
|
|
|
|
- it: renders user namespace enablement under [openshell.drivers.kubernetes]
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.enableUserNamespaces: true
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?enable_user_namespaces\s*=\s*true'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\][^\[]*?enable_user_namespaces'
|
|
|
|
- it: renders operator-owned upstream proxy settings under the Kubernetes driver
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
upstreamProxy.url: http://proxy.corp.example:8080
|
|
upstreamProxy.noProxy: .svc.cluster.local,10.96.0.0/12
|
|
upstreamProxy.authSecret.name: corporate-proxy-auth
|
|
upstreamProxy.authSecret.key: credentials
|
|
upstreamProxy.authAllowInsecure: true
|
|
upstreamProxy.connectByHostname: true
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?https_proxy\s*=\s*"http://proxy\.corp\.example:8080"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'proxy_auth_secret_name\s*=\s*"corporate-proxy-auth"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'proxy_auth_secret_key\s*=\s*"credentials"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'proxy_auth_allow_insecure\s*=\s*true'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'no_proxy\s*=\s*"\.svc\.cluster\.local,10\.96\.0\.0/12"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'proxy_connect_by_hostname\s*=\s*true'
|
|
|
|
- it: renders proxy_ca_bundle when a corporate CA ConfigMap is configured
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
upstreamProxy.url: https://proxy.corp.example:3130
|
|
upstreamProxy.caBundle.configMapName: corporate-proxy-ca
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?proxy_ca_bundle\s*=\s*"/etc/openshell-tls/proxy-ca/ca\.crt"'
|
|
|
|
- it: omits proxy_ca_bundle when no corporate CA ConfigMap is configured
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
upstreamProxy.url: http://proxy.corp.example:8080
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'proxy_ca_bundle\s*='
|
|
|
|
- it: renders the default supervisor image
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
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
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'sandbox_runtime_image\s*='
|
|
|
|
- it: renders independent sandbox runtime and supervisor image overrides
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandboxRuntime.image.registry: registry.example.com
|
|
sandboxRuntime.image.repository: openshell/sandbox
|
|
sandboxRuntime.image.tag: sandbox-build
|
|
sandboxRuntime.image.pullPolicy: Always
|
|
supervisor.image.registry: registry.example.com
|
|
supervisor.image.repository: openshell/supervisor
|
|
supervisor.image.tag: supervisor-build
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'sandbox_runtime_image\s*=\s*"registry\.example\.com/openshell/sandbox:sandbox-build"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'sandbox_runtime_image_pull_policy\s*=\s*"always"'
|
|
- matchRegex:
|
|
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:
|
|
supervisor.image.tag: 1.2.3
|
|
asserts:
|
|
- matchRegex:
|
|
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 default chart tag
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
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:0\.0\.0"'
|
|
|
|
- it: renders complete supervisor repository and tag overrides
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
supervisor.image.registry: registry.example.com
|
|
supervisor.image.repository: openshell/supervisor
|
|
supervisor.image.tag: supervisor-build
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'supervisor_image\s*=\s*"registry\.example\.com/openshell/supervisor:supervisor-build"'
|
|
|
|
- it: configures managed SSH isolation with the gateway peer
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\.managed_ssh_ingress\].*?enabled\s*=\s*true.*?gateway_namespace\s*=\s*"my-namespace".*?gateway_pod_selector\s*=.*?app\.kubernetes\.io/name.*?openshell'
|
|
|
|
- it: renders sandbox image pull secrets under [openshell.drivers.kubernetes]
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.sandboxImagePullSecrets:
|
|
- name: regcred
|
|
- name: backup-regcred
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?image_pull_secrets\s*=\s*\["regcred", "backup-regcred"\]'
|
|
|
|
- it: does not reuse gateway image pull secrets for sandbox pods
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
imagePullSecrets:
|
|
- name: gateway-regcred
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'image_pull_secrets\s*='
|
|
|
|
- it: renders allowed pod annotations and labels for the Kubernetes driver
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
gatewayConfig:
|
|
openshell.drivers.kubernetes:
|
|
allowed_pod_annotations:
|
|
- example.com/*
|
|
- team.example.org/owner
|
|
allowed_pod_labels:
|
|
- app
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?allowed_pod_annotations\s*=\s*\[\s*"example\.com/\*",\s*"team\.example\.org/owner",?\s*\]'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?allowed_pod_labels\s*=\s*\[\s*"app",?\s*\]'
|
|
|
|
- it: omits allowed pod annotations and labels by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'allowed_pod_annotations\s*='
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'allowed_pod_labels\s*='
|
|
|
|
- it: does not render local mTLS user auth for Kubernetes deployments
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.mtls_auth\]'
|
|
|
|
- it: does not allow unauthenticated users by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.auth\]'
|
|
|
|
- it: renders explicit unauthenticated user dev mode when enabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.auth.allowUnauthenticatedUsers: true
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\.auth\].*?allow_unauthenticated_users\s*=\s*true'
|
|
|
|
- it: omits the gRPC rate limit by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'grpc_rate_limit_requests\s*='
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'grpc_rate_limit_window_seconds\s*='
|
|
|
|
- it: renders fail-closed policy validation posture by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\].*?policy_validation_failure_mode\s*=\s*"fail_closed"'
|
|
|
|
- it: renders retain-last-valid policy validation posture
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.policyValidationFailureMode: retain_last_valid
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\].*?policy_validation_failure_mode\s*=\s*"retain_last_valid"'
|
|
|
|
- it: renders the gRPC rate limit under [openshell.gateway] when both values are positive
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.grpcRateLimit.requests: 120
|
|
server.grpcRateLimit.windowSeconds: 60
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\].*?grpc_rate_limit_requests\s*=\s*120'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.gateway\].*?grpc_rate_limit_window_seconds\s*=\s*60'
|
|
|
|
# The validation lives in gateway-config.yaml but surfaces through the
|
|
# statefulset checksum include, so the failure is asserted against that
|
|
# template (mirrors the postgres serviceBindings failure test below).
|
|
- it: fails to render when only requests is positive
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.grpcRateLimit.requests: 120
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.grpcRateLimit requires both requests and windowSeconds to be positive to enable rate limiting, or both 0/unset to disable it"
|
|
|
|
- it: fails to render when only windowSeconds is positive
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.grpcRateLimit.windowSeconds: 60
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.grpcRateLimit requires both requests and windowSeconds to be positive to enable rate limiting, or both 0/unset to disable it"
|
|
|
|
- it: fails to render when requests is negative
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.grpcRateLimit.requests: -1
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.grpcRateLimit.requests and server.grpcRateLimit.windowSeconds must not be negative; they map to unsigned gateway settings"
|
|
|
|
- it: fails to render when windowSeconds is negative
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.grpcRateLimit.windowSeconds: -5
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "server.grpcRateLimit.requests and server.grpcRateLimit.windowSeconds must not be negative; they map to unsigned gateway settings"
|
|
|
|
- it: uses the configured existing sandbox service account name
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandboxServiceAccount.create: false
|
|
sandboxServiceAccount.name: precreated-sandbox
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '(?ms)\[openshell\.drivers\.kubernetes\].*?service_account_name\s*=\s*"precreated-sandbox"'
|
|
|
|
- it: omits server_sans when no DNS SANs are configured
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'server_sans\s*='
|
|
|
|
- it: emits disable_tls=true and omits the [openshell.gateway.tls] section when disableTls is set
|
|
set:
|
|
server.disableTls: true
|
|
certManager.enabled: false
|
|
pkiInitJob.enabled: false
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'disable_tls\s*=\s*true'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.tls\]'
|
|
|
|
- it: omits client_ca_path from the TLS section when mTLS is disabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.tls.enableMtls: false
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.tls\]'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'cert_path\s*='
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'key_path\s*='
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'client_ca_path\s*='
|
|
|
|
- it: renders client_ca_path for built-in PKI by default
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'client_ca_path\s*=\s*"/etc/openshell-tls/client-ca/ca\.crt"'
|
|
|
|
- it: omits client_ca_path when clientCaSecretName is empty for HTTPS-only mode
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.tls.clientCaSecretName: ""
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.tls\]'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'cert_path\s*=\s*"/etc/openshell-tls/server/tls\.crt"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'key_path\s*=\s*"/etc/openshell-tls/server/tls\.key"'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'client_ca_path\s*='
|
|
|
|
- it: keeps external server certificate fields when clientCaSecretName is empty
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
certManager.enabled: true
|
|
certManager.serverIssuerRef.name: letsencrypt-prod
|
|
certManager.serverDnsNames:
|
|
- gateway.example.com
|
|
server.tls.clientCaSecretName: ""
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'client_ca_path\s*='
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'external_cert_path\s*=\s*"/etc/openshell-tls/server-external/tls\.crt"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'external_key_path\s*=\s*"/etc/openshell-tls/server-external/tls\.key"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'external_server_names\s*=\s*\["gateway\.example\.com"\]'
|
|
|
|
- it: omits client_ca_path for cert-manager shared CA when clientCaSecretName is empty
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
certManager.enabled: true
|
|
certManager.clientCaFromServerTlsSecret: true
|
|
server.tls.clientCaSecretName: ""
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.tls\]'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'cert_path\s*=\s*"/etc/openshell-tls/server/tls\.crt"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'key_path\s*=\s*"/etc/openshell-tls/server/tls\.key"'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'client_ca_path\s*='
|
|
|
|
- it: omits client_ca_path when cert-manager owns TLS and no client CA secret is set
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
certManager.enabled: true
|
|
certManager.clientCaFromServerTlsSecret: false
|
|
pkiInitJob.enabled: true
|
|
server.tls.clientCaSecretName: ""
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.tls\]'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'cert_path\s*=\s*"/etc/openshell-tls/server/tls\.crt"'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'key_path\s*=\s*"/etc/openshell-tls/server/tls\.key"'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'client_ca_path\s*='
|
|
|
|
- it: renders server_sans from certManager.serverDnsNames
|
|
set:
|
|
certManager.enabled: true
|
|
certManager.serverDnsNames:
|
|
- openshell
|
|
- "*.dev.openshell.localhost"
|
|
pkiInitJob.enabled: false
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'server_sans\s*=\s*\["openshell", "\*\.dev\.openshell\.localhost"\]'
|
|
|
|
- it: preserves pkiInitJob serverDnsNames as the runtime SAN fallback
|
|
set:
|
|
certManager.enabled: false
|
|
pkiInitJob.serverDnsNames:
|
|
- "*.apps.example.com"
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'server_sans\s*=\s*\["\*\.apps\.example\.com"\]'
|
|
|
|
- it: passes sqlite db-url via --db-url arg by default
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].args
|
|
content: "sqlite:/var/openshell/openshell.db"
|
|
|
|
- it: fails when clientCaSecretName is null
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.tls.clientCaSecretName: null
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "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"
|
|
|
|
- it: configures gateway peer identity and projected peer token
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- equal:
|
|
path: spec.serviceName
|
|
value: openshell-peer
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_SERVICE_NAME
|
|
value: openshell-peer
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_TOKEN_AUDIENCE
|
|
value: openshell-gateway-peer
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_TLS_SERVER_NAME
|
|
value: openshell.my-namespace.svc.cluster.local
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_TLS_CA_FILE
|
|
value: /etc/openshell-tls/server/ca.crt
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_TLS_CERT_FILE
|
|
value: /etc/openshell-tls/peer-client/tls.crt
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_TLS_KEY_FILE
|
|
value: /etc/openshell-tls/peer-client/tls.key
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: gateway-peer-token
|
|
mountPath: /var/run/secrets/openshell-peer
|
|
readOnly: true
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: peer-client-tls
|
|
mountPath: /etc/openshell-tls/peer-client
|
|
readOnly: true
|
|
- equal:
|
|
path: spec.template.spec.volumes[2].projected.sources[0].serviceAccountToken.audience
|
|
value: openshell-gateway-peer
|
|
|
|
- it: configures gateway peer identity and projected peer token for Deployment
|
|
template: templates/deployment.yaml
|
|
set:
|
|
workload.kind: deployment
|
|
server.externalDbSecret: my-pg-secret
|
|
server.disableTls: true
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_POD_IP
|
|
valueFrom:
|
|
fieldRef:
|
|
fieldPath: status.podIP
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_ENDPOINT
|
|
value: http://$(OPENSHELL_POD_IP):8080
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_SERVICE_NAME
|
|
value: openshell-peer
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_PEER_TOKEN_AUDIENCE
|
|
value: openshell-gateway-peer
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: gateway-peer-token
|
|
mountPath: /var/run/secrets/openshell-peer
|
|
readOnly: true
|
|
- equal:
|
|
path: spec.template.spec.volumes[2].projected.sources[0].serviceAccountToken.audience
|
|
value: openshell-gateway-peer
|
|
|
|
- it: renders headless gateway peer service
|
|
template: templates/peer-service.yaml
|
|
asserts:
|
|
- equal:
|
|
path: metadata.name
|
|
value: openshell-peer
|
|
- equal:
|
|
path: spec.clusterIP
|
|
value: None
|
|
- equal:
|
|
path: spec.publishNotReadyAddresses
|
|
value: true
|
|
|
|
- it: grants release-namespace pod lookup for gateway peer identity validation
|
|
template: templates/peer-role.yaml
|
|
asserts:
|
|
- hasDocuments:
|
|
count: 2
|
|
- equal:
|
|
path: metadata.namespace
|
|
value: my-namespace
|
|
documentIndex: 0
|
|
- equal:
|
|
path: rules[0].resources[0]
|
|
value: pods
|
|
documentIndex: 0
|
|
- equal:
|
|
path: subjects[0].name
|
|
value: openshell
|
|
documentIndex: 1
|
|
|
|
- it: fails when legacy postgres.enabled is set
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
postgres.enabled: true
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "postgres.enabled was removed"
|
|
|
|
- it: fails when multiple replicas use the default SQLite database
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
replicaCount: 2
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "replicaCount > 1 requires server.externalDbSecret"
|
|
|
|
- it: renders a Deployment for external database-backed gateway workloads
|
|
template: templates/deployment.yaml
|
|
set:
|
|
workload.kind: deployment
|
|
replicaCount: 2
|
|
server.externalDbSecret: my-pg-secret
|
|
server.credentialDrivers.kubernetesSecrets.enabled: true
|
|
asserts:
|
|
- equal:
|
|
path: kind
|
|
value: Deployment
|
|
- equal:
|
|
path: spec.replicas
|
|
value: 2
|
|
- equal:
|
|
path: spec.template.spec.containers[0].name
|
|
value: openshell-gateway
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: gateway-config
|
|
mountPath: /etc/openshell/gateway.toml
|
|
subPath: gateway.toml
|
|
readOnly: true
|
|
- notContains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: openshell-data
|
|
mountPath: /var/openshell
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_DB_URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: my-pg-secret
|
|
key: uri
|
|
|
|
- it: fails when a Deployment uses the default SQLite database
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
workload.kind: deployment
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "workload.kind=deployment requires server.externalDbSecret"
|
|
|
|
- it: fails when multiple replicas use a StatefulSet without an override
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
replicaCount: 2
|
|
server.externalDbSecret: my-pg-secret
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "replicaCount > 1 with workload.kind=statefulset requires workload.allowMultiReplicaStatefulSet=true"
|
|
|
|
- it: allows a multi-replica StatefulSet with an external database and explicit override
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
replicaCount: 2
|
|
server.externalDbSecret: my-pg-secret
|
|
workload.allowMultiReplicaStatefulSet: true
|
|
server.credentialDrivers.kubernetesSecrets.enabled: true
|
|
asserts:
|
|
- equal:
|
|
path: kind
|
|
value: StatefulSet
|
|
- equal:
|
|
path: spec.replicas
|
|
value: 2
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: openshell-data
|
|
mountPath: /var/openshell
|
|
|
|
- it: fails when workload.kind is invalid
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
workload.kind: daemonset
|
|
asserts:
|
|
- failedTemplate:
|
|
errorPattern: "workload.kind must be one of: statefulset, deployment"
|
|
|
|
- it: does not pass --db-url in args when externalDbSecret is set
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.externalDbSecret: my-pg-secret
|
|
asserts:
|
|
- notContains:
|
|
path: spec.template.spec.containers[0].args
|
|
content: "--db-url"
|
|
|
|
- it: references externalDbSecret when set
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
server.externalDbSecret: my-pg-secret
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_DB_URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: my-pg-secret
|
|
key: uri
|
|
|
|
- it: renders HA external database configuration from the CI overlay as a Deployment
|
|
template: templates/deployment.yaml
|
|
values:
|
|
- ../ci/values-high-availability.yaml
|
|
asserts:
|
|
- equal:
|
|
path: kind
|
|
value: Deployment
|
|
- equal:
|
|
path: spec.replicas
|
|
value: 2
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_DB_URL
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: openshell-ha-pg
|
|
key: uri
|
|
- it: renders provider SPIFFE token grants while keeping gateway JWT auth
|
|
set:
|
|
server.providerTokenGrants.spiffe.enabled: true
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.gateway_jwt\]'
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: 'provider_spiffe_workload_api_socket_path\s*=\s*"/spiffe-workload-api/spire-agent\.sock"'
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: '\[openshell\.gateway\.spiffe\]'
|
|
|
|
- it: mounts the gateway SPIFFE socket while keeping sandbox JWT auth
|
|
set:
|
|
server.providerTokenGrants.spiffe.enabled: true
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- contains:
|
|
path: spec.template.spec.containers[0].env
|
|
content:
|
|
name: OPENSHELL_GATEWAY_SPIFFE_WORKLOAD_API_SOCKET
|
|
value: /spiffe-workload-api/spire-agent.sock
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: sandbox-jwt
|
|
mountPath: /etc/openshell-jwt
|
|
readOnly: true
|
|
- contains:
|
|
path: spec.template.spec.containers[0].volumeMounts
|
|
content:
|
|
name: spiffe-workload-api
|
|
mountPath: /spiffe-workload-api
|
|
readOnly: true
|
|
- contains:
|
|
path: spec.template.spec.volumes
|
|
content:
|
|
name: sandbox-jwt
|
|
secret:
|
|
defaultMode: 256
|
|
secretName: openshell-jwt-keys
|
|
- contains:
|
|
path: spec.template.spec.volumes
|
|
content:
|
|
name: spiffe-workload-api
|
|
csi:
|
|
driver: csi.spiffe.io
|
|
readOnly: true
|
|
|
|
- it: fails when serverIssuerRef is set but certManager is disabled
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
certManager.serverIssuerRef.name: letsencrypt-prod
|
|
certManager.enabled: false
|
|
asserts:
|
|
- failedTemplate:
|
|
errorMessage: "certManager.serverIssuerRef.name is set but certManager.enabled is false \u2014 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."
|
|
|
|
- it: does not render the schema-v1 compute_drivers selector
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?m)^\\s*compute_drivers\\s*="
|
|
|
|
- it: uses the release fullname and namespace for default Kubernetes settings
|
|
template: templates/gateway-config.yaml
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?grpc_endpoint\\s*=\\s*\\\"https://openshell\\.my-namespace\\.svc\\.cluster\\.local:8080\\\".*?namespace\\s*=\\s*\\\"my-namespace\\\""
|
|
|
|
- it: uses an explicit server grpc endpoint verbatim
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.grpcEndpoint: https://gateway.example.test:9443
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?grpc_endpoint\\s*=\\s*\\\"https://gateway\\.example\\.test:9443\\\""
|
|
|
|
- it: uses HTTP callback and omits client TLS secret when TLS is disabled
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
server.disableTls: true
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?grpc_endpoint\\s*=\\s*\\\"http://openshell\\.my-namespace\\.svc\\.cluster\\.local:8080\\\""
|
|
- notMatchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "client_tls_secret_name\\s*="
|
|
|
|
- it: accepts Always pull policy spelling for sandbox and supervisor
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: Always
|
|
supervisor.image.pullPolicy: Always
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?image_pull_policy\\s*=\\s*\\\"always\\\".*?supervisor_image_pull_policy\\s*=\\s*\\\"always\\\""
|
|
|
|
- it: accepts IfNotPresent pull policy spelling for sandbox and supervisor
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: IfNotPresent
|
|
supervisor.image.pullPolicy: IfNotPresent
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?image_pull_policy\\s*=\\s*\\\"if_not_present\\\".*?supervisor_image_pull_policy\\s*=\\s*\\\"if_not_present\\\""
|
|
|
|
- it: accepts Never pull policy spelling for sandbox and supervisor
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: Never
|
|
supervisor.image.pullPolicy: Never
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?image_pull_policy\\s*=\\s*\\\"never\\\".*?supervisor_image_pull_policy\\s*=\\s*\\\"never\\\""
|
|
|
|
- it: accepts canonical lowercase pull policies for sandbox and supervisor
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: always
|
|
supervisor.image.pullPolicy: always
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?image_pull_policy\\s*=\\s*\\\"always\\\".*?supervisor_image_pull_policy\\s*=\\s*\\\"always\\\""
|
|
|
|
- it: accepts canonical if_not_present pull policies for sandbox and supervisor
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: if_not_present
|
|
supervisor.image.pullPolicy: if_not_present
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?image_pull_policy\\s*=\\s*\\\"if_not_present\\\".*?supervisor_image_pull_policy\\s*=\\s*\\\"if_not_present\\\""
|
|
|
|
- it: accepts canonical never pull policies for sandbox and supervisor
|
|
template: templates/gateway-config.yaml
|
|
set:
|
|
sandbox.image.pullPolicy: never
|
|
supervisor.image.pullPolicy: never
|
|
asserts:
|
|
- matchRegex:
|
|
path: data["gateway.toml"]
|
|
pattern: "(?ms)\\[openshell\\.drivers\\.kubernetes\\].*?image_pull_policy\\s*=\\s*\\\"never\\\".*?supervisor_image_pull_policy\\s*=\\s*\\\"never\\\""
|
|
|
|
- it: gives the gateway time to finish shutdown by default
|
|
template: templates/statefulset.yaml
|
|
asserts:
|
|
- equal:
|
|
path: spec.template.spec.terminationGracePeriodSeconds
|
|
value: 30
|
|
|
|
- it: keeps an explicit termination grace period
|
|
template: templates/statefulset.yaml
|
|
set:
|
|
podLifecycle.terminationGracePeriodSeconds: 3
|
|
asserts:
|
|
- equal:
|
|
path: spec.template.spec.terminationGracePeriodSeconds
|
|
value: 3
|