diff --git a/ai-tooling/user/skills/nvcf-self-managed-installation/references/helmfile-structure.md b/ai-tooling/user/skills/nvcf-self-managed-installation/references/helmfile-structure.md index 1d97633d3..788d12a0f 100644 --- a/ai-tooling/user/skills/nvcf-self-managed-installation/references/helmfile-structure.md +++ b/ai-tooling/user/skills/nvcf-self-managed-installation/references/helmfile-structure.md @@ -208,10 +208,17 @@ repository, set the generated worker sidecar image explicitly: ```yaml api: - env: - NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: //pylon:0.2.1 + remoteConfig: + configData: + nvcf: + sidecars: + llm-router-client-image: //pylon:0.14.1 ``` +The legacy `api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE` path is deprecated. +The stack translates it for one compatibility window, but new configurations +must use the remote-config path. Conflicting values fail rendering. + Render and apply the updated control-plane environment, then refresh every registered compute plane using the same handoff used for its installation so NVCA receives `agentConfig.mergeConfig`: @@ -226,9 +233,11 @@ Use the complete compute-plane install command from CLI-profile installation must remain profile-driven; a Helmfile installation must remain values-driven. -Existing LLM function pods keep their existing sidecar arguments. Recreate or -redeploy those functions after the compute-plane refresh. Verify the control -plane, route, and worker sidecar: +Existing LLM function versions retain the worker-sidecar image metadata +captured when the version is created. Replacing pods or redeploying the same +version does not apply a new Pylon image. After the control-plane update, +create and deploy a new function version. Verify the control plane, route, and +worker sidecar: ```bash kubectl get deploy -n nvcf llm-api-gateway diff --git a/deploy/helm/cloud-functions/nvcf-api/values.yaml b/deploy/helm/cloud-functions/nvcf-api/values.yaml index e92c1a223..1b3710b77 100644 --- a/deploy/helm/cloud-functions/nvcf-api/values.yaml +++ b/deploy/helm/cloud-functions/nvcf-api/values.yaml @@ -245,7 +245,7 @@ api: ess-agent-container: ${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/ess-agent:1.4.0 otel-collector-container: dummy llm-credential-manager-image: ${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/nvcf-worker-llm-credentials-oss:1.1.1 - llm-router-client-image: ${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/pylon:0.10.0 + llm-router-client-image: ${nvcf.sidecars.hostname}/${nvcf.sidecars.repository}/pylon:0.14.1 notary: turn: enabled: true diff --git a/deploy/helm/cloud-functions/tests/sidecar_release_artifacts_test.sh b/deploy/helm/cloud-functions/tests/sidecar_release_artifacts_test.sh index 1d3283da3..363fc508b 100755 --- a/deploy/helm/cloud-functions/tests/sidecar_release_artifacts_test.sh +++ b/deploy/helm/cloud-functions/tests/sidecar_release_artifacts_test.sh @@ -53,7 +53,7 @@ expected_annotations=( "release-artifact-niclls-container-image: \"${hostname}/${repository}/nvcf_worker_niclls:2.109.4\"" "release-artifact-ess-agent-container-image: \"${hostname}/${repository}/ess-agent:1.4.0\"" "release-artifact-llm-credential-manager-image: \"${hostname}/${repository}/nvcf-worker-llm-credentials-oss:1.1.1\"" - "release-artifact-llm-router-client-image: \"${hostname}/${repository}/pylon:0.10.0\"" + "release-artifact-llm-router-client-image: \"${hostname}/${repository}/pylon:0.14.1\"" ) for annotation in "${expected_annotations[@]}"; do diff --git a/deploy/helm/llm-request-router/llm-request-router/Chart.yaml b/deploy/helm/llm-request-router/llm-request-router/Chart.yaml index 94153dacd..fcaaec07b 100644 --- a/deploy/helm/llm-request-router/llm-request-router/Chart.yaml +++ b/deploy/helm/llm-request-router/llm-request-router/Chart.yaml @@ -18,4 +18,4 @@ name: helm-nvcf-llm-request-router description: Helm chart for the NVCF LLM Request Router backed by Stargate type: application version: 0.0.0 # managed by semantic-release -appVersion: "0.11.1" # match the versioned stargate image tag +appVersion: "0.14.1" # match the versioned stargate image tag diff --git a/deploy/helm/llm-request-router/llm-request-router/values.yaml b/deploy/helm/llm-request-router/llm-request-router/values.yaml index a27aed76a..8db161899 100644 --- a/deploy/helm/llm-request-router/llm-request-router/values.yaml +++ b/deploy/helm/llm-request-router/llm-request-router/values.yaml @@ -43,7 +43,7 @@ llmRequestRouter: image: registry: "" repository: "" - tag: "0.11.1" + tag: "0.14.1" pullPolicy: IfNotPresent imagePullSecrets: [] diff --git a/deploy/helm/llm-request-router/scripts/check-backend-router-render.sh b/deploy/helm/llm-request-router/scripts/check-backend-router-render.sh index 19833c708..e890ca742 100755 --- a/deploy/helm/llm-request-router/scripts/check-backend-router-render.sh +++ b/deploy/helm/llm-request-router/scripts/check-backend-router-render.sh @@ -303,9 +303,9 @@ if ! grep -Fq -- "serviceAccountName: external-backend-router" "$external_servic fi assert_backend_router_role_binding_subject "$external_service_account" "external-backend-router" -assert_zero_config_contains "image: registry.example.invalid/nvcf/stargate:0.11.1" \ +assert_zero_config_contains "image: registry.example.invalid/nvcf/stargate:0.14.1" \ "backend router must inherit the released Stargate image with no router image configuration" -assert_zero_config_contains 'app.kubernetes.io/version: "0.11.1"' \ +assert_zero_config_contains 'app.kubernetes.io/version: "0.14.1"' \ "backend router labels must identify the inherited Stargate image version" helm template llm-request-router "$chart_dir" \ diff --git a/deploy/stacks/self-managed/Makefile b/deploy/stacks/self-managed/Makefile index d52f91630..9deb8223c 100644 --- a/deploy/stacks/self-managed/Makefile +++ b/deploy/stacks/self-managed/Makefile @@ -1,7 +1,7 @@ # Public developer entry point for the self-managed stack. include Makefile.dist -.PHONY: test +.PHONY: test test-published-charts test: @tests/llm-router-worker-address.sh @tests/llm-router-split-cluster.sh @@ -18,3 +18,11 @@ test: @tests/invocation-tracing-baggage.sh @tests/image-override-wiring.sh @tests/sidecar-registry-wiring.sh + @tests/api-env-wiring.sh + +test-published-charts: + @: "$${NVCF_PUBLISHED_CHART_REGISTRY:?NVCF_PUBLISHED_CHART_REGISTRY is required}" + @: "$${NVCF_PUBLISHED_CHART_REPOSITORY:?NVCF_PUBLISHED_CHART_REPOSITORY is required}" + @tests/llm-router-published-chart.sh + @tests/gateway-routes-published-chart.sh + @tests/openbao-published-chart.sh diff --git a/deploy/stacks/self-managed/environments/base.yaml b/deploy/stacks/self-managed/environments/base.yaml index 45ee1d9cd..790169e95 100644 --- a/deploy/stacks/self-managed/environments/base.yaml +++ b/deploy/stacks/self-managed/environments/base.yaml @@ -171,12 +171,20 @@ functionAutoscaler: # environment label. ignoreEnv: true +# API environment entries are merged over the stack defaults. Remote config is +# passed to the API chart and overrides its packaged application profile. When +# the LLM addon is enabled, the stack defaults the Pylon image from +# global.sidecars (or global.image) at version 0.14.1. +api: + env: {} + remoteConfig: + configData: {} + # Image overrides for the API account-bootstrap job. alpine-k8s is not # republished under the public nvidia/nvcf catalog, so it defaults to its # upstream source, docker.io/alpine/k8s:1.36.1. Leave these unset unless you # have mirrored the image into your own registry, in which case set registry # and repository together: -# api: # accountBootstrap: # image: # registry: @@ -306,6 +314,11 @@ addons: enabled: false requestRouter: replicaCount: 3 + # Pin the Stargate release independently of the request-router chart. + # Published chart defaults can lag the image release selected by this + # stack; backendRouter.image.tag inherits this value when omitted. + image: + tag: "0.14.1" # Set only for source-tree validation. Empty uses the released OCI chart. chartPath: "" workload: @@ -339,6 +352,9 @@ addons: backendRouter: # enabled: defaults to addons.llm.enabled replicaCount: 2 + image: + # Empty inherits addons.llm.requestRouter.image.tag. + tag: "" # Addresses workers dial to reach the router. Both default to the # backend-router Service in-cluster, which is correct when workers run # alongside the control plane. Set both to externally reachable diff --git a/deploy/stacks/self-managed/global.yaml.gotmpl b/deploy/stacks/self-managed/global.yaml.gotmpl index c384d3abe..836c77c18 100644 --- a/deploy/stacks/self-managed/global.yaml.gotmpl +++ b/deploy/stacks/self-managed/global.yaml.gotmpl @@ -376,6 +376,81 @@ natsAuthCalloutService: {{- end }} {{- $nvcfApiGrpcRouteHostnames := dig "ingress" "gatewayApi" "routes" "nvcfApi" "grpc" "hostnames" (list) .Values | default (list (printf "api-grpc.%s" .Values.global.domain)) }} {{- $nvctApiGrpcRouteHostnames := dig "ingress" "gatewayApi" "routes" "nvctApi" "grpc" "hostnames" (list) .Values | default (list (printf "tasks-grpc.%s" .Values.global.domain)) }} +{{- $configuredApiEnv := dig "api" "env" dict .Values }} +{{- if not (kindIs "map" $configuredApiEnv) }} +{{- fail "api.env must be a map" }} +{{- end }} +{{- $renderedApiEnv := deepCopy $configuredApiEnv }} +{{- $legacyPylonEnvKey := "NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE" }} +{{- $legacyPylonImageConfigured := hasKey $renderedApiEnv $legacyPylonEnvKey }} +{{- $legacyPylonImage := "" }} +{{- if $legacyPylonImageConfigured }} +{{- $legacyPylonImage = index $renderedApiEnv $legacyPylonEnvKey }} +{{- if not (kindIs "string" $legacyPylonImage) }} +{{- fail "api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE must be a string" }} +{{- end }} +{{- if eq (trim $legacyPylonImage) "" }} +{{- fail "api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE must be a non-empty string" }} +{{- end }} +{{- $legacyPylonImage = trim $legacyPylonImage }} +{{- $_ := unset $renderedApiEnv $legacyPylonEnvKey }} +{{- end }} +{{- $configuredApiRemoteConfigData := dig "api" "remoteConfig" "configData" dict .Values }} +{{- if not (kindIs "map" $configuredApiRemoteConfigData) }} +{{- fail "api.remoteConfig.configData must be a map" }} +{{- end }} +{{- $apiRemoteConfigData := deepCopy $configuredApiRemoteConfigData }} +{{- $configuredNvcfRemoteConfig := dict }} +{{- if hasKey $apiRemoteConfigData "nvcf" }} +{{- $configuredNvcfRemoteConfig = index $apiRemoteConfigData "nvcf" }} +{{- if not (kindIs "map" $configuredNvcfRemoteConfig) }} +{{- fail "api.remoteConfig.configData.nvcf must be a map" }} +{{- end }} +{{- end }} +{{- $configuredSidecarsRemoteConfig := dict }} +{{- if hasKey $configuredNvcfRemoteConfig "sidecars" }} +{{- $configuredSidecarsRemoteConfig = index $configuredNvcfRemoteConfig "sidecars" }} +{{- if not (kindIs "map" $configuredSidecarsRemoteConfig) }} +{{- fail "api.remoteConfig.configData.nvcf.sidecars must be a map" }} +{{- end }} +{{- end }} +{{- $pylonRemoteConfigKey := "llm-router-client-image" }} +{{- $configuredPylonImagePresent := hasKey $configuredSidecarsRemoteConfig $pylonRemoteConfigKey }} +{{- $configuredPylonImage := "" }} +{{- if $configuredPylonImagePresent }} +{{- $configuredPylonImage = index $configuredSidecarsRemoteConfig $pylonRemoteConfigKey }} +{{- if not (kindIs "string" $configuredPylonImage) }} +{{- fail "api.remoteConfig.configData.nvcf.sidecars.llm-router-client-image must be a string" }} +{{- end }} +{{- if eq (trim $configuredPylonImage) "" }} +{{- fail "api.remoteConfig.configData.nvcf.sidecars.llm-router-client-image must be a non-empty string" }} +{{- end }} +{{- $configuredPylonImage = trim $configuredPylonImage }} +{{- $_ := set $configuredSidecarsRemoteConfig $pylonRemoteConfigKey $configuredPylonImage }} +{{- end }} +{{- if and $legacyPylonImageConfigured $configuredPylonImagePresent (ne $legacyPylonImage $configuredPylonImage) }} +{{- fail "api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE is deprecated and conflicts with api.remoteConfig.configData.nvcf.sidecars.llm-router-client-image" }} +{{- end }} +{{- $effectivePylonImage := "" }} +{{- if $configuredPylonImagePresent }} +{{- $effectivePylonImage = $configuredPylonImage }} +{{- else if $legacyPylonImageConfigured }} +{{- $effectivePylonImage = $legacyPylonImage }} +{{- else if $llmEnabled }} +{{- $effectivePylonImage = printf "%s/%s/pylon:0.14.1" $sidecarsHost $sidecarsRepo }} +{{- end }} +{{- $stackApiRemoteConfigData := dict }} +{{- $stackNvcfRemoteConfig := dict }} +{{- if or $llmEnabled $legacyPylonImageConfigured }} +{{- $_ := set $stackNvcfRemoteConfig "sidecars" (dict $pylonRemoteConfigKey $effectivePylonImage) }} +{{- end }} +{{- if $llmEnabled }} +{{- $_ := set $stackNvcfRemoteConfig "llm-request-router" (dict "worker-address" $llmRequestRouterWorkerAddress) }} +{{- end }} +{{- if $stackNvcfRemoteConfig }} +{{- $_ := set $stackApiRemoteConfigData "nvcf" $stackNvcfRemoteConfig }} +{{- end }} +{{- $apiRemoteConfigData = mergeOverwrite $apiRemoteConfigData $stackApiRemoteConfigData }} api: fullnameOverride: nvcf-api {{- if .Values.global.imagePullSecrets }} @@ -396,12 +471,10 @@ api: {{- with include "nvcf.tolerations" (dict "type" "controlplane" "tolerations" .Values.global.tolerations) }} {{- . | nindent 2 }} {{- end }} - {{- if $llmEnabled }} + {{- if $apiRemoteConfigData }} remoteConfig: configData: - nvcf: - llm-request-router: - worker-address: {{ $llmRequestRouterWorkerAddress | quote }} + {{- toYaml $apiRemoteConfigData | nindent 6 }} {{- end }} # alpine-k8s is not republished under the public nvidia/nvcf catalog, so it # defaults to its upstream Docker Hub source and a public-catalog install @@ -442,38 +515,44 @@ api: {{- end }} maxRegistryCreds: {{ $maxRegistryCreds }} + {{- /* Build the fixed API environment first, then merge configured api.env + last so operators can override a default without emitting duplicate + YAML keys. toYaml provides deterministic key ordering and treats + configured values as data rather than evaluating them as templates. */}} + {{- $apiEnv := dict + "NVCF_NATS_REGION_PLACEMENT_TAG" "dc" + "NVCF_SIDECARS_HOSTNAME" $sidecarsHost + "NVCF_SIDECARS_REPOSITORY" $sidecarsRepo + "MANAGEMENT_TRACING_ENABLED" (printf "%v" .Values.global.observability.tracing.enabled) -}} + {{- with $nvcfWorkerServiceURL }} + {{- $_ := set $apiEnv "NVCF_FQDN" . }} + {{- end }} + {{- with $nvcfWorkerGrpcServiceURL }} + {{- $_ := set $apiEnv "NVCF_GLOBAL_FQDN_GRPC" . }} + {{- end }} + {{- with $nvcfNatsWorkerServiceURL }} + {{- $_ := set $apiEnv "NVCF_NATS_WORKER_URL" . }} + {{- end }} + {{- with $essWorkerBaseURL }} + {{- /* Keep the legacy worker alias until all API builds consume nvcf.ess.base-url directly. */}} + {{- $_ := set $apiEnv "NVCF_ESS_WORKER_BASE_URL" . }} + {{- $_ := set $apiEnv "NVCF_ESS_BASE_URL" . }} + {{- end }} + {{- with dig "api" "icmsBaseUrl" nil .Values }} + {{- $_ := set $apiEnv "NVCF_ICMS_BASE_URL" . }} + {{- end }} + {{- with dig "api" "jwt" "issuerUri" nil .Values }} + {{- $_ := set $apiEnv "SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI" . }} + {{- end }} + {{- with dig "api" "jwt" "jwkSetUri" nil .Values }} + {{- $_ := set $apiEnv "SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI" . }} + {{- end }} + {{- if .Values.global.observability.tracing.enabled }} + {{- $_ := set $apiEnv "MANAGEMENT_OTLP_TRACING_ENDPOINT" (printf "%s://%s:%v/v1/traces" .Values.global.observability.tracing.collectorProtocol .Values.global.observability.tracing.collectorEndpoint .Values.global.observability.tracing.collectorPort) }} + {{- end }} + {{- $apiEnv = mergeOverwrite $apiEnv $renderedApiEnv }} env: - NVCF_NATS_REGION_PLACEMENT_TAG: "dc" - NVCF_SIDECARS_HOSTNAME: {{ $sidecarsHost }} - NVCF_SIDECARS_REPOSITORY: {{ $sidecarsRepo }} - {{- with $nvcfWorkerServiceURL }} - NVCF_FQDN: {{ . | quote }} - {{- end }} - {{- with $nvcfWorkerGrpcServiceURL }} - NVCF_GLOBAL_FQDN_GRPC: {{ . | quote }} - {{- end }} - {{- with $nvcfNatsWorkerServiceURL }} - NVCF_NATS_WORKER_URL: {{ . | quote }} - {{- end }} - {{- with $essWorkerBaseURL }} - # Keep the legacy worker alias until all API builds consume nvcf.ess.base-url directly. - NVCF_ESS_WORKER_BASE_URL: {{ . | quote }} - NVCF_ESS_BASE_URL: {{ . | quote }} - {{- end }} - {{- with dig "api" "icmsBaseUrl" nil .Values }} - NVCF_ICMS_BASE_URL: {{ . | quote }} - {{- end }} - {{- with dig "api" "jwt" "issuerUri" nil .Values }} - SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: {{ . | quote }} - {{- end }} - {{- with dig "api" "jwt" "jwkSetUri" nil .Values }} - SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: {{ . | quote }} - {{- end }} - # Observability - MANAGEMENT_TRACING_ENABLED: {{ .Values.global.observability.tracing.enabled | quote }} - {{- if .Values.global.observability.tracing.enabled }} - MANAGEMENT_OTLP_TRACING_ENDPOINT: "{{ .Values.global.observability.tracing.collectorProtocol }}://{{ .Values.global.observability.tracing.collectorEndpoint }}:{{ .Values.global.observability.tracing.collectorPort }}/v1/traces" - {{- end }} + {{- toYaml $apiEnv | nindent 4 }} invocation: fullnameOverride: invocation-service @@ -874,6 +953,8 @@ llmApiGateway: {{- $pylonGrpcDialAddress := dig "addons" "llm" "requestRouter" "backendRouter" "pylonGrpcDialAddress" "" .Values | default "" | toString | trim }} {{- $pylonReverseTunnelDialAddress := dig "addons" "llm" "requestRouter" "backendRouter" "pylonReverseTunnelDialAddress" "" .Values | default "" | toString | trim }} {{- $backendRouterEnabled := dig "addons" "llm" "requestRouter" "backendRouter" "enabled" $llmEnabled .Values }} +{{- $llmRequestRouterImageTag := dig "addons" "llm" "requestRouter" "image" "tag" "0.14.1" .Values | default "0.14.1" | toString }} +{{- $backendRouterImageTag := dig "addons" "llm" "requestRouter" "backendRouter" "image" "tag" "" .Values | default $llmRequestRouterImageTag | toString }} {{- $llmWorkerRouteEnabled := dig "ingress" "gatewayApi" "routes" "llmWorker" "enabled" false .Values }} {{- $grpcTlsEnabled := dig "addons" "llm" "requestRouter" "grpcTls" "enabled" false .Values }} {{- $grpcTlsAllowInsecureHttp := dig "addons" "llm" "requestRouter" "grpcTls" "allowInsecureHttp" false .Values }} @@ -918,6 +999,7 @@ llmRequestRouter: image: registry: {{ .Values.global.image.registry }} repository: {{ .Values.global.image.repository }}/stargate + tag: {{ $llmRequestRouterImageTag | quote }} {{- /* Backend routing follows the LLM addon rather than being separately opted into. A worker holds one registration stream and one reverse tunnel per replica, and @@ -937,6 +1019,7 @@ llmRequestRouter: image: registry: {{ .Values.global.image.registry }} repository: {{ .Values.global.image.repository }}/stargate + tag: {{ $backendRouterImageTag | quote }} {{- with $pylonGrpcDialAddress }} pylonGrpcDialAddress: {{ . | quote }} {{- end }} diff --git a/deploy/stacks/self-managed/helmfile.d/01-dependencies.yaml.gotmpl b/deploy/stacks/self-managed/helmfile.d/01-dependencies.yaml.gotmpl index e456e84dd..def1ce71a 100644 --- a/deploy/stacks/self-managed/helmfile.d/01-dependencies.yaml.gotmpl +++ b/deploy/stacks/self-managed/helmfile.d/01-dependencies.yaml.gotmpl @@ -120,7 +120,7 @@ releases: <<: *dependency - name: openbao-server # this name MUST not change - version: 0.30.25 + version: 0.32.1 condition: openbao.enabled # From defaults.yaml or env overrides namespace: vault-system <<: *dependency # Inherits base values from the dependency template diff --git a/deploy/stacks/self-managed/helmfile.d/02-core.yaml.gotmpl b/deploy/stacks/self-managed/helmfile.d/02-core.yaml.gotmpl index 53ab1eb59..bcc5626ec 100644 --- a/deploy/stacks/self-managed/helmfile.d/02-core.yaml.gotmpl +++ b/deploy/stacks/self-managed/helmfile.d/02-core.yaml.gotmpl @@ -168,7 +168,7 @@ releases: {{- $llmRequestRouterChartPath := dig "addons" "llm" "requestRouter" "chartPath" "" .Values }} chart: {{ $llmRequestRouterChartPath | default "nvcf/helm-nvcf-llm-request-router" | quote }} {{- if not $llmRequestRouterChartPath }} - version: 1.10.0 + version: 1.12.0 {{- end }} namespace: nvcf condition: addons.llm.enabled @@ -217,7 +217,7 @@ releases: {{- $gatewayRoutesChartPath := dig "ingress" "gatewayApi" "chartPath" "" .Values }} chart: {{ $gatewayRoutesChartPath | default "nvcf/nvcf-gateway-routes" | quote }} {{- if not $gatewayRoutesChartPath }} - version: 1.16.0 + version: 1.17.0 {{- end }} needs: - nvcf/notary-service diff --git a/deploy/stacks/self-managed/tests/api-env-wiring.sh b/deploy/stacks/self-managed/tests/api-env-wiring.sh new file mode 100755 index 000000000..a0f1141aa --- /dev/null +++ b/deploy/stacks/self-managed/tests/api-env-wiring.sh @@ -0,0 +1,321 @@ +#!/usr/bin/env bash +# Test that the stack owns the API remote-config contract for the Pylon worker +# sidecar while preserving arbitrary API environment and remote-config values. +# The deprecated env key is translated into remote config for one compatibility +# window, omitted from the API env ConfigMap, and rejected when it conflicts +# with the chart-native remote-config property. +set -euo pipefail + +stack_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +repo_dir="$(cd "$stack_dir/../../.." && pwd)" +work_dir="$(mktemp -d)" +test_stack_dir="$work_dir/self-managed" +environment_name="api-env-wiring-test" +environment_file="$test_stack_dir/environments/$environment_name.yaml" +secrets_file="$test_stack_dir/secrets/$environment_name-secrets.yaml" +trap 'rm -rf "$work_dir"' EXIT + +fail() { + echo "api-env-wiring: $*" >&2 + exit 1 +} + +mkdir -p "$test_stack_dir" +cp -R "$stack_dir"/. "$test_stack_dir" +printf '{}\n' >"$secrets_file" + +write_environment() { + cat >"$environment_file" +} + +render_api_values() { + local output_file="$1" + + HELMFILE_ENV="$environment_name" \ + HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" \ + helmfile \ + --file "$test_stack_dir/helmfile.d/02-core.yaml.gotmpl" \ + --environment default \ + --state-values-set ingress.gatewayApi.controllerNamespace=envoy-gateway-system \ + --state-values-set ingress.gatewayApi.gateways.shared.name=shared-gw \ + --state-values-set ingress.gatewayApi.gateways.shared.namespace=envoy-gateway-system \ + --state-values-set ingress.gatewayApi.gateways.grpc.name=grpc-gw \ + --state-values-set ingress.gatewayApi.gateways.grpc.namespace=envoy-gateway-system \ + --selector name=api \ + write-values \ + --output-file-template "$output_file" +} + +render_api_manifest() { + local values_file="$1" manifest_file="$2" + + helm template api "$repo_dir/deploy/helm/cloud-functions/nvcf-api" \ + --namespace nvcf \ + --values "$values_file" >"$manifest_file" +} + +assert_yaml_value() { + local input_file="$1" expression="$2" want="$3" label="$4" got + got="$(yq ea -r "$expression" "$input_file")" + [[ "$got" == "$want" ]] || + fail "$label: expected $want, got ${got:-}" +} + +assert_env_absent() { + local values_file="$1" key="$2" label="$3" + [[ "$(yq -r ".api.env.\"$key\"" "$values_file")" == null ]] || + fail "$label: $key must not reach rendered API env" +} + +assert_render_failure() { + local output_file="$1" expected_error="$2" label="$3" + local log_file="${output_file%.yaml}.log" + + if render_api_values "$output_file" >"$log_file" 2>&1; then + fail "$label was accepted" + fi + grep -Fq "$expected_error" "$log_file" || + fail "$label did not return the expected error" +} + +remote_pylon_expression='.api.remoteConfig.configData.nvcf.sidecars."llm-router-client-image"' +remote_worker_address_expression='.api.remoteConfig.configData.nvcf."llm-request-router"."worker-address"' +manifest_pylon_expression='select(.kind == "ConfigMap" and .data."nvcf-api.yaml" != null) | .data."nvcf-api.yaml" | from_yaml | .nvcf.sidecars."llm-router-client-image"' + +# The public chart-native path overrides the computed default. Arbitrary remote +# config and API env survive, while the stack-owned worker address wins. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +addons: + llm: + enabled: true +api: + remoteConfig: + configData: + custom: + retained: keep-me + nvcf: + sidecars: + llm-router-client-image: mirror.example.test/team/pylon:9.9.9 + retained-setting: keep-inside-sidecars + llm-request-router: + worker-address: wrong-owner.example.test:1 + env: + CUSTOM_API_ENV: configured + LITERAL_TEMPLATE_VALUE: '{{ requiredEnv "DO_NOT_EVALUATE_API_ENV" }}' + NVCF_NATS_REGION_PLACEMENT_TAG: custom-dc +EOF + +explicit_values="$work_dir/explicit-values.yaml" +explicit_manifest="$work_dir/explicit-manifest.yaml" +render_api_values "$explicit_values" >/dev/null +render_api_manifest "$explicit_values" "$explicit_manifest" +assert_yaml_value "$explicit_values" "$remote_pylon_expression" \ + mirror.example.test/team/pylon:9.9.9 "chart-native Pylon override" +assert_yaml_value "$explicit_values" '.api.remoteConfig.configData.custom.retained' \ + keep-me "generic remote config" +assert_yaml_value "$explicit_values" '.api.remoteConfig.configData.nvcf.sidecars.retained-setting' \ + keep-inside-sidecars "nested sidecar remote config" +assert_yaml_value "$explicit_values" "$remote_worker_address_expression" \ + llm-request-router.nvcf.svc.cluster.local:50071 "stack-owned worker address" +assert_yaml_value "$explicit_values" '.api.env.CUSTOM_API_ENV' \ + configured "generic API env" +assert_yaml_value "$explicit_values" '.api.env.LITERAL_TEMPLATE_VALUE' \ + '{{ requiredEnv "DO_NOT_EVALUATE_API_ENV" }}' "literal API env" +assert_yaml_value "$explicit_values" '.api.env.NVCF_NATS_REGION_PLACEMENT_TAG' \ + custom-dc "API env override precedence" +assert_yaml_value "$explicit_values" '.api.env.NVCF_SIDECARS_HOSTNAME' \ + nvcr.io "built-in sidecar hostname" +assert_yaml_value "$explicit_values" '.api.env.NVCF_SIDECARS_REPOSITORY' \ + nvidia/nvcf "built-in sidecar repository" +assert_env_absent "$explicit_values" NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE \ + "chart-native Pylon override" +assert_yaml_value "$explicit_manifest" "$manifest_pylon_expression" \ + mirror.example.test/team/pylon:9.9.9 "rendered API remote ConfigMap" +assert_yaml_value "$explicit_manifest" \ + 'select(.kind == "ConfigMap" and .data."nvcf-api.yaml" != null) | .data."nvcf-api.yaml" | from_yaml | .nvcf.sidecars.retained-setting' \ + keep-inside-sidecars "rendered nested sidecar remote config" + +# With no explicit value, the enabled LLM addon derives Pylon 0.14.1 from the +# effective worker-sidecar registry rather than the control-plane image path. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf + sidecars: + hostname: registry.example.test + repository: team/sidecars +addons: + llm: + enabled: true +EOF + +default_values="$work_dir/default-values.yaml" +render_api_values "$default_values" >/dev/null +assert_yaml_value "$default_values" "$remote_pylon_expression" \ + registry.example.test/team/sidecars/pylon:0.14.1 "computed Pylon default" + +# Local BDD fixtures rely on the same computed default after their registry +# paths are configured. They must not carry unresolved fixture placeholders +# into the higher-precedence API remote ConfigMap. +for fixture_name in self-managed-local-bdd.yaml self-managed-local-bdd-multi.yaml; do + cp "$repo_dir/tests/bdd/fixtures/$fixture_name" "$environment_file" + yq -i \ + '.global.helm.sources.repository = "sample-org/sample-team" | + .global.image.repository = "sample-org/sample-team"' \ + "$environment_file" + + fixture_values="$work_dir/${fixture_name%.yaml}-values.yaml" + render_api_values "$fixture_values" >/dev/null + fixture_pylon_image="$(yq -r "$remote_pylon_expression" "$fixture_values")" + [[ "$fixture_pylon_image" != *REPLACE_WITH_* ]] || + fail "$fixture_name Pylon property retained an unresolved fixture placeholder" + [[ "$fixture_pylon_image" == nvcr.io/sample-org/sample-team/pylon:0.14.1 ]] || + fail "$fixture_name Pylon property: expected computed 0.14.1 image, got $fixture_pylon_image" +done + +# The deprecated env key remains accepted for one compatibility window, but +# translates into remote config and never reaches the API environment. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +api: + env: + CUSTOM_API_ENV: legacy-compatible + NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: legacy.example.test/team/pylon:0.14.1 +EOF + +legacy_values="$work_dir/legacy-values.yaml" +render_api_values "$legacy_values" >/dev/null +assert_yaml_value "$legacy_values" "$remote_pylon_expression" \ + legacy.example.test/team/pylon:0.14.1 "legacy Pylon translation" +assert_yaml_value "$legacy_values" '.api.env.CUSTOM_API_ENV' \ + legacy-compatible "legacy generic API env" +assert_env_absent "$legacy_values" NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE \ + "legacy Pylon translation" + +# Supplying the same value through both paths is compatible during the +# deprecation window. The legacy env key is still omitted. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +api: + remoteConfig: + configData: + nvcf: + sidecars: + llm-router-client-image: " compatible.example.test/team/pylon:0.14.1 " + env: + NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: " compatible.example.test/team/pylon:0.14.1 " +EOF + +compatible_values="$work_dir/compatible-values.yaml" +render_api_values "$compatible_values" >/dev/null +assert_yaml_value "$compatible_values" "$remote_pylon_expression" \ + compatible.example.test/team/pylon:0.14.1 "same-value Pylon compatibility" +assert_env_absent "$compatible_values" NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE \ + "same-value Pylon compatibility" + +# Valid configured images are normalized at the remote-config boundary even +# when the LLM addon is disabled and no stack-owned LLM overlay is rendered. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +api: + remoteConfig: + configData: + nvcf: + sidecars: + llm-router-client-image: " canonical.example.test/team/pylon:0.14.1 " +EOF + +normalized_canonical_values="$work_dir/normalized-canonical-values.yaml" +render_api_values "$normalized_canonical_values" >/dev/null +assert_yaml_value "$normalized_canonical_values" "$remote_pylon_expression" \ + canonical.example.test/team/pylon:0.14.1 "normalized chart-native Pylon value" + +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +api: + env: + NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: " legacy-normalized.example.test/team/pylon:0.14.1 " +EOF + +normalized_legacy_values="$work_dir/normalized-legacy-values.yaml" +render_api_values "$normalized_legacy_values" >/dev/null +assert_yaml_value "$normalized_legacy_values" "$remote_pylon_expression" \ + legacy-normalized.example.test/team/pylon:0.14.1 "normalized legacy Pylon value" +assert_env_absent "$normalized_legacy_values" NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE \ + "normalized legacy Pylon value" + +# Explicit empty values cannot suppress the safe default or create an invalid +# worker-sidecar reference in higher-precedence remote config. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +api: + remoteConfig: + configData: + nvcf: + sidecars: + llm-router-client-image: " " +EOF + +canonical_empty_error='api.remoteConfig.configData.nvcf.sidecars.llm-router-client-image must be a non-empty string' +assert_render_failure "$work_dir/canonical-empty-values.yaml" "$canonical_empty_error" \ + "empty chart-native Pylon value" + +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +api: + env: + NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: "" +EOF + +legacy_empty_error='api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE must be a non-empty string' +assert_render_failure "$work_dir/legacy-empty-values.yaml" "$legacy_empty_error" \ + "empty legacy Pylon value" + +# Supplying conflicting legacy and chart-native values is ambiguous and fails +# before Helm receives the generated values. +write_environment <<'EOF' +global: + image: + registry: nvcr.io + repository: nvidia/nvcf +addons: + llm: + enabled: true +api: + remoteConfig: + configData: + nvcf: + sidecars: + llm-router-client-image: new.example.test/team/pylon:0.14.1 + env: + NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: legacy.example.test/team/pylon:0.14.1 +EOF + +conflict_error='api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE is deprecated and conflicts with api.remoteConfig.configData.nvcf.sidecars.llm-router-client-image' +assert_render_failure "$work_dir/conflict-values.yaml" "$conflict_error" \ + "conflicting legacy and chart-native Pylon values" + +echo "api-env-wiring: OK" diff --git a/deploy/stacks/self-managed/tests/gateway-routes-published-chart.sh b/deploy/stacks/self-managed/tests/gateway-routes-published-chart.sh new file mode 100755 index 000000000..0af769416 --- /dev/null +++ b/deploy/stacks/self-managed/tests/gateway-routes-published-chart.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +: "${NVCF_PUBLISHED_CHART_REGISTRY:?NVCF_PUBLISHED_CHART_REGISTRY is required}" +: "${NVCF_PUBLISHED_CHART_REPOSITORY:?NVCF_PUBLISHED_CHART_REPOSITORY is required}" + +stack_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +work_dir="$(mktemp -d)" +published_manifest="$work_dir/gateway-routes.yaml" +published_release="$work_dir/gateway-routes-release.json" +published_version=1.17.0 +gateway_namespace=gateway +trap 'rm -rf "$work_dir"' EXIT + +fail() { + echo "gateway-routes-published-chart: $*" >&2 + exit 1 +} + +source_args=( + --state-values-set-string "global.helm.sources.registry=$NVCF_PUBLISHED_CHART_REGISTRY" + --state-values-set-string "global.helm.sources.repository=$NVCF_PUBLISHED_CHART_REPOSITORY" +) +values_args=( + --state-values-set-string global.workerEndpoints.llmRequestRouterAddress=https://llm-grpc.example.com:50071 + --state-values-set addons.llm.enabled=true + --state-values-set addons.llm.pki.enabled=true + --state-values-set-string addons.llm.pki.allowedDomains=cluster.local + --state-values-set-string addons.llm.requestRouter.backendRouter.pylonGrpcDialAddress=https://llm-grpc.example.com:50071 + --state-values-set-string addons.llm.requestRouter.backendRouter.pylonReverseTunnelDialAddress=llm-quic.example.com:50072 + --state-values-set addons.llm.requestRouter.grpcTls.enabled=true + --state-values-set-string addons.llm.requestRouter.grpcTls.mode=certManager + --state-values-set-string addons.llm.requestRouter.grpcTls.secretName=llm-grpc-tls + --state-values-set-string 'addons.llm.requestRouter.grpcTls.dnsNames[0]=llm-grpc.example.com' + --state-values-set-string addons.llm.requestRouter.grpcTls.issuerRef.kind=ClusterIssuer + --state-values-set-string addons.llm.requestRouter.grpcTls.issuerRef.name=nvcf-openbao-pki + --state-values-set ingress.gatewayApi.enabled=true + --state-values-set-string ingress.gatewayApi.controllerNamespace="$gateway_namespace" + --state-values-set-string ingress.gatewayApi.gateways.shared.name=shared-gw + --state-values-set-string ingress.gatewayApi.gateways.shared.namespace="$gateway_namespace" + --state-values-set-string ingress.gatewayApi.gateways.grpc.name=grpc-gw + --state-values-set-string ingress.gatewayApi.gateways.grpc.namespace="$gateway_namespace" + --state-values-set ingress.gatewayApi.routes.llmWorker.enabled=true + --state-values-set-string ingress.gatewayApi.routes.llmWorker.backend.namespace=nvcf + --state-values-set-string ingress.gatewayApi.gateways.llmGrpc.name=llm-grpc-gw + --state-values-set-string ingress.gatewayApi.gateways.llmGrpc.namespace="$gateway_namespace" + --state-values-set-string ingress.gatewayApi.gateways.llmGrpc.listenerName=llm-grpc + --state-values-set-string ingress.gatewayApi.gateways.llmQuic.name=llm-quic-gw + --state-values-set-string ingress.gatewayApi.gateways.llmQuic.namespace="$gateway_namespace" + --state-values-set-string ingress.gatewayApi.gateways.llmQuic.listenerName=llm-quic +) + +(cd "$stack_dir" && HELMFILE_ENV=base HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" helmfile \ + --file helmfile.d/02-core.yaml.gotmpl \ + --environment default \ + "${source_args[@]}" \ + "${values_args[@]}" \ + --selector name=ingress \ + list --skip-charts --output json >"$published_release") + +test "$(yq -r '.[0].chart' "$published_release")" = \ + 'nvcf/nvcf-gateway-routes' || + fail "default chart source selected the wrong gateway-routes chart" +test "$(yq -r '.[0].version' "$published_release")" = "$published_version" || + fail "default chart source did not select gateway-routes $published_version" + +(cd "$stack_dir" && HELMFILE_ENV=base HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" helmfile \ + --file helmfile.d/02-core.yaml.gotmpl \ + --environment default \ + "${source_args[@]}" \ + "${values_args[@]}" \ + --selector name=ingress \ + template >"$published_manifest") + +assert_manifest_value() { + local expression="$1" + local expected="$2" + local actual + + actual="$(yq ea -r "$expression" "$published_manifest")" + test "$actual" = "$expected" || + fail "expected $expression to be $expected, got $actual" +} + +assert_manifest_value \ + '[select(.kind == "GRPCRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "GRPCRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway") | .spec.parentRefs[0].name' \ + 'llm-grpc-gw' +assert_manifest_value \ + 'select(.kind == "GRPCRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway") | .spec.parentRefs[0].sectionName' \ + 'llm-grpc' +assert_manifest_value \ + 'select(.kind == "GRPCRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway") | .spec.rules[0].backendRefs[0].name' \ + 'llm-request-router-backend-router' +assert_manifest_value \ + 'select(.kind == "GRPCRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway") | .spec.rules[0].backendRefs[0].namespace' \ + 'nvcf' +assert_manifest_value \ + 'select(.kind == "GRPCRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway") | .spec.rules[0].backendRefs[0].port' \ + '50071' +assert_manifest_value \ + '[select(.kind == "TCPRoute" and .metadata.name == "llm-worker-grpc" and .metadata.namespace == "gateway")] | length' \ + '0' + +assert_manifest_value \ + '[select(.kind == "UDPRoute" and .metadata.name == "llm-worker-quic" and .metadata.namespace == "gateway")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "UDPRoute" and .metadata.name == "llm-worker-quic" and .metadata.namespace == "gateway") | .spec.parentRefs[0].name' \ + 'llm-quic-gw' +assert_manifest_value \ + 'select(.kind == "UDPRoute" and .metadata.name == "llm-worker-quic" and .metadata.namespace == "gateway") | .spec.parentRefs[0].sectionName' \ + 'llm-quic' +assert_manifest_value \ + 'select(.kind == "UDPRoute" and .metadata.name == "llm-worker-quic" and .metadata.namespace == "gateway") | .spec.rules[0].backendRefs[0].port' \ + '50072' + +assert_manifest_value \ + '[select(.kind == "Certificate" and .metadata.name == "llm-grpc-tls" and .metadata.namespace == "gateway")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "Certificate" and .metadata.name == "llm-grpc-tls" and .metadata.namespace == "gateway") | .spec.secretName' \ + 'llm-grpc-tls' +assert_manifest_value \ + 'select(.kind == "Certificate" and .metadata.name == "llm-grpc-tls" and .metadata.namespace == "gateway") | .spec.dnsNames[0]' \ + 'llm-grpc.example.com' +assert_manifest_value \ + 'select(.kind == "Certificate" and .metadata.name == "llm-grpc-tls" and .metadata.namespace == "gateway") | .spec.issuerRef.kind' \ + 'ClusterIssuer' +assert_manifest_value \ + 'select(.kind == "Certificate" and .metadata.name == "llm-grpc-tls" and .metadata.namespace == "gateway") | .spec.issuerRef.name' \ + 'nvcf-openbao-pki' + +assert_manifest_value \ + '[select(.kind == "BackendTrafficPolicy" and .metadata.name == "llm-worker-grpc-streams" and .metadata.namespace == "gateway")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "BackendTrafficPolicy" and .metadata.name == "llm-worker-grpc-streams" and .metadata.namespace == "gateway") | .spec.targetRefs[0].kind' \ + 'GRPCRoute' +assert_manifest_value \ + 'select(.kind == "BackendTrafficPolicy" and .metadata.name == "llm-worker-grpc-streams" and .metadata.namespace == "gateway") | .spec.targetRefs[0].name' \ + 'llm-worker-grpc' +assert_manifest_value \ + 'select(.kind == "BackendTrafficPolicy" and .metadata.name == "llm-worker-grpc-streams" and .metadata.namespace == "gateway") | .spec.timeout.http.requestTimeout' \ + '0s' + +echo "gateway-routes-published-chart: all checks passed" diff --git a/deploy/stacks/self-managed/tests/llm-router-published-chart.sh b/deploy/stacks/self-managed/tests/llm-router-published-chart.sh new file mode 100755 index 000000000..f4d24a25f --- /dev/null +++ b/deploy/stacks/self-managed/tests/llm-router-published-chart.sh @@ -0,0 +1,113 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +: "${NVCF_PUBLISHED_CHART_REGISTRY:?NVCF_PUBLISHED_CHART_REGISTRY is required}" +: "${NVCF_PUBLISHED_CHART_REPOSITORY:?NVCF_PUBLISHED_CHART_REPOSITORY is required}" + +stack_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +work_dir="$(mktemp -d)" +published_manifest="$work_dir/llm-request-router.yaml" +published_release="$work_dir/llm-request-router-release.json" +published_version=1.12.0 +expected_stargate_image="${NVCF_PUBLISHED_CHART_REGISTRY}/${NVCF_PUBLISHED_CHART_REPOSITORY}/stargate:0.14.1" +trap 'rm -rf "$work_dir"' EXIT + +fail() { + echo "llm-router-published-chart: $*" >&2 + exit 1 +} + +source_args=( + --state-values-set-string "global.helm.sources.registry=$NVCF_PUBLISHED_CHART_REGISTRY" + --state-values-set-string "global.helm.sources.repository=$NVCF_PUBLISHED_CHART_REPOSITORY" +) +values_args=( + --state-values-set-string "global.image.registry=$NVCF_PUBLISHED_CHART_REGISTRY" + --state-values-set-string "global.image.repository=$NVCF_PUBLISHED_CHART_REPOSITORY" + --state-values-set-string global.workerEndpoints.llmRequestRouterAddress=https://llm-grpc.example.com:50071 + --state-values-set addons.llm.enabled=true + --state-values-set addons.llm.pki.enabled=true + --state-values-set-string addons.llm.pki.allowedDomains=cluster.local + --state-values-set-string 'addons.llm.pki.dnsNames[0]=llm-request-router.nvcf.svc.cluster.local' + --state-values-set-string 'addons.llm.pki.dnsNames[1]=*.llm-request-router-headless.nvcf.svc.cluster.local' + --state-values-set-string addons.llm.requestRouter.backendRouter.pylonGrpcDialAddress=https://llm-grpc.example.com:50071 + --state-values-set-string addons.llm.requestRouter.backendRouter.pylonReverseTunnelDialAddress=llm-quic.example.com:50072 + --state-values-set ingress.gatewayApi.controllerNamespace=gateway + --state-values-set ingress.gatewayApi.gateways.shared.name=shared-gw + --state-values-set ingress.gatewayApi.gateways.shared.namespace=gateway + --state-values-set ingress.gatewayApi.gateways.grpc.name=grpc-gw + --state-values-set ingress.gatewayApi.gateways.grpc.namespace=gateway +) + +(cd "$stack_dir" && HELMFILE_ENV=base HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" helmfile \ + --file helmfile.d/02-core.yaml.gotmpl \ + --environment default \ + "${source_args[@]}" \ + "${values_args[@]}" \ + --selector name=llm-request-router \ + list --skip-charts --output json >"$published_release") + +test "$(yq -r '.[0].chart' "$published_release")" = \ + 'nvcf/helm-nvcf-llm-request-router' || + fail "default chart source selected the wrong request-router chart" +test "$(yq -r '.[0].version' "$published_release")" = "$published_version" || + fail "default chart source did not select request-router $published_version" + +(cd "$stack_dir" && HELMFILE_ENV=base HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" helmfile \ + --file helmfile.d/02-core.yaml.gotmpl \ + --environment default \ + "${source_args[@]}" \ + "${values_args[@]}" \ + --selector name=llm-request-router \ + template >"$published_manifest") + +assert_manifest_value() { + local expression="$1" + local expected="$2" + local actual + + actual="$(yq ea -r "$expression" "$published_manifest")" + test "$actual" = "$expected" || + fail "expected $expression to be $expected, got $actual" +} + +assert_manifest_value \ + '[select(.kind == "Deployment" and .metadata.name == "llm-request-router" and .metadata.namespace == "nvcf")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "Deployment" and .metadata.name == "llm-request-router" and .metadata.namespace == "nvcf") | .spec.template.spec.containers[0].image' \ + "$expected_stargate_image" +assert_manifest_value \ + '[select(.kind == "Deployment" and .metadata.name == "llm-request-router-backend-router" and .metadata.namespace == "nvcf")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "Deployment" and .metadata.name == "llm-request-router-backend-router" and .metadata.namespace == "nvcf") | .spec.template.spec.containers[] | select(.name == "backend-router") | .image' \ + "$expected_stargate_image" + +for expected_arg in \ + '--grpc-pylon-dial-addr=https://llm-grpc.example.com:50071' \ + '--reverse-tunnel-pylon-dial-addr=llm-quic.example.com:50072' \ + '--tls-cert-path=/etc/stargate/tls/tls.crt' \ + '--tls-key-path=/etc/stargate/tls/tls.key'; do + assert_manifest_value \ + "[select(.kind == \"Deployment\" and .metadata.name == \"llm-request-router\") | .spec.template.spec.containers[0].args[] | select(. == \"$expected_arg\")] | length" \ + '1' +done + +assert_manifest_value \ + '[select(.kind == "Deployment" and .metadata.name == "llm-request-router") | .spec.template.spec.containers[0].args[] | select(. == "--quic-insecure")] | length' \ + '0' +assert_manifest_value \ + '[select(.kind == "Certificate" and .metadata.name == "stargate-quic-tls")] | length' \ + '1' +assert_manifest_value \ + 'select(.kind == "Certificate" and .metadata.name == "stargate-quic-tls") | .spec.dnsNames[0]' \ + 'llm-request-router.nvcf.svc.cluster.local' +assert_manifest_value \ + 'select(.kind == "Certificate" and .metadata.name == "stargate-quic-tls") | .spec.dnsNames[1]' \ + '*.llm-request-router-headless.nvcf.svc.cluster.local' + +echo "llm-router-published-chart: all checks passed" diff --git a/deploy/stacks/self-managed/tests/openbao-published-chart.sh b/deploy/stacks/self-managed/tests/openbao-published-chart.sh new file mode 100755 index 000000000..f1796bdc7 --- /dev/null +++ b/deploy/stacks/self-managed/tests/openbao-published-chart.sh @@ -0,0 +1,110 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +stack_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +stacks_dir="$(cd "$stack_dir/.." && pwd)" +work_dir="$(mktemp -d)" +test_stacks_dir="$work_dir/stacks" +test_stack_dir="$test_stacks_dir/self-managed" +environment_name="openbao-published-chart-test" +secrets_file="$test_stack_dir/secrets/$environment_name-secrets.yaml" +: "${NVCF_PUBLISHED_CHART_REGISTRY:?NVCF_PUBLISHED_CHART_REGISTRY is required}" +: "${NVCF_PUBLISHED_CHART_REPOSITORY:?NVCF_PUBLISHED_CHART_REPOSITORY is required}" +published_chart_registry="$NVCF_PUBLISHED_CHART_REGISTRY" +published_chart_repository="$NVCF_PUBLISHED_CHART_REPOSITORY" +published_chart_version=0.32.1 +trap 'rm -rf "$work_dir"' EXIT + +fail() { + echo "openbao-published-chart: $*" >&2 + exit 1 +} + +# Match the existing Cassandra/OpenBao credential-wiring test's rendered stack +# values, but keep that local-chart test offline and independent of registry +# availability. +mkdir -p "$test_stacks_dir" +cp -R "$stacks_dir"/. "$test_stacks_dir" +cp "$test_stack_dir/secrets/secrets.yaml.template" "$secrets_file" + +for stack_environments in "$test_stacks_dir"/*/environments; do + test -d "$stack_environments" || continue + test -e "$stack_environments/$environment_name.yaml" || + printf '{}\n' >"$stack_environments/$environment_name.yaml" +done + +openbao_values="$work_dir/openbao-values.yaml" +openbao_release="$work_dir/openbao-release.json" +HELMFILE_ENV="$environment_name" \ + HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" \ + helmfile \ + --file "$test_stack_dir/helmfile.d" \ + --environment default \ + --state-values-set ingress.gatewayApi.controllerNamespace=envoy-gateway-system \ + --state-values-set ingress.gatewayApi.gateways.shared.name=shared-gw \ + --state-values-set ingress.gatewayApi.gateways.shared.namespace=envoy-gateway-system \ + --state-values-set ingress.gatewayApi.gateways.grpc.name=grpc-gw \ + --state-values-set ingress.gatewayApi.gateways.grpc.namespace=envoy-gateway-system \ + --selector name=openbao-server \ + write-values \ + --output-file-template "$openbao_values" >/dev/null + +test -s "$openbao_values" || fail "helmfile wrote no OpenBao values" + +HELMFILE_ENV="$environment_name" \ + HELMFILE_CACHE_HOME="$work_dir/helmfile-cache" \ + helmfile \ + --file "$test_stack_dir/helmfile.d/01-dependencies.yaml.gotmpl" \ + --environment default \ + --selector name=openbao-server \ + list --skip-charts --output json >"$openbao_release" + +test "$(yq -r '.[0].chart' "$openbao_release")" = \ + 'nvcf/helm-nvcf-openbao-server' || + fail "dependency Helmfile selected the wrong OpenBao chart" +test "$(yq -r '.[0].version' "$openbao_release")" = \ + "$published_chart_version" || + fail "dependency Helmfile did not select OpenBao $published_chart_version" + +expected_password="$(yq -r ' + .openbao.migrations.env[] | + select(.name == "DEFAULT_CASSANDRA_PASSWORD") | + .value +' "$openbao_values")" +if test -z "$expected_password" || test "$expected_password" = 'null'; then + fail "rendered stack values contain no DEFAULT_CASSANDRA_PASSWORD" +fi + +published_manifest="$work_dir/openbao-published-manifest.yaml" +published_chart="oci://${published_chart_registry}/${published_chart_repository}/helm-nvcf-openbao-server" +helm template openbao-server "$published_chart" \ + --version "$published_chart_version" \ + --namespace vault-system \ + --values "$openbao_values" \ + --show-only templates/hook-post-02-migrations.yaml >"$published_manifest" || + fail "published OpenBao $published_chart_version chart did not render" + +password_count="$(yq -r ' + [.spec.template.spec.containers[] | + select(.name == "bao-migrations") | + .env[] | + select(.name == "DEFAULT_CASSANDRA_PASSWORD")] | + length +' "$published_manifest")" +test "$password_count" = '1' || + fail "published OpenBao chart must render exactly one DEFAULT_CASSANDRA_PASSWORD" + +password="$(yq -r ' + .spec.template.spec.containers[] | + select(.name == "bao-migrations") | + .env[] | + select(.name == "DEFAULT_CASSANDRA_PASSWORD") | + .value +' "$published_manifest")" +test "$password" = "$expected_password" || + fail "published OpenBao chart did not preserve DEFAULT_CASSANDRA_PASSWORD" + +echo "openbao-published-chart: all checks passed" diff --git a/docs/user/llm-function-enablement.md b/docs/user/llm-function-enablement.md index d9c288241..b934216a1 100644 --- a/docs/user/llm-function-enablement.md +++ b/docs/user/llm-function-enablement.md @@ -567,10 +567,17 @@ sidecar image passed to generated LLM workers: ```yaml api: - env: - NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: //pylon:0.2.1 + remoteConfig: + configData: + nvcf: + sidecars: + llm-router-client-image: //pylon:0.14.1 ``` +`api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE` is deprecated. The stack +translates it into remote config for one compatibility window and omits it from +the API environment. Do not set both paths to different values. + The LLM API Gateway and request router images are resolved from the same stack artifact registry settings as the other control-plane services. @@ -649,8 +656,10 @@ nvcf-cli self-hosted \ --cluster-name ``` -Existing LLM function pods keep their current sidecar arguments. Recreate or -redeploy those functions after refreshing the compute plane. +Existing LLM function versions retain the worker-sidecar image metadata +captured when the version is created. Replacing pods or redeploying the same +version does not apply a new Pylon image. After updating the control plane, +create and deploy a new function version. After deploying an LLM function, verify the workload trust bundle. Compare only `.data.fingerprint` with `transportTls.trustBundleFingerprint` in the diff --git a/docs/user/local-development/single-cluster-helmfile.md b/docs/user/local-development/single-cluster-helmfile.md index c4325c26f..893efa168 100644 --- a/docs/user/local-development/single-cluster-helmfile.md +++ b/docs/user/local-development/single-cluster-helmfile.md @@ -98,9 +98,9 @@ done Across the two files, the substitutions update `global.helm.sources.repository` and `global.image.repository`. The control-plane file also updates -`api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE`. When the LLM addon is enabled, -the stack defaults the worker address to the cluster-local request-router -service. Set +`api.remoteConfig.configData.nvcf.sidecars.llm-router-client-image`. When the +LLM addon is enabled, the stack defaults the worker address to the cluster-local +request-router service. Set `global.imagePullSecrets[0].name` if your secret name differs from `nvcr-pull-secret`. The compute-plane environment provides the NVCA endpoint settings consumed by its Helmfile. diff --git a/docs/user/manifest.md b/docs/user/manifest.md index f0a4aa0a1..c2548fae2 100644 --- a/docs/user/manifest.md +++ b/docs/user/manifest.md @@ -154,12 +154,12 @@ The following tables list the complete artifact inventory. | `helm-nvcf-grpc-proxy` | `1.6.7` | Required | Deploys the gRPC proxy service. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-grpc-proxy:1.6.7` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/grpc-proxy) | | `helm-nvcf-invocation-service` | `1.5.5` | Required | Deploys the HTTP invocation service. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-invocation-service:1.5.5` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/http-invocation) | | `helm-nvcf-llm-api-gateway` | `1.2.0` | Optional | Deploys the OpenAI-compatible LLM API gateway. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-llm-api-gateway:1.2.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/llm-api-gateway) | -| `helm-nvcf-llm-request-router` | `1.10.0` | Optional | Deploys the LLM request router. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-llm-request-router:1.10.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/llm-request-router) | +| `helm-nvcf-llm-request-router` | `1.12.0` | Optional | Deploys the LLM request router. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-llm-request-router:1.12.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/llm-request-router) | | `helm-nvcf-nats` | `0.7.1` | Required | Deploys NATS messaging for the control plane. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-nats:0.7.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/nats) / [Upstream](https://github.com/nats-io/k8s) | | `helm-nvcf-nats-auth-callout-service` | `1.1.3` | Required | Deploys the NATS authorization callout service. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-nats-auth-callout-service:1.1.3` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/nats-auth-callout) | | `helm-nvcf-notary-service` | `1.4.2` | Required | Deploys the notary service for signing and validation. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-notary-service:1.4.2` | | | `helm-nvcf-nvct-api` | `1.4.3` | Required | Deploys the NVCF tenant API service. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-nvct-api:1.4.3` | | -| `helm-nvcf-openbao-server` | `0.30.23` | Required | Deploys OpenBao secret management. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-openbao-server:0.30.23` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/openbao) / [Upstream](https://github.com/openbao/openbao-helm) | +| `helm-nvcf-openbao-server` | `0.32.1` | Required | Deploys OpenBao secret management. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-openbao-server:0.32.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/openbao) / [Upstream](https://github.com/openbao/openbao-helm) | | `helm-nvcf-pki` | `0.1.0` | Optional | Provisions the OpenBao-backed ClusterIssuer for NVCF service TLS. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-pki:0.1.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/nvcf-pki) | | `helm-nvcf-rate-limiter` | `1.0.3` | Optional | Deploys request rate limiting for supported invocation paths. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-rate-limiter:1.0.3` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/ratelimiter) | | `helm-nvcf-sis` | `1.18.3` | Required | Deploys the Spot Instance Service. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-sis:1.18.3` | | @@ -168,7 +168,7 @@ The following tables list the complete artifact inventory. | `helm-nvcf-vanity-gateway` | `0.3.0` | Optional | Deploys the optional vanity hostname gateway. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-nvcf-vanity-gateway:0.3.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/vanity-gateway) | | `helm-reval` | `1.3.8` | Required | Deploys the function revalidation service. | `https://helm.ngc.nvidia.com/nvidia/nvcf/helm-reval:1.3.8` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/helm-reval) | | `nvcf-example-dashboards` | `1.6.0` | Optional | Deploys example Grafana dashboards for NVCF telemetry. | `https://helm.ngc.nvidia.com/nvidia/nvcf/nvcf-example-dashboards:1.6.0` | | -| `nvcf-gateway-routes` | `1.16.0` | Optional | Deploys Gateway API routes for the reference architecture. | `https://helm.ngc.nvidia.com/nvidia/nvcf/nvcf-gateway-routes:1.16.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/gateway-routes) | +| `nvcf-gateway-routes` | `1.17.0` | Optional | Deploys Gateway API routes for the reference architecture. | `https://helm.ngc.nvidia.com/nvidia/nvcf/nvcf-gateway-routes:1.17.0` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/deploy/helm/gateway-routes) | | `nvcf-observability-reference-stack` | `1.10.0` | Optional | Deploys a reference observability backend for evaluation. | `https://helm.ngc.nvidia.com/nvidia/nvcf/nvcf-observability-reference-stack:1.10.0` | | ### Control plane services and images @@ -184,7 +184,7 @@ The following tables list the complete artifact inventory. | `cert-manager-webhook` | `v1.20.2` | Required | Validates and converts cert-manager API resources. | `nvcr.io/nvidia/nvcf/cert-manager-webhook:v1.20.2` | [Upstream](https://github.com/cert-manager/cert-manager) | | `ess-api` | `v0.57.26` | Required | Provides encrypted application secrets to NVCF workloads. | `nvcr.io/nvidia/nvcf/ess-api:v0.57.26` | | | `llm-api-gateway` | `0.8.3` | Optional | Exposes OpenAI-compatible APIs for LLM functions. | `nvcr.io/0833294136851237/selfhosted-ga/llm-api-gateway:0.8.3-ea` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/invocation-plane-services/llm-api-gateway) | -| `llm-request-router` | `0.3.2` | Optional | Routes LLM requests to eligible worker instances. | `nvcr.io/nvidia/nvcf/stargate:0.3.2` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/libraries/rust/stargate) | +| `llm-request-router` | `0.14.1` | Optional | Routes LLM requests to eligible worker instances. | `nvcr.io/nvidia/nvcf/stargate:0.14.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/libraries/rust/stargate) | | `nats-box` | `0.19.7-nonroot` | Required | Provides NATS administration and diagnostic utilities. | `nvcr.io/nvidia/nvcf/nats-box:0.19.7-nonroot` | [Upstream](https://github.com/nats-io/nats-box) | | `nats-server` | `2.11.17-alpine3.22` | Required | Provides messaging for function deployment and invocation. | `nvcr.io/nvidia/nvcf/nats-server:2.11.17-alpine3.22` | [Upstream](https://github.com/nats-io/nats-server) | | `nats-server-config-reloader` | `0.23.0` | Required | Reloads NATS server configuration when mounted settings change. | `docker.io/natsio/nats-server-config-reloader:0.23.0` | [Upstream](https://github.com/nats-io/k8s) | @@ -228,7 +228,7 @@ The following tables list the complete artifact inventory. | `nvcf_worker_init` | `1.0.1` | Required | Prepares function resources before the user container starts. | `nvcr.io/nvidia/nvcf/nvcf_worker_init:1.0.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/compute-plane-services/worker-init) | | `nvcf_worker_llm_credentials` | `1.0.1` | Optional | Maintains a current NVCF worker token for LLM function workloads. | `nvcr.io/nvidia/nvcf/nvcf_worker_llm_credentials:1.0.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/compute-plane-services/worker-llm-credentials) | | `nvcf_worker_utils` | `1.0.1` | Required | Proxies NATS traffic between function containers and the control plane. | `nvcr.io/nvidia/nvcf/nvcf_worker_utils:1.0.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/compute-plane-services/worker-utils) | -| `pylon` | `0.3.1` | Optional | Connects LLM worker pods to the LLM request router. | `nvcr.io/nvidia/nvcf/pylon:0.3.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/libraries/rust/stargate) | +| `pylon` | `0.14.1` | Optional | Connects LLM worker pods to the LLM request router. | `nvcr.io/nvidia/nvcf/pylon:0.14.1` | [GitHub](https://github.com/NVIDIA/nvcf/tree/main/src/libraries/rust/stargate) | ### EA-only CVE-impacted artifacts diff --git a/docs/version-catalog/main.yaml b/docs/version-catalog/main.yaml index 5b25545be..85265dc41 100644 --- a/docs/version-catalog/main.yaml +++ b/docs/version-catalog/main.yaml @@ -115,7 +115,7 @@ publications: version: 1.5.9-hotfix.1 registry: public-images - name: pylon - version: 0.3.1 + version: 0.14.1 registry: public-images - name: spot version: 1.563.1-hotfix.1 @@ -128,7 +128,7 @@ publications: published_version: 0.8.3-ea registry: selfhosted-ga - name: llm-request-router - version: 0.3.2 + version: 0.14.1 registry: public-images - name: nats-server-config-reloader version: 0.23.0 @@ -181,7 +181,7 @@ publications: registry: public-helm chart_format: http - name: helm-nvcf-llm-request-router - version: 1.10.0 + version: 1.12.0 registry: public-helm chart_format: http - name: helm-nvcf-nats-auth-callout-service @@ -201,7 +201,7 @@ publications: registry: public-helm chart_format: http - name: helm-nvcf-openbao-server - version: 0.30.23 + version: 0.32.1 registry: public-helm chart_format: http - name: helm-nvcf-pki @@ -233,7 +233,7 @@ publications: registry: public-helm chart_format: http - name: nvcf-gateway-routes - version: 1.16.0 + version: 1.17.0 registry: public-helm chart_format: http - name: nvcf-container-cache @@ -805,7 +805,7 @@ artifacts: - name: helm-nvcf-llm-request-router type: chart registry: staging - version: 1.10.0 + version: 1.12.0 - name: helm-nvcf-nats-auth-callout-service type: chart registry: staging @@ -825,7 +825,7 @@ artifacts: - name: helm-nvcf-openbao-server type: chart registry: staging - version: 0.30.23 + version: 0.32.1 - name: helm-nvcf-pki type: chart registry: staging @@ -881,7 +881,7 @@ artifacts: - name: nvcf-gateway-routes type: chart registry: staging - version: 1.16.0 + version: 1.17.0 - name: nvcf-grpc-proxy type: image registry: staging @@ -933,7 +933,7 @@ artifacts: - name: pylon type: image registry: staging - version: 0.3.1 + version: 0.14.1 - name: reval-server type: image registry: staging @@ -971,7 +971,7 @@ supplemental_artifacts: type: image registry: staging repository_name: stargate - version: 0.3.2 + version: 0.14.1 - name: nvcf-ai-api-gateway-service type: image registry: staging diff --git a/src/clis/nvcf-cli/internal/agentskill/skilldata_generated.go b/src/clis/nvcf-cli/internal/agentskill/skilldata_generated.go index d1ddd58e2..2cf9fa07f 100644 --- a/src/clis/nvcf-cli/internal/agentskill/skilldata_generated.go +++ b/src/clis/nvcf-cli/internal/agentskill/skilldata_generated.go @@ -31,7 +31,7 @@ import ( const skillDataZipBase64 = "" + "UEsDBBQACAAAAAAAIQAAAAAAAAAAAAAAAAASAAkAZGF0YS9tYW5pZmVzdC5qc29uVVQFAAEAps4S" + "ewogICJzY2hlbWFWZXJzaW9uIjogMSwKICAiZ2VuZXJhdGVkQXQiOiAiMTk3MC0wMS0wMVQwMDow" + - "MDowMFoiLAogICJ0b3RhbEZpbGVzIjogMjIsCiAgInRvdGFsQnl0ZXMiOiAxOTg5NzcsCiAgImZp" + + "MDowMFoiLAogICJ0b3RhbEZpbGVzIjogMjIsCiAgInRvdGFsQnl0ZXMiOiAxOTkzNzksCiAgImZp" + "bGVzIjogWwogICAgewogICAgICAicGF0aCI6ICJudmNmLXNlbGYtbWFuYWdlZC1jbGkvU0tJTEwu" + "bWQiLAogICAgICAic2hhMjU2IjogImM0ZjI4OGY2MmU1MDg4NjI5ZGY1MTcyYTlhYjMwZWVlOGMz" + "OTRmYzgzOTFjMjMwMDMzNzI2NGVmZjRiMmU3ZWYiLAogICAgICAic2l6ZSI6IDI0OTA5CiAgICB9" + @@ -94,16 +94,16 @@ const skillDataZipBase64 = "" + "ICJzaGEyNTYiOiAiYTUyODE1OGJjMGJmZmZlZWY2ZTAxZGUzODRjYWY1NjVlNjQ0NWI0OGFiZDNj" + "ZWU0MDA0ODcxZjUyM2Y1M2I5NyIsCiAgICAgICJzaXplIjogMTI0MzcKICAgIH0sCiAgICB7CiAg" + "ICAgICJwYXRoIjogIm52Y2Ytc2VsZi1tYW5hZ2VkLWluc3RhbGxhdGlvbi9yZWZlcmVuY2VzL2hl" + - "bG1maWxlLXN0cnVjdHVyZS5tZCIsCiAgICAgICJzaGEyNTYiOiAiMWQ1Mjc3NmY5ZDdkOGRmNThk" + - "ZWZlMzA0NmQ2M2VjOGY1MmZkMzAxYzIxNWJmMGUxYWI2YmI5MTkxNjIwNzNkZSIsCiAgICAgICJz" + - "aXplIjogMTAxMjcKICAgIH0sCiAgICB7CiAgICAgICJwYXRoIjogIm52Y2Ytc2VsZi1tYW5hZ2Vk" + + "bG1maWxlLXN0cnVjdHVyZS5tZCIsCiAgICAgICJzaGEyNTYiOiAiMjAxMTdkOWVlYzk5NzhiM2Q5" + + "M2Q1MjQyZjMxYTBkODUzYzgxNTY5YWNhNzY4NDFjYWViNDVkOGU0NmE3M2Y1NCIsCiAgICAgICJz" + + "aXplIjogMTA1MjkKICAgIH0sCiAgICB7CiAgICAgICJwYXRoIjogIm52Y2Ytc2VsZi1tYW5hZ2Vk" + "LWluc3RhbGxhdGlvbi9yZWZlcmVuY2VzL252Y2YtdWktYWRkb24ubWQiLAogICAgICAic2hhMjU2" + "IjogIjUwNTI0ZTZlMTk3MTE4M2JkYzlhZDBhYzM4YTgwN2RmNzJjYzA4NTFiNzlmNTE4ZmUxMWE0" + "OGU0NTk3Yzg1MGUiLAogICAgICAic2l6ZSI6IDE5NzcKICAgIH0sCiAgICB7CiAgICAgICJwYXRo" + "IjogIm52Y2Ytc2VsZi1tYW5hZ2VkLWluc3RhbGxhdGlvbi9yZWZlcmVuY2VzL3B1bGwtc2VjcmV0" + "cy5tZCIsCiAgICAgICJzaGEyNTYiOiAiZWMxZjNmZmNkMTdlNWY5NTgzZmVlMzk3NDRlZTg5N2Q0" + "NjZiYzNjZjYzODkyOTQyNjRkODllN2Y5ZWQwMWI4NyIsCiAgICAgICJzaXplIjogNTYzMgogICAg" + - "fQogIF0KfQpQSwcI0GJVtkkQAABJEAAAUEsDBBQACAAAAAAAIQAAAAAAAAAAAAAAAAAjAAkAZGF0" + + "fQogIF0KfQpQSwcIxAuyqkkQAABJEAAAUEsDBBQACAAAAAAAIQAAAAAAAAAAAAAAAAAjAAkAZGF0" + "YS9udmNmLXNlbGYtbWFuYWdlZC1jbGkvU0tJTEwubWRVVAUAAQCmzhItLS0KbmFtZTogbnZjZi1z" + "ZWxmLW1hbmFnZWQtY2xpCmRlc2NyaXB0aW9uOiB8CiAgSW5zdGFsbCwgb3BlcmF0ZSwgYW5kIHRl" + "YXIgZG93biBzZWxmLWhvc3RlZCBOVklESUEgQ2xvdWQgRnVuY3Rpb25zIChOVkNGKQogIGRlcGxv" + @@ -3457,230 +3457,237 @@ const skillDataZipBase64 = "" + "dWVzdC1yb3V0ZXItbG9hZC1iYWxhbmNpbmcubWQpCmd1aWRlLgoKSWYgdGhlIHNpZGVjYXIgaW1h" + "Z2UgaXMgbWlycm9yZWQgb3V0c2lkZSB0aGUgc3RhY2sncyBkZWZhdWx0IGltYWdlIHJlZ2lzdHJ5" + "IGFuZApyZXBvc2l0b3J5LCBzZXQgdGhlIGdlbmVyYXRlZCB3b3JrZXIgc2lkZWNhciBpbWFnZSBl" + - "eHBsaWNpdGx5OgoKYGBgeWFtbAphcGk6CiAgZW52OgogICAgTlZDRl9TSURFQ0FSU19MTE1fUk9V" + - "VEVSX0NMSUVOVF9JTUFHRTogPHJlZ2lzdHJ5Pi88cmVwb3NpdG9yeT4vcHlsb246MC4yLjEKYGBg" + - "CgpSZW5kZXIgYW5kIGFwcGx5IHRoZSB1cGRhdGVkIGNvbnRyb2wtcGxhbmUgZW52aXJvbm1lbnQs" + - "IHRoZW4gcmVmcmVzaCBldmVyeQpyZWdpc3RlcmVkIGNvbXB1dGUgcGxhbmUgdXNpbmcgdGhlIHNh" + - "bWUgaGFuZG9mZiB1c2VkIGZvciBpdHMgaW5zdGFsbGF0aW9uIHNvCk5WQ0EgcmVjZWl2ZXMgYGFn" + - "ZW50Q29uZmlnLm1lcmdlQ29uZmlnYDoKCmBgYGJhc2gKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1m" + - "aWxlIHRlbXBsYXRlCkhFTE1GSUxFX0VOVj08ZW52PiBoZWxtZmlsZSBzeW5jCmBgYAoKVXNlIHRo" + - "ZSBjb21wbGV0ZSBjb21wdXRlLXBsYW5lIGluc3RhbGwgY29tbWFuZCBmcm9tCltTcGxpdCBDb21w" + - "dXRlLVBsYW5lIEluc3RhbGxhdGlvbl0oY29tcHV0ZS1wbGFuZS1pbnN0YWxsYXRpb24ubWQpLiBB" + - "CkNMSS1wcm9maWxlIGluc3RhbGxhdGlvbiBtdXN0IHJlbWFpbiBwcm9maWxlLWRyaXZlbjsgYSBI" + - "ZWxtZmlsZSBpbnN0YWxsYXRpb24KbXVzdCByZW1haW4gdmFsdWVzLWRyaXZlbi4KCkV4aXN0aW5n" + - "IExMTSBmdW5jdGlvbiBwb2RzIGtlZXAgdGhlaXIgZXhpc3Rpbmcgc2lkZWNhciBhcmd1bWVudHMu" + - "IFJlY3JlYXRlIG9yCnJlZGVwbG95IHRob3NlIGZ1bmN0aW9ucyBhZnRlciB0aGUgY29tcHV0ZS1w" + - "bGFuZSByZWZyZXNoLiBWZXJpZnkgdGhlIGNvbnRyb2wKcGxhbmUsIHJvdXRlLCBhbmQgd29ya2Vy" + - "IHNpZGVjYXI6CgpgYGBiYXNoCmt1YmVjdGwgZ2V0IGRlcGxveSAtbiBudmNmIGxsbS1hcGktZ2F0" + - "ZXdheQprdWJlY3RsIGdldCBzdGF0ZWZ1bHNldCAtbiBudmNmIGxsbS1yZXF1ZXN0LXJvdXRlcgpr" + - "dWJlY3RsIGdldCBwb2RzIC1uIG52Y2YgfCBncmVwIC1FICdsbG0tYXBpLWdhdGV3YXl8bGxtLXJl" + - "cXVlc3Qtcm91dGVyJwprdWJlY3RsIGdldCBodHRwcm91dGUgLUEgfCBncmVwIGxsbQprdWJlY3Rs" + - "IC1uIG52Y2YtYmFja2VuZCBnZXQgcG9kIDxmdW5jdGlvbi1wb2Q+IFwKICAtbyBqc29ucGF0aD0n" + - "e3JhbmdlIC5zcGVjLmNvbnRhaW5lcnNbPyhALm5hbWU9PSJsbG0td29ya2VyIildLmFyZ3NbKl19" + - "ey59eyJcXG4ifXtlbmR9JwpgYGAKCkZvciBsb2NhbCBwbGFpbnRleHQgY2x1c3RlcnMsIHRoZSBg" + - "bGxtLXdvcmtlcmAgYXJncyBtdXN0IGluY2x1ZGUKYC0tcXVpYy1pbnNlY3VyZWAuCgpGb3IgbG9h" + - "ZC1iYWxhbmNlciBzY2hlbWEgYW5kIGFsZ29yaXRobSBiZWhhdmlvciwgc2VlIHRoZQpbU3Rhcmdh" + - "dGUgY29uZmlndXJhdGlvbiBndWlkZV0oaHR0cHM6Ly9naXRodWIuY29tL05WSURJQS9udmNmL2Js" + - "b2IvbWFpbi9zcmMvbGlicmFyaWVzL3J1c3Qvc3RhcmdhdGUvZG9jcy9sb2FkLWJhbGFuY2VyLWNv" + - "bmZpZ3VyYXRpb24ubWQpLgoKIyMgSGVsbWZpbGUgU2VsZWN0b3JzCgpUYXJnZXQgc3BlY2lmaWMg" + - "cmVsZWFzZXMgb3IgZ3JvdXBzOgoKYGBgYmFzaAojIEJ5IHJlbGVhc2UgZ3JvdXAKSEVMTUZJTEVf" + - "RU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgcmVsZWFzZS1ncm91cD1kZXBlbmRlbmNpZXMg" + - "c3luYwpIRUxNRklMRV9FTlY9PGVudj4gaGVsbWZpbGUgLS1zZWxlY3RvciByZWxlYXNlLWdyb3Vw" + - "PXNlcnZpY2VzIHN5bmMKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgcmVs" + - "ZWFzZS1ncm91cD1pbmdyZXNzIHN5bmMKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2Vs" + - "ZWN0b3IgcmVsZWFzZS1ncm91cD1vYnNlcnZhYmlsaXR5IHN5bmMKCiMgQnkgcmVsZWFzZSBuYW1l" + - "CkhFTE1GSUxFX0VOVj08ZW52PiBoZWxtZmlsZSAtLXNlbGVjdG9yIG5hbWU9Y2Fzc2FuZHJhIHN5" + - "bmMKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgbmFtZT1hZG1pbi1pc3N1" + - "ZXItcHJveHkgc3luYwpIRUxNRklMRV9FTlY9PGVudj4gaGVsbWZpbGUgLS1zZWxlY3RvciBuYW1l" + - "PWxsbS1yZXF1ZXN0LXJvdXRlciBzeW5jCkhFTE1GSUxFX0VOVj08ZW52PiBoZWxtZmlsZSAtLXNl" + - "bGVjdG9yIG5hbWU9bGxtLWFwaS1nYXRld2F5IHN5bmMKCiMgVGVtcGxhdGUgb25seSAoZHJ5IHJ1" + - "bikKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgbmFtZT1jYXNzYW5kcmEg" + - "dGVtcGxhdGUKCiMgRGVzdHJveSBhIHNpbmdsZSByZWxlYXNlCkhFTE1GSUxFX0VOVj08ZW52PiBo" + - "ZWxtZmlsZSAtLXNlbGVjdG9yIG5hbWU9Y2Fzc2FuZHJhIGRlc3Ryb3kKYGBgCgpOb3RlOiBgYWRt" + - "aW4taXNzdWVyLXByb3h5YCBoYXMgbm8gYHJlbGVhc2UtZ3JvdXBgIGxhYmVsLiBVc2UgYC0tc2Vs" + - "ZWN0b3IgbmFtZT1hZG1pbi1pc3N1ZXItcHJveHlgLgpQSwcIwFYg548nAACPJwAAUEsDBBQACAAA" + - "AAAAIQAAAAAAAAAAAAAAAAA/AAkAZGF0YS9udmNmLXNlbGYtbWFuYWdlZC1pbnN0YWxsYXRpb24v" + - "cmVmZXJlbmNlcy9udmNmLXVpLWFkZG9uLm1kVVQFAAEAps4SIyBOVkNGIFVJIEFkZG9uCgpOVkNG" + - "IFVJIGlzIGFuIG9wdGlvbmFsLCBjdXN0b21lci1mYWNpbmcgTlZDRiBhZG1pbi1wYW5lbCBVSS4g" + - "SXQgaXMgZGlzYWJsZWQgYnkKZGVmYXVsdCBhbmQgYXZhaWxhYmxlIG9ubHkgaW4gc3RhY2sgcGFj" + - "a2FnZXMgdGhhdCBpbmNsdWRlIHRoZSBOVkNGIFVJIGFkZG9uLgoKIyMgV2hlbiB0byBlbmFibGUK" + - "CkVuYWJsZSBOVkNGIFVJIG9ubHkgd2hlbiB5b3UgbmVlZCB0aGUgYWRtaW4tcGFuZWwgVUkgYW5k" + - "IHlvdXIgZXh0cmFjdGVkIHN0YWNrCnBhY2thZ2UgaW5jbHVkZXMgdGhlIGFkZG9uLiBJZiB0aGUg" + - "cGFja2FnZSBkb2VzIG5vdCBjb250YWluIGEgYG52Y2YtdWlgIHJlbGVhc2UKYW5kIGBudmNmVWlg" + - "IHJvdXRlIHZhbHVlcywgc2tpcCB0aGUgYWRkb24gdW50aWwgeW91IHVzZSBhIHBhY2thZ2UgdGhh" + - "dCBpbmNsdWRlcwp0aGVtLgoKIyMgRW5hYmxlIHRoZSBhZGRvbgoKRW5hYmxlIGl0IGluIGBlbnZp" + - "cm9ubWVudHMvPGVudi1uYW1lPi55YW1sYDoKCmBgYHlhbWwKYWRkb25zOgogIG52Y2ZVaToKICAg" + - "IGVuYWJsZWQ6IHRydWUKYGBgCgojIyBDcmVhdGUgbmFtZXNwYWNlIGFuZCBpbWFnZSBwdWxsIHNl" + - "Y3JldAoKQ3JlYXRlIHRoZSBuYW1lc3BhY2UsIGFuZCBhbiBpbWFnZSBwdWxsIHNlY3JldCBvbmx5" + - "IGlmIHB1bGxpbmcgZnJvbSBhIHByaXZhdGUKcmVnaXN0cnk6CgpgYGBiYXNoCmt1YmVjdGwgY3Jl" + - "YXRlIG5hbWVzcGFjZSBudmNmLXVpIC0tZHJ5LXJ1bj1jbGllbnQgLW8geWFtbCB8IGt1YmVjdGwg" + - "YXBwbHkgLWYgLQoKIyBPbmx5IGlmIHB1bGxpbmcgZnJvbSBhIHByaXZhdGUgcmVnaXN0cnkgKGUu" + - "Zy4sIE5HQyBudmNyLmlvKQpleHBvcnQgTkdDX0FQSV9LRVk9Ijx5b3VyLWtleT4iCmt1YmVjdGwg" + - "Y3JlYXRlIHNlY3JldCBkb2NrZXItcmVnaXN0cnkgbnZjci1jcmVkcyBcCiAgLS1kb2NrZXItc2Vy" + - "dmVyPW52Y3IuaW8gXAogIC0tZG9ja2VyLXVzZXJuYW1lPSckb2F1dGh0b2tlbicgXAogIC0tZG9j" + - "a2VyLXBhc3N3b3JkPSIkTkdDX0FQSV9LRVkiIFwKICAtLW5hbWVzcGFjZT1udmNmLXVpIFwKICAt" + - "LWRyeS1ydW49Y2xpZW50IC1vIHlhbWwgfCBrdWJlY3RsIGFwcGx5IC1mIC0KYGBgCgpUaGUgZGVm" + - "YXVsdCByb3V0ZSBob3N0IGlzIGBudmNmLXVpLjxkb21haW4+YCBhbmQgdGhlIGJhY2tlbmQgaXMK" + - "YG52Y2YtdWkubnZjZi11aTo4MzAwYC4KCiMjIEFwcGx5IHRoZSBzZXJ2aWNlIGFuZCByb3V0ZQoK" + - "QWZ0ZXIgY29uZmlybWluZyB0aGUgcGFja2FnZSBpbmNsdWRlcyB0aGUgYG52Y2YtdWlgIHJlbGVh" + - "c2UsIHByZXZpZXcgYW5kIGFwcGx5CnRoZSBzZXJ2aWNlIGFuZCByb3V0ZToKCmBgYGJhc2gKSEVM" + - "TUZJTEVfRU5WPTxlbnYtbmFtZT4gaGVsbWZpbGUgLS1zZWxlY3RvciBuYW1lPW52Y2YtdWkgdGVt" + - "cGxhdGUKSEVMTUZJTEVfRU5WPTxlbnYtbmFtZT4gaGVsbWZpbGUgLS1zZWxlY3RvciBuYW1lPW52" + - "Y2YtdWkgc3luYwpIRUxNRklMRV9FTlY9PGVudi1uYW1lPiBoZWxtZmlsZSAtLXNlbGVjdG9yIHJl" + - "bGVhc2UtZ3JvdXA9aW5ncmVzcyBzeW5jCmBgYAoKIyMgVmVyaWZ5CgpWZXJpZnkgdGhlIHJvdXRl" + - "IG9ubHkgd2hlbiB0aGUgYWRkb24gaXMgcHJlc2VudCBhbmQgZW5hYmxlZDoKCmBgYGJhc2gKa3Vi" + - "ZWN0bCBnZXQgZGVwbG95LHN2YyAtbiBudmNmLXVpCmt1YmVjdGwgZ2V0IGh0dHByb3V0ZSAtQSB8" + - "IGdyZXAgLWkgbnZjZi11aQpjdXJsIC1pIC1IICJIb3N0OiBudmNmLXVpLjxkb21haW4+IiAiaHR0" + - "cDovLzxnYXRld2F5LWFkZHJlc3M+L3N0YXR1cyIKYGBgCgojIyBTZWN1cml0eQoKVGhlIE5WQ0Yg" + - "VUkgYWRtaW4gcGFuZWwgaXMgY3VycmVudGx5IHVuYXV0aGVudGljYXRlZC4gRG8gbm90IGV4cG9z" + - "ZSBpdCB0byB0aGUKcHVibGljIGludGVybmV0LiBSZXN0cmljdCBhY2Nlc3MgdG8gYSB0cnVzdGVk" + - "IG5ldHdvcmssIFZQTiwgb3IgYW4gYXV0aGVudGljYXRpbmcKcHJveHkgaW4gZnJvbnQgb2YgdGhl" + - "IGBudmNmLXVpYCByb3V0ZS4KUEsHCDj5J6+5BwAAuQcAAFBLAwQUAAgAAAAAACEAAAAAAAAAAAAA" + - "AAAAPgAJAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtaW5zdGFsbGF0aW9uL3JlZmVyZW5jZXMvcHVs" + - "bC1zZWNyZXRzLm1kVVQFAAEAps4SIyBJbWFnZSBQdWxsIFNlY3JldHMgUmVmZXJlbmNlCgojIyBP" + - "dmVydmlldwoKV2hlbiBwdWxsaW5nIE5WQ0YgaW1hZ2VzIGZyb20gYSBwcml2YXRlIHJlZ2lzdHJ5" + - "IChmb3IgZXhhbXBsZSwgTkdDCmBudmNyLmlvYCksIEt1YmVybmV0ZXMgbmVlZHMgYGltYWdlUHVs" + - "bFNlY3JldHNgIG9uIGV2ZXJ5IHBvZC4gQ29udHJvbC1wbGFuZSBhbmQKY29tcHV0ZS1wbGFuZSBj" + - "cmVkZW50aWFscyBhcmUgY29uZmlndXJlZCBpbiB0aGVpciBvd24gY2x1c3RlciBjb250ZXh0cy4g" + - "RG8gbm90CnVzZSBvbmUgbWl4ZWQgbmFtZXNwYWNlIGxpc3QuCgpUaGUgcmVjb21tZW5kZWQgY29u" + - "dHJvbC1wbGFuZSBhcHByb2FjaCB1c2VzIEt5dmVybm8gdG8gaW5qZWN0IHRoZSBzZWNyZXQgaW50" + - "bwpjb250cm9sLXBsYW5lIHBvZHMgYXQgYWRtaXNzaW9uIHRpbWUuIFRoZSBjb21wdXRlLXBsYW5l" + - "IGNoYXJ0IGFuZCBvcGVyYXRvciB1c2UKdGhlIHNlY3JldCBpbiBgbnZjYS1vcGVyYXRvcmAgYW5k" + - "IG1pcnJvciBpdCB0byBgbnZjYS1zeXN0ZW1gLgoKIyMgS3l2ZXJubyBBcHByb2FjaCAoUmVjb21t" + - "ZW5kZWQpCgojIyMgMS4gSW5zdGFsbCBLeXZlcm5vCgpgYGBiYXNoCmhlbG0gcmVwbyBhZGQga3l2" + - "ZXJubyBodHRwczovL2t5dmVybm8uZ2l0aHViLmlvL2t5dmVybm8vCmhlbG0gcmVwbyB1cGRhdGUK" + - "aGVsbSBpbnN0YWxsIGt5dmVybm8ga3l2ZXJuby9reXZlcm5vIC1uIGt5dmVybm8gLS1jcmVhdGUt" + - "bmFtZXNwYWNlIFwKICAtLWt1YmUtY29udGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0PgpgYGAK" + - "CiMjIyAyLiBDcmVhdGUgY29udHJvbC1wbGFuZSBwdWxsIHNlY3JldHMKCmBgYGJhc2gKZXhwb3J0" + - "IE5HQ19BUElfS0VZPSI8eW91ci1uZ2MtYXBpLWtleT4iCgpmb3IgbnMgaW4gY2Fzc2FuZHJhLXN5" + - "c3RlbSBuYXRzLXN5c3RlbSBudmNmIGFwaS1rZXlzIGVzcyBzaXMgdmF1bHQtc3lzdGVtIGNlcnQt" + - "bWFuYWdlcjsgZG8KICBrdWJlY3RsIC0tY29udGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0PiBj" + - "cmVhdGUgbmFtZXNwYWNlICIkbnMiIFwKICAgIC0tZHJ5LXJ1bj1jbGllbnQgLW8geWFtbCB8IGt1" + - "YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KZG9uZQoK" + - "Zm9yIG5zIGluIGNhc3NhbmRyYS1zeXN0ZW0gbmF0cy1zeXN0ZW0gbnZjZiBhcGkta2V5cyBlc3Mg" + - "c2lzIHZhdWx0LXN5c3RlbSBjZXJ0LW1hbmFnZXI7IGRvCiAga3ViZWN0bCAtLWNvbnRleHQgPGNv" + - "bnRyb2wtcGxhbmUtY29udGV4dD4gY3JlYXRlIHNlY3JldCBkb2NrZXItcmVnaXN0cnkgbnZjci1j" + - "cmVkcyBcCiAgICAtLWRvY2tlci1zZXJ2ZXI9bnZjci5pbyBcCiAgICAtLWRvY2tlci11c2VybmFt" + - "ZT0nJG9hdXRodG9rZW4nIFwKICAgIC0tZG9ja2VyLXBhc3N3b3JkPSIkTkdDX0FQSV9LRVkiIFwK" + - "ICAgIC0tbmFtZXNwYWNlPSIkbnMiIFwKICAgIC0tZHJ5LXJ1bj1jbGllbnQgLW8geWFtbCB8IGt1" + - "YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KZG9uZQpg" + - "YGAKCkZvciBub24tTkdDIHJlZ2lzdHJpZXMsIHJlcGxhY2UgYC0tZG9ja2VyLXNlcnZlcmAsIGAt" + - "LWRvY2tlci11c2VybmFtZWAsIGFuZCBgLS1kb2NrZXItcGFzc3dvcmRgLgoKIyMjIDMuIEFwcGx5" + - "IEt5dmVybm8gQ2x1c3RlclBvbGljeQoKYGBgeWFtbAphcGlWZXJzaW9uOiBreXZlcm5vLmlvL3Yx" + - "CmtpbmQ6IENsdXN0ZXJQb2xpY3kKbWV0YWRhdGE6CiAgbmFtZTogbnZjZi1hZGQtaW1hZ2VwdWxs" + - "c2VjcmV0cwpzcGVjOgogIGJhY2tncm91bmQ6IGZhbHNlCiAgcnVsZXM6CiAgICAtIG5hbWU6IGFk" + - "ZC1pbWFnZXB1bGxzZWNyZXQtdG8tbnZjZi1wb2RzCiAgICAgIG1hdGNoOgogICAgICAgIGFueToK" + - "ICAgICAgICAtIHJlc291cmNlczoKICAgICAgICAgICAga2luZHM6CiAgICAgICAgICAgIC0gUG9k" + - "CiAgICAgICAgICAgIG5hbWVzcGFjZXM6CiAgICAgICAgICAgIC0gIm52Y2YiCiAgICAgICAgICAg" + - "IC0gImFwaS1rZXlzIgogICAgICAgICAgICAtICJzaXMiCiAgICAgICAgICAgIC0gImVzcyIKICAg" + - "ICAgICAgICAgLSAibmF0cy1zeXN0ZW0iCiAgICAgICAgICAgIC0gImNhc3NhbmRyYS1zeXN0ZW0i" + - "CiAgICAgICAgICAgIC0gInZhdWx0LXN5c3RlbSIKICAgICAgICAgICAgLSAiY2VydC1tYW5hZ2Vy" + - "IgogICAgICBtdXRhdGU6CiAgICAgICAgcGF0Y2hTdHJhdGVnaWNNZXJnZToKICAgICAgICAgIG1l" + - "dGFkYXRhOgogICAgICAgICAgICBhbm5vdGF0aW9uczoKICAgICAgICAgICAgICBudmNmLm52aWRp" + - "YS5jb20vaW1hZ2VwdWxsc2VjcmV0LWluamVjdGVkLWJ5OiBreXZlcm5vCiAgICAgICAgICBzcGVj" + - "OgogICAgICAgICAgICBpbWFnZVB1bGxTZWNyZXRzOgogICAgICAgICAgICAtIG5hbWU6IG52Y3It" + - "Y3JlZHMKYGBgCgpgYGBiYXNoCmt1YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRl" + - "eHQ+IGFwcGx5IFwKICAtZiBreXZlcm5vLWltYWdlcHVsbHNlY3JldC1wb2xpY3kueWFtbApgYGAK" + - "CiMjIyA0LiBJbnN0YWxsIHRoZSBjb250cm9sIHBsYW5lCgpDb250aW51ZSB3aXRoIHRoZSBzZWxl" + - "Y3RlZCBjb250cm9sLXBsYW5lIGluc3RhbGxhdGlvbiBpbgpbU3BsaXQgQ29tcHV0ZS1QbGFuZSBJ" + - "bnN0YWxsYXRpb25dKGNvbXB1dGUtcGxhbmUtaW5zdGFsbGF0aW9uLm1kKS4gTm8KY29udHJvbC1w" + - "bGFuZSBoZWxtZmlsZSBtb2RpZmljYXRpb25zIGFyZSBuZWVkZWQuIEt5dmVybm8gaW5qZWN0cyB0" + - "aGUgcHVsbApzZWNyZXQgaW50byBuZXcgY29udHJvbC1wbGFuZSBwb2RzLgoKIyMgQ29tcHV0ZS1Q" + - "bGFuZSBTZWNyZXQKCkNyZWF0ZSB0aGUgc2VjcmV0IG9ubHkgaW4gYG52Y2Etb3BlcmF0b3JgIG9u" + - "IHRoZSBjb21wdXRlIGNsdXN0ZXI6CgpgYGBiYXNoCmt1YmVjdGwgLS1jb250ZXh0IDxjb21wdXRl" + - "LXBsYW5lLWNvbnRleHQ+IGNyZWF0ZSBuYW1lc3BhY2UgbnZjYS1vcGVyYXRvciBcCiAgLS1kcnkt" + - "cnVuPWNsaWVudCAtbyB5YW1sIHwgXAogIGt1YmVjdGwgLS1jb250ZXh0IDxjb21wdXRlLXBsYW5l" + - "LWNvbnRleHQ+IGFwcGx5IC1mIC0KCmt1YmVjdGwgLS1jb250ZXh0IDxjb21wdXRlLXBsYW5lLWNv" + - "bnRleHQ+IGNyZWF0ZSBzZWNyZXQgZG9ja2VyLXJlZ2lzdHJ5IG52Y3ItY3JlZHMgXAogIC0tZG9j" + - "a2VyLXNlcnZlcj1udmNyLmlvIFwKICAtLWRvY2tlci11c2VybmFtZT0nJG9hdXRodG9rZW4nIFwK" + - "ICAtLWRvY2tlci1wYXNzd29yZD0iJE5HQ19BUElfS0VZIiBcCiAgLS1uYW1lc3BhY2U9bnZjYS1v" + - "cGVyYXRvciBcCiAgLS1kcnktcnVuPWNsaWVudCAtbyB5YW1sIHwgXAogIGt1YmVjdGwgLS1jb250" + - "ZXh0IDxjb21wdXRlLXBsYW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KYGBgCgpSZWZlcmVuY2UgaXQg" + - "aW4gdGhlIGNvbXB1dGUtcGxhbmUgZW52aXJvbm1lbnQgZmlsZToKCmBgYHlhbWwKZ2xvYmFsOgog" + - "IGltYWdlUHVsbFNlY3JldHM6CiAgICAtIG5hbWU6IG52Y3ItY3JlZHMKYGBgCgpUaGUgY2hhcnQg" + - "YW5kIG9wZXJhdG9yIHVzZSB0aGF0IHJlZmVyZW5jZSBpbiBgbnZjYS1vcGVyYXRvcmAgYW5kIG1p" + - "cnJvciB0aGUKc2VjcmV0IHRvIGBudmNhLXN5c3RlbWAuIERvIG5vdCBjcmVhdGUgYSBicm9hZGVy" + - "IG1hbnVhbCBjb21wdXRlIG5hbWVzcGFjZSBsaXN0LiBGb2xsb3cKW1NwbGl0IENvbXB1dGUtUGxh" + - "bmUgSW5zdGFsbGF0aW9uXShjb21wdXRlLXBsYW5lLWluc3RhbGxhdGlvbi5tZCkgZm9yIHRoZQpz" + - "ZWxlY3RlZCBpbnN0YWxsIGZsb3cuCgojIyBWZXJpZmljYXRpb24KCmBgYGJhc2gKIyBDaGVjayB0" + - "aGF0IGEgY29udHJvbC1wbGFuZSBwb2QgaGFzIHRoZSBpbmplY3RlZCBzZWNyZXQKa3ViZWN0bCAt" + - "LWNvbnRleHQgPGNvbnRyb2wtcGxhbmUtY29udGV4dD4gZ2V0IHBvZCAtbiA8bmFtZXNwYWNlPiA8" + - "cG9kLW5hbWU+IFwKICAtbyBqc29ucGF0aD0ney5zcGVjLmltYWdlUHVsbFNlY3JldHN9JwojIEV4" + - "cGVjdGVkOiBbeyJuYW1lIjoibnZjci1jcmVkcyJ9XQoKIyBDaGVjayB0aGUgS3l2ZXJubyBhbm5v" + - "dGF0aW9uCmt1YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRleHQ+IGdldCBwb2Qg" + - "LW4gPG5hbWVzcGFjZT4gPHBvZC1uYW1lPiBcCiAgLW8ganNvbnBhdGg9J3subWV0YWRhdGEuYW5u" + - "b3RhdGlvbnMubnZjZlwubnZpZGlhXC5jb20vaW1hZ2VwdWxsc2VjcmV0LWluamVjdGVkLWJ5fScK" + - "IyBFeHBlY3RlZDoga3l2ZXJubwoKIyBDaGVjayBwdWxsIGV2ZW50cwprdWJlY3RsIC0tY29udGV4" + - "dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0PiBnZXQgZXZlbnRzIC1uIDxuYW1lc3BhY2U+IFwKICAt" + - "LXNvcnQtYnk9Jy5sYXN0VGltZXN0YW1wJyB8IGdyZXAgLWkgcHVsbAojIFNob3VsZCBzaG93ICJT" + - "dWNjZXNzZnVsbHkgcHVsbGVkIiBub3QgIjQwMSBVbmF1dGhvcml6ZWQiCgojIENoZWNrIHRoZSBj" + - "aGFydC9vcGVyYXRvciBtaXJyb3Igb24gdGhlIGNvbXB1dGUgY2x1c3RlcgprdWJlY3RsIC0tY29u" + - "dGV4dCA8Y29tcHV0ZS1wbGFuZS1jb250ZXh0PiBnZXQgc2VjcmV0IG52Y3ItY3JlZHMgXAogIC1u" + - "IG52Y2Etc3lzdGVtCmBgYAoKIyMgV2hlbiBQdWxsIFNlY3JldHMgQXJlIE5vdCBOZWVkZWQKCi0g" + - "KipBV1MgRUNSIHdpdGggSUFNIG5vZGUgcm9sZXMqKjogSWYgeW91ciBub2RlcyBoYXZlIGBBbWF6" + - "b25FQzJDb250YWluZXJSZWdpc3RyeVJlYWRPbmx5YCBJQU0gcG9saWN5LCBLdWJlcm5ldGVzIGNh" + - "biBwdWxsIGZyb20gRUNSIHdpdGhvdXQgZXhwbGljaXQgc2VjcmV0cy4KLSAqKlB1YmxpYyByZWdp" + - "c3RyaWVzKio6IE5vIHB1bGwgc2VjcmV0cyBuZWVkZWQuCi0gKipDU1AgYnVpbHQtaW4gY3JlZGVu" + - "dGlhbCBoZWxwZXJzKio6IEdLRSBBcnRpZmFjdCBSZWdpc3RyeSwgQXp1cmUgQUNSIHdpdGggbWFu" + - "YWdlZCBpZGVudGl0eSwgYW5kIHNpbWlsYXIgc2VydmljZXMuCgojIyBUcm91Ymxlc2hvb3RpbmcK" + - "CiMjIyBQb2RzIHN0aWxsIGluIEltYWdlUHVsbEJhY2tPZmYgYWZ0ZXIgYXBwbHlpbmcgcG9saWN5" + - "CgpUaGUgS3l2ZXJubyBwb2xpY3kgb25seSBhZmZlY3RzIHBvZHMgY3JlYXRlZCAqKmFmdGVyKiog" + - "dGhlIHBvbGljeSBpcyBhcHBsaWVkLiBEZWxldGUgc3R1Y2sgcG9kcyB0byB0cmlnZ2VyIHJlY3Jl" + - "YXRpb246CgpgYGBiYXNoCmt1YmVjdGwgLS1jb250ZXh0IDx0YXJnZXQtY29udGV4dD4gZGVsZXRl" + - "IHBvZHMgLW4gPG5hbWVzcGFjZT4gLS1hbGwKYGBgCgojIyMgS3l2ZXJubyBhZG1pc3Npb24gY29u" + - "dHJvbGxlciBub3QgcnVubmluZwoKYGBgYmFzaAprdWJlY3RsIC0tY29udGV4dCA8Y29udHJvbC1w" + - "bGFuZS1jb250ZXh0PiBnZXQgcG9kcyAtbiBreXZlcm5vCiMgQWxsIHBvZHMgc2hvdWxkIGJlIFJ1" + - "bm5pbmcKYGBgCgojIyMgUG9saWN5IG5vdCBtYXRjaGluZwoKYGBgYmFzaAprdWJlY3RsIC0tY29u" + - "dGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0PiBnZXQgY2x1c3RlcnBvbGljeSBudmNmLWFkZC1p" + - "bWFnZXB1bGxzZWNyZXRzCiMgU2hvdWxkIHNob3cgUkVBRFk6IFRydWUKYGBgCgojIyMgV3Jvbmcg" + - "c2VjcmV0IG5hbWUKClRoZSBwb2xpY3kgcmVmZXJlbmNlcyBgbnZjci1jcmVkc2AuIFZlcmlmeSB0" + - "aGUgc2VjcmV0IGV4aXN0cyB3aXRoIHRoYXQgZXhhY3QgbmFtZToKCmBgYGJhc2gKa3ViZWN0bCAt" + - "LWNvbnRleHQgPHRhcmdldC1jb250ZXh0PiBnZXQgc2VjcmV0IG52Y3ItY3JlZHMgLW4gPG5hbWVz" + - "cGFjZT4KYGBgClBLBwhLQjWoABYAAAAWAABQSwECFAAUAAgAAAAAACEA0GJVtkkQAABJEAAAEgAJ" + - "AAAAAAAAAAAAAAAAAAAAZGF0YS9tYW5pZmVzdC5qc29uVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAh" + - "AM2BMORNYQAATWEAACMACQAAAAAAAAAAAAAAkhAAAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtY2xp" + - "L1NLSUxMLm1kVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhACHGyIbwCgAA8AoAADMACQAAAAAAAAAA" + - "AAAAOXIAAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtY2xpL2V4YW1wbGVzL2NpLXBpcGVsaW5lcy5t" + - "ZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQChFOOBXQwAAF0MAAA0AAkAAAAAAAAAAAAAAJN9AABk" + - "YXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWNsaS9leGFtcGxlcy9tdWx0aS1jbHVzdGVyLm1kVVQFAAEA" + - "ps4SUEsBAhQAFAAIAAAAAAAhAEInSQ8FDQAABQ0AADcACQAAAAAAAAAAAAAAW4oAAGRhdGEvbnZj" + - "Zi1zZWxmLW1hbmFnZWQtY2xpL3Byb21wdHMvYWRkLWNvbXB1dGUtcGxhbmUubWRVVAUAAQCmzhJQ" + - "SwECFAAUAAgAAAAAACEAkdwb+ZgZAACYGQAAOQAJAAAAAAAAAAAAAADOlwAAZGF0YS9udmNmLXNl" + - "bGYtbWFuYWdlZC1jbGkvcHJvbXB0cy9jcmVhdGUtYW5kLXJ1bi10YXNrLm1kVVQFAAEAps4SUEsB" + - "AhQAFAAIAAAAAAAhANJD3AQYJQAAGCUAADcACQAAAAAAAAAAAAAA1rEAAGRhdGEvbnZjZi1zZWxm" + - "LW1hbmFnZWQtY2xpL3Byb21wdHMvZGVwbG95LWFuZC1pbnZva2UubWRVVAUAAQCmzhJQSwECFAAU" + - "AAgAAAAAACEADpw/l70OAAC9DgAAPQAJAAAAAAAAAAAAAABc1wAAZGF0YS9udmNmLXNlbGYtbWFu" + - "YWdlZC1jbGkvcHJvbXB0cy9kaWFnbm9zZS1mYWlsZWQtaW5zdGFsbC5tZFVUBQABAKbOElBLAQIU" + - "ABQACAAAAAAAIQBCceYeASgAAAEoAAA6AAkAAAAAAAAAAAAAAI3mAABkYXRhL252Y2Ytc2VsZi1t" + - "YW5hZ2VkLWNsaS9wcm9tcHRzL2luc3RhbGwtZnJvbS1zY3JhdGNoLm1kVVQFAAEAps4SUEsBAhQA" + - "FAAIAAAAAAAhABrELJ7HBwAAxwcAADkACQAAAAAAAAAAAAAA/w4BAGRhdGEvbnZjZi1zZWxmLW1h" + - "bmFnZWQtY2xpL3Byb21wdHMvcm90YXRlLWNsdXN0ZXItandrcy5tZFVUBQABAKbOElBLAQIUABQA" + - "CAAAAAAAIQASkzNZ4CEAAOAhAAAuAAkAAAAAAAAAAAAAADYXAQBkYXRhL252Y2Ytc2VsZi1tYW5h" + - "Z2VkLWNsaS9wcm9tcHRzL3RlYXJkb3duLm1kVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhAHIND2Qh" + - "IwAAISMAADAACQAAAAAAAAAAAAAAezkBAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtY2xpL3JlZmVy" + - "ZW5jZS9jb21tYW5kcy5tZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQBzp1pk1QYAANUGAAAyAAkA" + - "AAAAAAAAAAAAAANdAQBkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWNsaS9yZWZlcmVuY2UvZXhpdC1j" + - "b2Rlcy5tZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQD8o5HgxiIAAMYiAAAtAAkAAAAAAAAAAAAA" + - "AEFkAQBkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWNsaS9yZWZlcmVuY2UvZmxhZ3MubWRVVAUAAQCm" + - "zhJQSwECFAAUAAgAAAAAACEA864ThHQRAAB0EQAANwAJAAAAAAAAAAAAAABrhwEAZGF0YS9udmNm" + - "LXNlbGYtbWFuYWdlZC1jbGkvcmVmZXJlbmNlL3Ryb3VibGVzaG9vdGluZy5tZFVUBQABAKbOElBL" + - "AQIUABQACAAAAAAAIQAv2rl1QmIAAEJiAAAsAAkAAAAAAAAAAAAAAE2ZAQBkYXRhL252Y2Ytc2Vs" + - "Zi1tYW5hZ2VkLWluc3RhbGxhdGlvbi9TS0lMTC5tZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQAr" + - "E7g/rVUAAK1VAAA0AAkAAAAAAAAAAAAAAPL7AQBkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWluc3Rh" + - "bGxhdGlvbi9ldmFscy9ldmFscy5qc29uVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhAAJSTkB/KAAA" + - "fygAAC8ACQAAAAAAAAAAAAAAClICAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtaW5zdGFsbGF0aW9u" + - "L2V4YW1wbGVzLm1kVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhAJZjvFkSMAAAEjAAAEwACQAAAAAA" + - "AAAAAAAA73oCAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtaW5zdGFsbGF0aW9uL3JlZmVyZW5jZXMv" + - "Y29tcHV0ZS1wbGFuZS1pbnN0YWxsYXRpb24ubWRVVAUAAQCmzhJQSwECFAAUAAgAAAAAACEAVj5n" + - "kJUwAACVMAAAOwAJAAAAAAAAAAAAAACEqwIAZGF0YS9udmNmLXNlbGYtbWFuYWdlZC1pbnN0YWxs" + - "YXRpb24vcmVmZXJlbmNlcy9kZWJ1Z2dpbmcubWRVVAUAAQCmzhJQSwECFAAUAAgAAAAAACEAwFYg" + - "548nAACPJwAARAAJAAAAAAAAAAAAAACL3AIAZGF0YS9udmNmLXNlbGYtbWFuYWdlZC1pbnN0YWxs" + - "YXRpb24vcmVmZXJlbmNlcy9oZWxtZmlsZS1zdHJ1Y3R1cmUubWRVVAUAAQCmzhJQSwECFAAUAAgA" + - "AAAAACEAOPknr7kHAAC5BwAAPwAJAAAAAAAAAAAAAACVBAMAZGF0YS9udmNmLXNlbGYtbWFuYWdl" + - "ZC1pbnN0YWxsYXRpb24vcmVmZXJlbmNlcy9udmNmLXVpLWFkZG9uLm1kVVQFAAEAps4SUEsBAhQA" + - "FAAIAAAAAAAhAEtCNagAFgAAABYAAD4ACQAAAAAAAAAAAAAAxAwDAGRhdGEvbnZjZi1zZWxmLW1h" + - "bmFnZWQtaW5zdGFsbGF0aW9uL3JlZmVyZW5jZXMvcHVsbC1zZWNyZXRzLm1kVVQFAAEAps4SUEsF" + - "BgAAAAAXABcArwkAADkjAwAAAA==" + "eHBsaWNpdGx5OgoKYGBgeWFtbAphcGk6CiAgcmVtb3RlQ29uZmlnOgogICAgY29uZmlnRGF0YToK" + + "ICAgICAgbnZjZjoKICAgICAgICBzaWRlY2FyczoKICAgICAgICAgIGxsbS1yb3V0ZXItY2xpZW50" + + "LWltYWdlOiA8cmVnaXN0cnk+LzxyZXBvc2l0b3J5Pi9weWxvbjowLjE0LjEKYGBgCgpUaGUgbGVn" + + "YWN5IGBhcGkuZW52Lk5WQ0ZfU0lERUNBUlNfTExNX1JPVVRFUl9DTElFTlRfSU1BR0VgIHBhdGgg" + + "aXMgZGVwcmVjYXRlZC4KVGhlIHN0YWNrIHRyYW5zbGF0ZXMgaXQgZm9yIG9uZSBjb21wYXRpYmls" + + "aXR5IHdpbmRvdywgYnV0IG5ldyBjb25maWd1cmF0aW9ucwptdXN0IHVzZSB0aGUgcmVtb3RlLWNv" + + "bmZpZyBwYXRoLiBDb25mbGljdGluZyB2YWx1ZXMgZmFpbCByZW5kZXJpbmcuCgpSZW5kZXIgYW5k" + + "IGFwcGx5IHRoZSB1cGRhdGVkIGNvbnRyb2wtcGxhbmUgZW52aXJvbm1lbnQsIHRoZW4gcmVmcmVz" + + "aCBldmVyeQpyZWdpc3RlcmVkIGNvbXB1dGUgcGxhbmUgdXNpbmcgdGhlIHNhbWUgaGFuZG9mZiB1" + + "c2VkIGZvciBpdHMgaW5zdGFsbGF0aW9uIHNvCk5WQ0EgcmVjZWl2ZXMgYGFnZW50Q29uZmlnLm1l" + + "cmdlQ29uZmlnYDoKCmBgYGJhc2gKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIHRlbXBsYXRl" + + "CkhFTE1GSUxFX0VOVj08ZW52PiBoZWxtZmlsZSBzeW5jCmBgYAoKVXNlIHRoZSBjb21wbGV0ZSBj" + + "b21wdXRlLXBsYW5lIGluc3RhbGwgY29tbWFuZCBmcm9tCltTcGxpdCBDb21wdXRlLVBsYW5lIElu" + + "c3RhbGxhdGlvbl0oY29tcHV0ZS1wbGFuZS1pbnN0YWxsYXRpb24ubWQpLiBBCkNMSS1wcm9maWxl" + + "IGluc3RhbGxhdGlvbiBtdXN0IHJlbWFpbiBwcm9maWxlLWRyaXZlbjsgYSBIZWxtZmlsZSBpbnN0" + + "YWxsYXRpb24KbXVzdCByZW1haW4gdmFsdWVzLWRyaXZlbi4KCkV4aXN0aW5nIExMTSBmdW5jdGlv" + + "biB2ZXJzaW9ucyByZXRhaW4gdGhlIHdvcmtlci1zaWRlY2FyIGltYWdlIG1ldGFkYXRhCmNhcHR1" + + "cmVkIHdoZW4gdGhlIHZlcnNpb24gaXMgY3JlYXRlZC4gUmVwbGFjaW5nIHBvZHMgb3IgcmVkZXBs" + + "b3lpbmcgdGhlIHNhbWUKdmVyc2lvbiBkb2VzIG5vdCBhcHBseSBhIG5ldyBQeWxvbiBpbWFnZS4g" + + "QWZ0ZXIgdGhlIGNvbnRyb2wtcGxhbmUgdXBkYXRlLApjcmVhdGUgYW5kIGRlcGxveSBhIG5ldyBm" + + "dW5jdGlvbiB2ZXJzaW9uLiBWZXJpZnkgdGhlIGNvbnRyb2wgcGxhbmUsIHJvdXRlLCBhbmQKd29y" + + "a2VyIHNpZGVjYXI6CgpgYGBiYXNoCmt1YmVjdGwgZ2V0IGRlcGxveSAtbiBudmNmIGxsbS1hcGkt" + + "Z2F0ZXdheQprdWJlY3RsIGdldCBzdGF0ZWZ1bHNldCAtbiBudmNmIGxsbS1yZXF1ZXN0LXJvdXRl" + + "cgprdWJlY3RsIGdldCBwb2RzIC1uIG52Y2YgfCBncmVwIC1FICdsbG0tYXBpLWdhdGV3YXl8bGxt" + + "LXJlcXVlc3Qtcm91dGVyJwprdWJlY3RsIGdldCBodHRwcm91dGUgLUEgfCBncmVwIGxsbQprdWJl" + + "Y3RsIC1uIG52Y2YtYmFja2VuZCBnZXQgcG9kIDxmdW5jdGlvbi1wb2Q+IFwKICAtbyBqc29ucGF0" + + "aD0ne3JhbmdlIC5zcGVjLmNvbnRhaW5lcnNbPyhALm5hbWU9PSJsbG0td29ya2VyIildLmFyZ3Nb" + + "Kl19ey59eyJcXG4ifXtlbmR9JwpgYGAKCkZvciBsb2NhbCBwbGFpbnRleHQgY2x1c3RlcnMsIHRo" + + "ZSBgbGxtLXdvcmtlcmAgYXJncyBtdXN0IGluY2x1ZGUKYC0tcXVpYy1pbnNlY3VyZWAuCgpGb3Ig" + + "bG9hZC1iYWxhbmNlciBzY2hlbWEgYW5kIGFsZ29yaXRobSBiZWhhdmlvciwgc2VlIHRoZQpbU3Rh" + + "cmdhdGUgY29uZmlndXJhdGlvbiBndWlkZV0oaHR0cHM6Ly9naXRodWIuY29tL05WSURJQS9udmNm" + + "L2Jsb2IvbWFpbi9zcmMvbGlicmFyaWVzL3J1c3Qvc3RhcmdhdGUvZG9jcy9sb2FkLWJhbGFuY2Vy" + + "LWNvbmZpZ3VyYXRpb24ubWQpLgoKIyMgSGVsbWZpbGUgU2VsZWN0b3JzCgpUYXJnZXQgc3BlY2lm" + + "aWMgcmVsZWFzZXMgb3IgZ3JvdXBzOgoKYGBgYmFzaAojIEJ5IHJlbGVhc2UgZ3JvdXAKSEVMTUZJ" + + "TEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgcmVsZWFzZS1ncm91cD1kZXBlbmRlbmNp" + + "ZXMgc3luYwpIRUxNRklMRV9FTlY9PGVudj4gaGVsbWZpbGUgLS1zZWxlY3RvciByZWxlYXNlLWdy" + + "b3VwPXNlcnZpY2VzIHN5bmMKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3Ig" + + "cmVsZWFzZS1ncm91cD1pbmdyZXNzIHN5bmMKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0t" + + "c2VsZWN0b3IgcmVsZWFzZS1ncm91cD1vYnNlcnZhYmlsaXR5IHN5bmMKCiMgQnkgcmVsZWFzZSBu" + + "YW1lCkhFTE1GSUxFX0VOVj08ZW52PiBoZWxtZmlsZSAtLXNlbGVjdG9yIG5hbWU9Y2Fzc2FuZHJh" + + "IHN5bmMKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgbmFtZT1hZG1pbi1p" + + "c3N1ZXItcHJveHkgc3luYwpIRUxNRklMRV9FTlY9PGVudj4gaGVsbWZpbGUgLS1zZWxlY3RvciBu" + + "YW1lPWxsbS1yZXF1ZXN0LXJvdXRlciBzeW5jCkhFTE1GSUxFX0VOVj08ZW52PiBoZWxtZmlsZSAt" + + "LXNlbGVjdG9yIG5hbWU9bGxtLWFwaS1nYXRld2F5IHN5bmMKCiMgVGVtcGxhdGUgb25seSAoZHJ5" + + "IHJ1bikKSEVMTUZJTEVfRU5WPTxlbnY+IGhlbG1maWxlIC0tc2VsZWN0b3IgbmFtZT1jYXNzYW5k" + + "cmEgdGVtcGxhdGUKCiMgRGVzdHJveSBhIHNpbmdsZSByZWxlYXNlCkhFTE1GSUxFX0VOVj08ZW52" + + "PiBoZWxtZmlsZSAtLXNlbGVjdG9yIG5hbWU9Y2Fzc2FuZHJhIGRlc3Ryb3kKYGBgCgpOb3RlOiBg" + + "YWRtaW4taXNzdWVyLXByb3h5YCBoYXMgbm8gYHJlbGVhc2UtZ3JvdXBgIGxhYmVsLiBVc2UgYC0t" + + "c2VsZWN0b3IgbmFtZT1hZG1pbi1pc3N1ZXItcHJveHlgLgpQSwcIle7q7yEpAAAhKQAAUEsDBBQA" + + "CAAAAAAAIQAAAAAAAAAAAAAAAAA/AAkAZGF0YS9udmNmLXNlbGYtbWFuYWdlZC1pbnN0YWxsYXRp" + + "b24vcmVmZXJlbmNlcy9udmNmLXVpLWFkZG9uLm1kVVQFAAEAps4SIyBOVkNGIFVJIEFkZG9uCgpO" + + "VkNGIFVJIGlzIGFuIG9wdGlvbmFsLCBjdXN0b21lci1mYWNpbmcgTlZDRiBhZG1pbi1wYW5lbCBV" + + "SS4gSXQgaXMgZGlzYWJsZWQgYnkKZGVmYXVsdCBhbmQgYXZhaWxhYmxlIG9ubHkgaW4gc3RhY2sg" + + "cGFja2FnZXMgdGhhdCBpbmNsdWRlIHRoZSBOVkNGIFVJIGFkZG9uLgoKIyMgV2hlbiB0byBlbmFi" + + "bGUKCkVuYWJsZSBOVkNGIFVJIG9ubHkgd2hlbiB5b3UgbmVlZCB0aGUgYWRtaW4tcGFuZWwgVUkg" + + "YW5kIHlvdXIgZXh0cmFjdGVkIHN0YWNrCnBhY2thZ2UgaW5jbHVkZXMgdGhlIGFkZG9uLiBJZiB0" + + "aGUgcGFja2FnZSBkb2VzIG5vdCBjb250YWluIGEgYG52Y2YtdWlgIHJlbGVhc2UKYW5kIGBudmNm" + + "VWlgIHJvdXRlIHZhbHVlcywgc2tpcCB0aGUgYWRkb24gdW50aWwgeW91IHVzZSBhIHBhY2thZ2Ug" + + "dGhhdCBpbmNsdWRlcwp0aGVtLgoKIyMgRW5hYmxlIHRoZSBhZGRvbgoKRW5hYmxlIGl0IGluIGBl" + + "bnZpcm9ubWVudHMvPGVudi1uYW1lPi55YW1sYDoKCmBgYHlhbWwKYWRkb25zOgogIG52Y2ZVaToK" + + "ICAgIGVuYWJsZWQ6IHRydWUKYGBgCgojIyBDcmVhdGUgbmFtZXNwYWNlIGFuZCBpbWFnZSBwdWxs" + + "IHNlY3JldAoKQ3JlYXRlIHRoZSBuYW1lc3BhY2UsIGFuZCBhbiBpbWFnZSBwdWxsIHNlY3JldCBv" + + "bmx5IGlmIHB1bGxpbmcgZnJvbSBhIHByaXZhdGUKcmVnaXN0cnk6CgpgYGBiYXNoCmt1YmVjdGwg" + + "Y3JlYXRlIG5hbWVzcGFjZSBudmNmLXVpIC0tZHJ5LXJ1bj1jbGllbnQgLW8geWFtbCB8IGt1YmVj" + + "dGwgYXBwbHkgLWYgLQoKIyBPbmx5IGlmIHB1bGxpbmcgZnJvbSBhIHByaXZhdGUgcmVnaXN0cnkg" + + "KGUuZy4sIE5HQyBudmNyLmlvKQpleHBvcnQgTkdDX0FQSV9LRVk9Ijx5b3VyLWtleT4iCmt1YmVj" + + "dGwgY3JlYXRlIHNlY3JldCBkb2NrZXItcmVnaXN0cnkgbnZjci1jcmVkcyBcCiAgLS1kb2NrZXIt" + + "c2VydmVyPW52Y3IuaW8gXAogIC0tZG9ja2VyLXVzZXJuYW1lPSckb2F1dGh0b2tlbicgXAogIC0t" + + "ZG9ja2VyLXBhc3N3b3JkPSIkTkdDX0FQSV9LRVkiIFwKICAtLW5hbWVzcGFjZT1udmNmLXVpIFwK" + + "ICAtLWRyeS1ydW49Y2xpZW50IC1vIHlhbWwgfCBrdWJlY3RsIGFwcGx5IC1mIC0KYGBgCgpUaGUg" + + "ZGVmYXVsdCByb3V0ZSBob3N0IGlzIGBudmNmLXVpLjxkb21haW4+YCBhbmQgdGhlIGJhY2tlbmQg" + + "aXMKYG52Y2YtdWkubnZjZi11aTo4MzAwYC4KCiMjIEFwcGx5IHRoZSBzZXJ2aWNlIGFuZCByb3V0" + + "ZQoKQWZ0ZXIgY29uZmlybWluZyB0aGUgcGFja2FnZSBpbmNsdWRlcyB0aGUgYG52Y2YtdWlgIHJl" + + "bGVhc2UsIHByZXZpZXcgYW5kIGFwcGx5CnRoZSBzZXJ2aWNlIGFuZCByb3V0ZToKCmBgYGJhc2gK" + + "SEVMTUZJTEVfRU5WPTxlbnYtbmFtZT4gaGVsbWZpbGUgLS1zZWxlY3RvciBuYW1lPW52Y2YtdWkg" + + "dGVtcGxhdGUKSEVMTUZJTEVfRU5WPTxlbnYtbmFtZT4gaGVsbWZpbGUgLS1zZWxlY3RvciBuYW1l" + + "PW52Y2YtdWkgc3luYwpIRUxNRklMRV9FTlY9PGVudi1uYW1lPiBoZWxtZmlsZSAtLXNlbGVjdG9y" + + "IHJlbGVhc2UtZ3JvdXA9aW5ncmVzcyBzeW5jCmBgYAoKIyMgVmVyaWZ5CgpWZXJpZnkgdGhlIHJv" + + "dXRlIG9ubHkgd2hlbiB0aGUgYWRkb24gaXMgcHJlc2VudCBhbmQgZW5hYmxlZDoKCmBgYGJhc2gK" + + "a3ViZWN0bCBnZXQgZGVwbG95LHN2YyAtbiBudmNmLXVpCmt1YmVjdGwgZ2V0IGh0dHByb3V0ZSAt" + + "QSB8IGdyZXAgLWkgbnZjZi11aQpjdXJsIC1pIC1IICJIb3N0OiBudmNmLXVpLjxkb21haW4+IiAi" + + "aHR0cDovLzxnYXRld2F5LWFkZHJlc3M+L3N0YXR1cyIKYGBgCgojIyBTZWN1cml0eQoKVGhlIE5W" + + "Q0YgVUkgYWRtaW4gcGFuZWwgaXMgY3VycmVudGx5IHVuYXV0aGVudGljYXRlZC4gRG8gbm90IGV4" + + "cG9zZSBpdCB0byB0aGUKcHVibGljIGludGVybmV0LiBSZXN0cmljdCBhY2Nlc3MgdG8gYSB0cnVz" + + "dGVkIG5ldHdvcmssIFZQTiwgb3IgYW4gYXV0aGVudGljYXRpbmcKcHJveHkgaW4gZnJvbnQgb2Yg" + + "dGhlIGBudmNmLXVpYCByb3V0ZS4KUEsHCDj5J6+5BwAAuQcAAFBLAwQUAAgAAAAAACEAAAAAAAAA" + + "AAAAAAAAPgAJAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtaW5zdGFsbGF0aW9uL3JlZmVyZW5jZXMv" + + "cHVsbC1zZWNyZXRzLm1kVVQFAAEAps4SIyBJbWFnZSBQdWxsIFNlY3JldHMgUmVmZXJlbmNlCgoj" + + "IyBPdmVydmlldwoKV2hlbiBwdWxsaW5nIE5WQ0YgaW1hZ2VzIGZyb20gYSBwcml2YXRlIHJlZ2lz" + + "dHJ5IChmb3IgZXhhbXBsZSwgTkdDCmBudmNyLmlvYCksIEt1YmVybmV0ZXMgbmVlZHMgYGltYWdl" + + "UHVsbFNlY3JldHNgIG9uIGV2ZXJ5IHBvZC4gQ29udHJvbC1wbGFuZSBhbmQKY29tcHV0ZS1wbGFu" + + "ZSBjcmVkZW50aWFscyBhcmUgY29uZmlndXJlZCBpbiB0aGVpciBvd24gY2x1c3RlciBjb250ZXh0" + + "cy4gRG8gbm90CnVzZSBvbmUgbWl4ZWQgbmFtZXNwYWNlIGxpc3QuCgpUaGUgcmVjb21tZW5kZWQg" + + "Y29udHJvbC1wbGFuZSBhcHByb2FjaCB1c2VzIEt5dmVybm8gdG8gaW5qZWN0IHRoZSBzZWNyZXQg" + + "aW50bwpjb250cm9sLXBsYW5lIHBvZHMgYXQgYWRtaXNzaW9uIHRpbWUuIFRoZSBjb21wdXRlLXBs" + + "YW5lIGNoYXJ0IGFuZCBvcGVyYXRvciB1c2UKdGhlIHNlY3JldCBpbiBgbnZjYS1vcGVyYXRvcmAg" + + "YW5kIG1pcnJvciBpdCB0byBgbnZjYS1zeXN0ZW1gLgoKIyMgS3l2ZXJubyBBcHByb2FjaCAoUmVj" + + "b21tZW5kZWQpCgojIyMgMS4gSW5zdGFsbCBLeXZlcm5vCgpgYGBiYXNoCmhlbG0gcmVwbyBhZGQg" + + "a3l2ZXJubyBodHRwczovL2t5dmVybm8uZ2l0aHViLmlvL2t5dmVybm8vCmhlbG0gcmVwbyB1cGRh" + + "dGUKaGVsbSBpbnN0YWxsIGt5dmVybm8ga3l2ZXJuby9reXZlcm5vIC1uIGt5dmVybm8gLS1jcmVh" + + "dGUtbmFtZXNwYWNlIFwKICAtLWt1YmUtY29udGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0Pgpg" + + "YGAKCiMjIyAyLiBDcmVhdGUgY29udHJvbC1wbGFuZSBwdWxsIHNlY3JldHMKCmBgYGJhc2gKZXhw" + + "b3J0IE5HQ19BUElfS0VZPSI8eW91ci1uZ2MtYXBpLWtleT4iCgpmb3IgbnMgaW4gY2Fzc2FuZHJh" + + "LXN5c3RlbSBuYXRzLXN5c3RlbSBudmNmIGFwaS1rZXlzIGVzcyBzaXMgdmF1bHQtc3lzdGVtIGNl" + + "cnQtbWFuYWdlcjsgZG8KICBrdWJlY3RsIC0tY29udGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0" + + "PiBjcmVhdGUgbmFtZXNwYWNlICIkbnMiIFwKICAgIC0tZHJ5LXJ1bj1jbGllbnQgLW8geWFtbCB8" + + "IGt1YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KZG9u" + + "ZQoKZm9yIG5zIGluIGNhc3NhbmRyYS1zeXN0ZW0gbmF0cy1zeXN0ZW0gbnZjZiBhcGkta2V5cyBl" + + "c3Mgc2lzIHZhdWx0LXN5c3RlbSBjZXJ0LW1hbmFnZXI7IGRvCiAga3ViZWN0bCAtLWNvbnRleHQg" + + "PGNvbnRyb2wtcGxhbmUtY29udGV4dD4gY3JlYXRlIHNlY3JldCBkb2NrZXItcmVnaXN0cnkgbnZj" + + "ci1jcmVkcyBcCiAgICAtLWRvY2tlci1zZXJ2ZXI9bnZjci5pbyBcCiAgICAtLWRvY2tlci11c2Vy" + + "bmFtZT0nJG9hdXRodG9rZW4nIFwKICAgIC0tZG9ja2VyLXBhc3N3b3JkPSIkTkdDX0FQSV9LRVki" + + "IFwKICAgIC0tbmFtZXNwYWNlPSIkbnMiIFwKICAgIC0tZHJ5LXJ1bj1jbGllbnQgLW8geWFtbCB8" + + "IGt1YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KZG9u" + + "ZQpgYGAKCkZvciBub24tTkdDIHJlZ2lzdHJpZXMsIHJlcGxhY2UgYC0tZG9ja2VyLXNlcnZlcmAs" + + "IGAtLWRvY2tlci11c2VybmFtZWAsIGFuZCBgLS1kb2NrZXItcGFzc3dvcmRgLgoKIyMjIDMuIEFw" + + "cGx5IEt5dmVybm8gQ2x1c3RlclBvbGljeQoKYGBgeWFtbAphcGlWZXJzaW9uOiBreXZlcm5vLmlv" + + "L3YxCmtpbmQ6IENsdXN0ZXJQb2xpY3kKbWV0YWRhdGE6CiAgbmFtZTogbnZjZi1hZGQtaW1hZ2Vw" + + "dWxsc2VjcmV0cwpzcGVjOgogIGJhY2tncm91bmQ6IGZhbHNlCiAgcnVsZXM6CiAgICAtIG5hbWU6" + + "IGFkZC1pbWFnZXB1bGxzZWNyZXQtdG8tbnZjZi1wb2RzCiAgICAgIG1hdGNoOgogICAgICAgIGFu" + + "eToKICAgICAgICAtIHJlc291cmNlczoKICAgICAgICAgICAga2luZHM6CiAgICAgICAgICAgIC0g" + + "UG9kCiAgICAgICAgICAgIG5hbWVzcGFjZXM6CiAgICAgICAgICAgIC0gIm52Y2YiCiAgICAgICAg" + + "ICAgIC0gImFwaS1rZXlzIgogICAgICAgICAgICAtICJzaXMiCiAgICAgICAgICAgIC0gImVzcyIK" + + "ICAgICAgICAgICAgLSAibmF0cy1zeXN0ZW0iCiAgICAgICAgICAgIC0gImNhc3NhbmRyYS1zeXN0" + + "ZW0iCiAgICAgICAgICAgIC0gInZhdWx0LXN5c3RlbSIKICAgICAgICAgICAgLSAiY2VydC1tYW5h" + + "Z2VyIgogICAgICBtdXRhdGU6CiAgICAgICAgcGF0Y2hTdHJhdGVnaWNNZXJnZToKICAgICAgICAg" + + "IG1ldGFkYXRhOgogICAgICAgICAgICBhbm5vdGF0aW9uczoKICAgICAgICAgICAgICBudmNmLm52" + + "aWRpYS5jb20vaW1hZ2VwdWxsc2VjcmV0LWluamVjdGVkLWJ5OiBreXZlcm5vCiAgICAgICAgICBz" + + "cGVjOgogICAgICAgICAgICBpbWFnZVB1bGxTZWNyZXRzOgogICAgICAgICAgICAtIG5hbWU6IG52" + + "Y3ItY3JlZHMKYGBgCgpgYGBiYXNoCmt1YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNv" + + "bnRleHQ+IGFwcGx5IFwKICAtZiBreXZlcm5vLWltYWdlcHVsbHNlY3JldC1wb2xpY3kueWFtbApg" + + "YGAKCiMjIyA0LiBJbnN0YWxsIHRoZSBjb250cm9sIHBsYW5lCgpDb250aW51ZSB3aXRoIHRoZSBz" + + "ZWxlY3RlZCBjb250cm9sLXBsYW5lIGluc3RhbGxhdGlvbiBpbgpbU3BsaXQgQ29tcHV0ZS1QbGFu" + + "ZSBJbnN0YWxsYXRpb25dKGNvbXB1dGUtcGxhbmUtaW5zdGFsbGF0aW9uLm1kKS4gTm8KY29udHJv" + + "bC1wbGFuZSBoZWxtZmlsZSBtb2RpZmljYXRpb25zIGFyZSBuZWVkZWQuIEt5dmVybm8gaW5qZWN0" + + "cyB0aGUgcHVsbApzZWNyZXQgaW50byBuZXcgY29udHJvbC1wbGFuZSBwb2RzLgoKIyMgQ29tcHV0" + + "ZS1QbGFuZSBTZWNyZXQKCkNyZWF0ZSB0aGUgc2VjcmV0IG9ubHkgaW4gYG52Y2Etb3BlcmF0b3Jg" + + "IG9uIHRoZSBjb21wdXRlIGNsdXN0ZXI6CgpgYGBiYXNoCmt1YmVjdGwgLS1jb250ZXh0IDxjb21w" + + "dXRlLXBsYW5lLWNvbnRleHQ+IGNyZWF0ZSBuYW1lc3BhY2UgbnZjYS1vcGVyYXRvciBcCiAgLS1k" + + "cnktcnVuPWNsaWVudCAtbyB5YW1sIHwgXAogIGt1YmVjdGwgLS1jb250ZXh0IDxjb21wdXRlLXBs" + + "YW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KCmt1YmVjdGwgLS1jb250ZXh0IDxjb21wdXRlLXBsYW5l" + + "LWNvbnRleHQ+IGNyZWF0ZSBzZWNyZXQgZG9ja2VyLXJlZ2lzdHJ5IG52Y3ItY3JlZHMgXAogIC0t" + + "ZG9ja2VyLXNlcnZlcj1udmNyLmlvIFwKICAtLWRvY2tlci11c2VybmFtZT0nJG9hdXRodG9rZW4n" + + "IFwKICAtLWRvY2tlci1wYXNzd29yZD0iJE5HQ19BUElfS0VZIiBcCiAgLS1uYW1lc3BhY2U9bnZj" + + "YS1vcGVyYXRvciBcCiAgLS1kcnktcnVuPWNsaWVudCAtbyB5YW1sIHwgXAogIGt1YmVjdGwgLS1j" + + "b250ZXh0IDxjb21wdXRlLXBsYW5lLWNvbnRleHQ+IGFwcGx5IC1mIC0KYGBgCgpSZWZlcmVuY2Ug" + + "aXQgaW4gdGhlIGNvbXB1dGUtcGxhbmUgZW52aXJvbm1lbnQgZmlsZToKCmBgYHlhbWwKZ2xvYmFs" + + "OgogIGltYWdlUHVsbFNlY3JldHM6CiAgICAtIG5hbWU6IG52Y3ItY3JlZHMKYGBgCgpUaGUgY2hh" + + "cnQgYW5kIG9wZXJhdG9yIHVzZSB0aGF0IHJlZmVyZW5jZSBpbiBgbnZjYS1vcGVyYXRvcmAgYW5k" + + "IG1pcnJvciB0aGUKc2VjcmV0IHRvIGBudmNhLXN5c3RlbWAuIERvIG5vdCBjcmVhdGUgYSBicm9h" + + "ZGVyIG1hbnVhbCBjb21wdXRlIG5hbWVzcGFjZSBsaXN0LiBGb2xsb3cKW1NwbGl0IENvbXB1dGUt" + + "UGxhbmUgSW5zdGFsbGF0aW9uXShjb21wdXRlLXBsYW5lLWluc3RhbGxhdGlvbi5tZCkgZm9yIHRo" + + "ZQpzZWxlY3RlZCBpbnN0YWxsIGZsb3cuCgojIyBWZXJpZmljYXRpb24KCmBgYGJhc2gKIyBDaGVj" + + "ayB0aGF0IGEgY29udHJvbC1wbGFuZSBwb2QgaGFzIHRoZSBpbmplY3RlZCBzZWNyZXQKa3ViZWN0" + + "bCAtLWNvbnRleHQgPGNvbnRyb2wtcGxhbmUtY29udGV4dD4gZ2V0IHBvZCAtbiA8bmFtZXNwYWNl" + + "PiA8cG9kLW5hbWU+IFwKICAtbyBqc29ucGF0aD0ney5zcGVjLmltYWdlUHVsbFNlY3JldHN9Jwoj" + + "IEV4cGVjdGVkOiBbeyJuYW1lIjoibnZjci1jcmVkcyJ9XQoKIyBDaGVjayB0aGUgS3l2ZXJubyBh" + + "bm5vdGF0aW9uCmt1YmVjdGwgLS1jb250ZXh0IDxjb250cm9sLXBsYW5lLWNvbnRleHQ+IGdldCBw" + + "b2QgLW4gPG5hbWVzcGFjZT4gPHBvZC1uYW1lPiBcCiAgLW8ganNvbnBhdGg9J3subWV0YWRhdGEu" + + "YW5ub3RhdGlvbnMubnZjZlwubnZpZGlhXC5jb20vaW1hZ2VwdWxsc2VjcmV0LWluamVjdGVkLWJ5" + + "fScKIyBFeHBlY3RlZDoga3l2ZXJubwoKIyBDaGVjayBwdWxsIGV2ZW50cwprdWJlY3RsIC0tY29u" + + "dGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0PiBnZXQgZXZlbnRzIC1uIDxuYW1lc3BhY2U+IFwK" + + "ICAtLXNvcnQtYnk9Jy5sYXN0VGltZXN0YW1wJyB8IGdyZXAgLWkgcHVsbAojIFNob3VsZCBzaG93" + + "ICJTdWNjZXNzZnVsbHkgcHVsbGVkIiBub3QgIjQwMSBVbmF1dGhvcml6ZWQiCgojIENoZWNrIHRo" + + "ZSBjaGFydC9vcGVyYXRvciBtaXJyb3Igb24gdGhlIGNvbXB1dGUgY2x1c3RlcgprdWJlY3RsIC0t" + + "Y29udGV4dCA8Y29tcHV0ZS1wbGFuZS1jb250ZXh0PiBnZXQgc2VjcmV0IG52Y3ItY3JlZHMgXAog" + + "IC1uIG52Y2Etc3lzdGVtCmBgYAoKIyMgV2hlbiBQdWxsIFNlY3JldHMgQXJlIE5vdCBOZWVkZWQK" + + "Ci0gKipBV1MgRUNSIHdpdGggSUFNIG5vZGUgcm9sZXMqKjogSWYgeW91ciBub2RlcyBoYXZlIGBB" + + "bWF6b25FQzJDb250YWluZXJSZWdpc3RyeVJlYWRPbmx5YCBJQU0gcG9saWN5LCBLdWJlcm5ldGVz" + + "IGNhbiBwdWxsIGZyb20gRUNSIHdpdGhvdXQgZXhwbGljaXQgc2VjcmV0cy4KLSAqKlB1YmxpYyBy" + + "ZWdpc3RyaWVzKio6IE5vIHB1bGwgc2VjcmV0cyBuZWVkZWQuCi0gKipDU1AgYnVpbHQtaW4gY3Jl" + + "ZGVudGlhbCBoZWxwZXJzKio6IEdLRSBBcnRpZmFjdCBSZWdpc3RyeSwgQXp1cmUgQUNSIHdpdGgg" + + "bWFuYWdlZCBpZGVudGl0eSwgYW5kIHNpbWlsYXIgc2VydmljZXMuCgojIyBUcm91Ymxlc2hvb3Rp" + + "bmcKCiMjIyBQb2RzIHN0aWxsIGluIEltYWdlUHVsbEJhY2tPZmYgYWZ0ZXIgYXBwbHlpbmcgcG9s" + + "aWN5CgpUaGUgS3l2ZXJubyBwb2xpY3kgb25seSBhZmZlY3RzIHBvZHMgY3JlYXRlZCAqKmFmdGVy" + + "KiogdGhlIHBvbGljeSBpcyBhcHBsaWVkLiBEZWxldGUgc3R1Y2sgcG9kcyB0byB0cmlnZ2VyIHJl" + + "Y3JlYXRpb246CgpgYGBiYXNoCmt1YmVjdGwgLS1jb250ZXh0IDx0YXJnZXQtY29udGV4dD4gZGVs" + + "ZXRlIHBvZHMgLW4gPG5hbWVzcGFjZT4gLS1hbGwKYGBgCgojIyMgS3l2ZXJubyBhZG1pc3Npb24g" + + "Y29udHJvbGxlciBub3QgcnVubmluZwoKYGBgYmFzaAprdWJlY3RsIC0tY29udGV4dCA8Y29udHJv" + + "bC1wbGFuZS1jb250ZXh0PiBnZXQgcG9kcyAtbiBreXZlcm5vCiMgQWxsIHBvZHMgc2hvdWxkIGJl" + + "IFJ1bm5pbmcKYGBgCgojIyMgUG9saWN5IG5vdCBtYXRjaGluZwoKYGBgYmFzaAprdWJlY3RsIC0t" + + "Y29udGV4dCA8Y29udHJvbC1wbGFuZS1jb250ZXh0PiBnZXQgY2x1c3RlcnBvbGljeSBudmNmLWFk" + + "ZC1pbWFnZXB1bGxzZWNyZXRzCiMgU2hvdWxkIHNob3cgUkVBRFk6IFRydWUKYGBgCgojIyMgV3Jv" + + "bmcgc2VjcmV0IG5hbWUKClRoZSBwb2xpY3kgcmVmZXJlbmNlcyBgbnZjci1jcmVkc2AuIFZlcmlm" + + "eSB0aGUgc2VjcmV0IGV4aXN0cyB3aXRoIHRoYXQgZXhhY3QgbmFtZToKCmBgYGJhc2gKa3ViZWN0" + + "bCAtLWNvbnRleHQgPHRhcmdldC1jb250ZXh0PiBnZXQgc2VjcmV0IG52Y3ItY3JlZHMgLW4gPG5h" + + "bWVzcGFjZT4KYGBgClBLBwhLQjWoABYAAAAWAABQSwECFAAUAAgAAAAAACEAxAuyqkkQAABJEAAA" + + "EgAJAAAAAAAAAAAAAAAAAAAAZGF0YS9tYW5pZmVzdC5qc29uVVQFAAEAps4SUEsBAhQAFAAIAAAA" + + "AAAhAM2BMORNYQAATWEAACMACQAAAAAAAAAAAAAAkhAAAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQt" + + "Y2xpL1NLSUxMLm1kVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhACHGyIbwCgAA8AoAADMACQAAAAAA" + + "AAAAAAAAOXIAAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtY2xpL2V4YW1wbGVzL2NpLXBpcGVsaW5l" + + "cy5tZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQChFOOBXQwAAF0MAAA0AAkAAAAAAAAAAAAAAJN9" + + "AABkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWNsaS9leGFtcGxlcy9tdWx0aS1jbHVzdGVyLm1kVVQF" + + "AAEAps4SUEsBAhQAFAAIAAAAAAAhAEInSQ8FDQAABQ0AADcACQAAAAAAAAAAAAAAW4oAAGRhdGEv" + + "bnZjZi1zZWxmLW1hbmFnZWQtY2xpL3Byb21wdHMvYWRkLWNvbXB1dGUtcGxhbmUubWRVVAUAAQCm" + + "zhJQSwECFAAUAAgAAAAAACEAkdwb+ZgZAACYGQAAOQAJAAAAAAAAAAAAAADOlwAAZGF0YS9udmNm" + + "LXNlbGYtbWFuYWdlZC1jbGkvcHJvbXB0cy9jcmVhdGUtYW5kLXJ1bi10YXNrLm1kVVQFAAEAps4S" + + "UEsBAhQAFAAIAAAAAAAhANJD3AQYJQAAGCUAADcACQAAAAAAAAAAAAAA1rEAAGRhdGEvbnZjZi1z" + + "ZWxmLW1hbmFnZWQtY2xpL3Byb21wdHMvZGVwbG95LWFuZC1pbnZva2UubWRVVAUAAQCmzhJQSwEC" + + "FAAUAAgAAAAAACEADpw/l70OAAC9DgAAPQAJAAAAAAAAAAAAAABc1wAAZGF0YS9udmNmLXNlbGYt" + + "bWFuYWdlZC1jbGkvcHJvbXB0cy9kaWFnbm9zZS1mYWlsZWQtaW5zdGFsbC5tZFVUBQABAKbOElBL" + + "AQIUABQACAAAAAAAIQBCceYeASgAAAEoAAA6AAkAAAAAAAAAAAAAAI3mAABkYXRhL252Y2Ytc2Vs" + + "Zi1tYW5hZ2VkLWNsaS9wcm9tcHRzL2luc3RhbGwtZnJvbS1zY3JhdGNoLm1kVVQFAAEAps4SUEsB" + + "AhQAFAAIAAAAAAAhABrELJ7HBwAAxwcAADkACQAAAAAAAAAAAAAA/w4BAGRhdGEvbnZjZi1zZWxm" + + "LW1hbmFnZWQtY2xpL3Byb21wdHMvcm90YXRlLWNsdXN0ZXItandrcy5tZFVUBQABAKbOElBLAQIU" + + "ABQACAAAAAAAIQASkzNZ4CEAAOAhAAAuAAkAAAAAAAAAAAAAADYXAQBkYXRhL252Y2Ytc2VsZi1t" + + "YW5hZ2VkLWNsaS9wcm9tcHRzL3RlYXJkb3duLm1kVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhAHIN" + + "D2QhIwAAISMAADAACQAAAAAAAAAAAAAAezkBAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtY2xpL3Jl" + + "ZmVyZW5jZS9jb21tYW5kcy5tZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQBzp1pk1QYAANUGAAAy" + + "AAkAAAAAAAAAAAAAAANdAQBkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWNsaS9yZWZlcmVuY2UvZXhp" + + "dC1jb2Rlcy5tZFVUBQABAKbOElBLAQIUABQACAAAAAAAIQD8o5HgxiIAAMYiAAAtAAkAAAAAAAAA" + + "AAAAAEFkAQBkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWNsaS9yZWZlcmVuY2UvZmxhZ3MubWRVVAUA" + + "AQCmzhJQSwECFAAUAAgAAAAAACEA864ThHQRAAB0EQAANwAJAAAAAAAAAAAAAABrhwEAZGF0YS9u" + + "dmNmLXNlbGYtbWFuYWdlZC1jbGkvcmVmZXJlbmNlL3Ryb3VibGVzaG9vdGluZy5tZFVUBQABAKbO" + + "ElBLAQIUABQACAAAAAAAIQAv2rl1QmIAAEJiAAAsAAkAAAAAAAAAAAAAAE2ZAQBkYXRhL252Y2Yt" + + "c2VsZi1tYW5hZ2VkLWluc3RhbGxhdGlvbi9TS0lMTC5tZFVUBQABAKbOElBLAQIUABQACAAAAAAA" + + "IQArE7g/rVUAAK1VAAA0AAkAAAAAAAAAAAAAAPL7AQBkYXRhL252Y2Ytc2VsZi1tYW5hZ2VkLWlu" + + "c3RhbGxhdGlvbi9ldmFscy9ldmFscy5qc29uVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhAAJSTkB/" + + "KAAAfygAAC8ACQAAAAAAAAAAAAAAClICAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtaW5zdGFsbGF0" + + "aW9uL2V4YW1wbGVzLm1kVVQFAAEAps4SUEsBAhQAFAAIAAAAAAAhAJZjvFkSMAAAEjAAAEwACQAA" + + "AAAAAAAAAAAA73oCAGRhdGEvbnZjZi1zZWxmLW1hbmFnZWQtaW5zdGFsbGF0aW9uL3JlZmVyZW5j" + + "ZXMvY29tcHV0ZS1wbGFuZS1pbnN0YWxsYXRpb24ubWRVVAUAAQCmzhJQSwECFAAUAAgAAAAAACEA" + + "Vj5nkJUwAACVMAAAOwAJAAAAAAAAAAAAAACEqwIAZGF0YS9udmNmLXNlbGYtbWFuYWdlZC1pbnN0" + + "YWxsYXRpb24vcmVmZXJlbmNlcy9kZWJ1Z2dpbmcubWRVVAUAAQCmzhJQSwECFAAUAAgAAAAAACEA" + + "le7q7yEpAAAhKQAARAAJAAAAAAAAAAAAAACL3AIAZGF0YS9udmNmLXNlbGYtbWFuYWdlZC1pbnN0" + + "YWxsYXRpb24vcmVmZXJlbmNlcy9oZWxtZmlsZS1zdHJ1Y3R1cmUubWRVVAUAAQCmzhJQSwECFAAU" + + "AAgAAAAAACEAOPknr7kHAAC5BwAAPwAJAAAAAAAAAAAAAAAnBgMAZGF0YS9udmNmLXNlbGYtbWFu" + + "YWdlZC1pbnN0YWxsYXRpb24vcmVmZXJlbmNlcy9udmNmLXVpLWFkZG9uLm1kVVQFAAEAps4SUEsB" + + "AhQAFAAIAAAAAAAhAEtCNagAFgAAABYAAD4ACQAAAAAAAAAAAAAAVg4DAGRhdGEvbnZjZi1zZWxm" + + "LW1hbmFnZWQtaW5zdGFsbGF0aW9uL3JlZmVyZW5jZXMvcHVsbC1zZWNyZXRzLm1kVVQFAAEAps4S" + + "UEsFBgAAAAAXABcArwkAAMskAwAAAA==" var ( skillDataOnce sync.Once diff --git a/tests/bdd/features/multi-cluster-helmfile.feature b/tests/bdd/features/multi-cluster-helmfile.feature index 393615560..27798a109 100644 --- a/tests/bdd/features/multi-cluster-helmfile.feature +++ b/tests/bdd/features/multi-cluster-helmfile.feature @@ -29,7 +29,6 @@ Feature: Install a local multi-cluster NVCF stack with Helmfile | global.imagePullSecrets[0].name | nvcr-pull-secret | | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | - | api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE | nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/stargate-client:0.2.0 | | observability.profile | disabled | And I prepare Helmfile environment "local-bdd" for stack "nvcf-compute-plane" from fixture "tests/bdd/fixtures/nvcf-compute-plane-local-bdd-multi.yaml" with values: | global.imagePullSecrets[0].name | nvcr-pull-secret | @@ -99,6 +98,7 @@ Feature: Install a local multi-cluster NVCF stack with Helmfile When I run command "kubectl --context k3d-ncp-local-cp get configmap/nvcf-api-remote-config -n nvcf -o yaml" Then the command exit code should be 0 And the command output should contain "worker-address: https://llm-request-router.nvcf.svc.cluster.local:50071" + And the command output should contain "llm-router-client-image: nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/pylon:0.14.1" # The dial address chooses the cross-cluster network path. Stargate's # per-pod authority remains the gRPC authority and reverse QUIC SNI, so diff --git a/tests/bdd/features/single-cluster-helmfile-llm-pki.feature b/tests/bdd/features/single-cluster-helmfile-llm-pki.feature index ffde92b55..98a5d24c1 100644 --- a/tests/bdd/features/single-cluster-helmfile-llm-pki.feature +++ b/tests/bdd/features/single-cluster-helmfile-llm-pki.feature @@ -30,7 +30,6 @@ Feature: Install a local single-cluster NVCF stack with PKI-secured LLM transpor | global.imagePullSecrets[0].name | nvcr-pull-secret | | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | - | api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE | nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/stargate-client:0.2.0 | | addons.llm.requestRouter.chartPath | ../../../helm/llm-request-router/llm-request-router | | addons.llm.pki.enabled | true | | addons.llm.pki.dnsNames[0] | llm-request-router.nvcf.svc.cluster.local | @@ -117,6 +116,7 @@ Feature: Install a local single-cluster NVCF stack with PKI-secured LLM transpor When I run command "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml" Then the command exit code should be 0 And the command output should contain "worker-address: llm-request-router.nvcf.svc.cluster.local:50071" + And the command output should contain "llm-router-client-image: nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/pylon:0.14.1" Then these Kubernetes resources should not exist in namespace "envoy-gateway-system" using context "k3d-ncp-local": | kind | name | diff --git a/tests/bdd/features/single-cluster-helmfile.feature b/tests/bdd/features/single-cluster-helmfile.feature index bc9f37669..628b9cd60 100644 --- a/tests/bdd/features/single-cluster-helmfile.feature +++ b/tests/bdd/features/single-cluster-helmfile.feature @@ -22,7 +22,6 @@ Feature: Install a local single-cluster NVCF stack with Helmfile | global.imagePullSecrets[0].name | nvcr-pull-secret | | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | - | api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE | nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/stargate-client:0.2.0 | | observability.profile | disabled | And I prepare Helmfile environment "local-bdd" for stack "nvcf-compute-plane" from fixture "tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml" with values: | global.imagePullSecrets[0].name | nvcr-pull-secret | @@ -92,6 +91,10 @@ Feature: Install a local single-cluster NVCF stack with Helmfile | llm-request-router | nvcf | | llm-api-gateway | nvcf | + When I run command "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml" + Then the command exit code should be 0 + And the command output should contain "llm-router-client-image: nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/pylon:0.14.1" + Rule: Helmfile installs NVCA on the same local cluster after registration via the stack Makefile Background: diff --git a/tests/bdd/features/single-cluster-up-oneclick.feature b/tests/bdd/features/single-cluster-up-oneclick.feature index e5c72e191..d81ad8482 100644 --- a/tests/bdd/features/single-cluster-up-oneclick.feature +++ b/tests/bdd/features/single-cluster-up-oneclick.feature @@ -37,7 +37,6 @@ Feature: Bring up a local single-cluster NVCF stack with the self-hosted up one- | global.imagePullSecrets[0].name | nvcr-pull-secret | | global.helm.sources.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | | global.image.repository | ${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM} | - | api.env.NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE | nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/stargate-client:0.2.0 | | observability.profile | disabled | And I prepare Helmfile environment "local" for stack "nvcf-compute-plane" from fixture "tests/bdd/fixtures/nvcf-compute-plane-local-bdd.yaml" with values: | global.imagePullSecrets[0].name | nvcr-pull-secret | @@ -101,6 +100,10 @@ Feature: Bring up a local single-cluster NVCF stack with the self-hosted up one- | sis | sis | | api | nvcf | + When I run command "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml" + Then the command exit code should be 0 + And the command output should contain "llm-router-client-image: nvcr.io/${SAMPLE_NGC_ORG}/${SAMPLE_NGC_TEAM}/pylon:0.14.1" + # The compute plane (NVCA operator) is deployed on the same cluster. Then these Helm releases should be deployed using context "k3d-ncp-local": | name | namespace | diff --git a/tests/bdd/fixtures/self-managed-local-bdd-multi.yaml b/tests/bdd/fixtures/self-managed-local-bdd-multi.yaml index 9cec004a9..bb22893e8 100644 --- a/tests/bdd/fixtures/self-managed-local-bdd-multi.yaml +++ b/tests/bdd/fixtures/self-managed-local-bdd-multi.yaml @@ -54,10 +54,6 @@ openbao: # Single-node local cluster: hard anti-affinity would leave the 2nd replica Pending. replicas: 1 -api: - env: - NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: nvcr.io/REPLACE_WITH_SAMPLE_NGC_ORG/REPLACE_WITH_SAMPLE_NGC_TEAM/stargate-client:0.2.0 - # LLM gateway talks to nvcf-api over plaintext gRPC in local dev # (api.nvcf.svc.cluster.local:9090 has no TLS). addons: diff --git a/tests/bdd/fixtures/self-managed-local-bdd.yaml b/tests/bdd/fixtures/self-managed-local-bdd.yaml index b4ed793ca..b8236f14d 100644 --- a/tests/bdd/fixtures/self-managed-local-bdd.yaml +++ b/tests/bdd/fixtures/self-managed-local-bdd.yaml @@ -51,12 +51,11 @@ openbao: # endpoint because the current binary has no OpenTelemetry TLS provider. Keep # worker telemetry on HTTP in this fixture until Pylon supports HTTPS OTLP. # -# The LLM router client sidecar image defaults to "dummy" in the -# self-managed API profile; the Background overlays the placeholders. +# The stack derives the Pylon image from the effective sidecar registry and +# repository when the LLM addon is enabled. api: env: MANAGEMENT_OTLP_TRACING_ENDPOINT: http://dummy:8282 - NVCF_SIDECARS_LLM_ROUTER_CLIENT_IMAGE: nvcr.io/REPLACE_WITH_SAMPLE_NGC_ORG/REPLACE_WITH_SAMPLE_NGC_TEAM/stargate-client:0.2.0 # LLM gateway talks to nvcf-api over plaintext gRPC in local dev # (api.nvcf.svc.cluster.local:9090 has no TLS). diff --git a/tests/bdd/godog_test.go b/tests/bdd/godog_test.go index eedb40e1f..d95017d89 100644 --- a/tests/bdd/godog_test.go +++ b/tests/bdd/godog_test.go @@ -334,6 +334,10 @@ func TestSingleClusterUpOneClickFeatureFileWiresToSteps(t *testing.T) { t.Setenv("SAMPLE_NGC_TEAM", "test-team") suite := newWiringSuite(t, newFakeRunner(map[string]harness.Result{ "helm list --all-namespaces --kube-context k3d-ncp-local -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, + "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml": { + ExitCode: 0, + Stdout: "data:\n nvcf-api.yaml: |\n nvcf:\n sidecars:\n llm-router-client-image: nvcr.io/test-org/test-team/pylon:0.14.1\n", + }, // Conflict precheck: feature asserts the multi-cluster // control-plane is absent. "k3d cluster get ncp-local-cp": {ExitCode: 1}, @@ -420,6 +424,10 @@ func TestSingleClusterHelmfileFeatureFileWiresToSteps(t *testing.T) { t.Setenv("REPO_ROOT", "/repo-root-placeholder") suite := newWiringSuite(t, newFakeRunner(map[string]harness.Result{ "helm list --all-namespaces --kube-context k3d-ncp-local -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, + "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml": { + ExitCode: 0, + Stdout: "data:\n nvcf-api.yaml: |\n nvcf:\n sidecars:\n llm-router-client-image: nvcr.io/test-org/test-team/pylon:0.14.1\n", + }, "/usr/bin/nvcf-cli --config /repo-root-placeholder/tests/bdd/fixtures/nvcf-cli-local.yaml function invoke --request-body '{\"message\":\"bdd-echo\",\"repeats\":1}' --timeout 120 --poll-duration 5": { ExitCode: 0, Stdout: "Function invocation completed!\n\nResponse:\n{\"rawResponse\":\"bdd-echo\"}\n", @@ -530,7 +538,9 @@ func TestSingleClusterHelmfileLLMPKIFeatureFileWiresToSteps(t *testing.T) { "helm list --all-namespaces --kube-context k3d-ncp-local -o json": {ExitCode: 0, Stdout: helmListAllNamespacesJSON()}, "kubectl --context k3d-ncp-local get configmap/nvcf-api-remote-config -n nvcf -o yaml": { ExitCode: 0, - Stdout: "data:\n application-custom.yaml: |\n nvcf:\n llm-request-router:\n worker-address: llm-request-router.nvcf.svc.cluster.local:50071\n", + Stdout: "data:\n nvcf-api.yaml: |\n nvcf:\n" + + " llm-request-router:\n worker-address: llm-request-router.nvcf.svc.cluster.local:50071\n" + + " sidecars:\n llm-router-client-image: nvcr.io/test-org/test-team/pylon:0.14.1\n", }, "helm get values nvca-operator --namespace nvca-operator --kube-context k3d-ncp-local -o yaml": { ExitCode: 0, @@ -1002,7 +1012,9 @@ func TestMultiClusterHelmfileFeatureFileWiresToSteps(t *testing.T) { "helm list --all-namespaces --kube-context k3d-ncp-local-compute-1 -o json": {ExitCode: 0, Stdout: helmListNVCAJSON()}, "kubectl --context k3d-ncp-local-cp get configmap/nvcf-api-remote-config -n nvcf -o yaml": { ExitCode: 0, - Stdout: "data:\n application-custom.yaml: |\n nvcf:\n llm-request-router:\n worker-address: https://llm-request-router.nvcf.svc.cluster.local:50071\n", + Stdout: "data:\n nvcf-api.yaml: |\n nvcf:\n" + + " llm-request-router:\n worker-address: https://llm-request-router.nvcf.svc.cluster.local:50071\n" + + " sidecars:\n llm-router-client-image: nvcr.io/test-org/test-team/pylon:0.14.1\n", }, "kubectl get certificate/stargate-quic-tls --namespace nvcf --context k3d-ncp-local-cp -o yaml": { ExitCode: 0,