diff --git a/.bazelignore b/.bazelignore index 5766817411..0c435ec9e0 100644 --- a/.bazelignore +++ b/.bazelignore @@ -45,7 +45,11 @@ src/libraries/python # @stargate_crates from the nested module) do not resolve against the # umbrella root MODULE.bazel. src/libraries/ts -deploy +# deploy is deliberately NOT ignored. The nvca-operator chart lives there and +# its storage-capability catalog is a data dependency of Go tests under src/. +# An ignored directory contributes no files to the sandbox, so the tests fail +# to read it. deploy contains no BUILD files, so leaving it loadable adds no +# packages: `bazel query //...` reports the same target count either way. docs fern infra diff --git a/.claude/skills/nvca-chart-release b/.claude/skills/nvca-chart-release deleted file mode 120000 index 4faf728bc9..0000000000 --- a/.claude/skills/nvca-chart-release +++ /dev/null @@ -1 +0,0 @@ -../../ai-tooling/dev/skills/nvca-chart-release \ No newline at end of file diff --git a/.codex/skills/nvca-chart-release b/.codex/skills/nvca-chart-release deleted file mode 120000 index 4faf728bc9..0000000000 --- a/.codex/skills/nvca-chart-release +++ /dev/null @@ -1 +0,0 @@ -../../ai-tooling/dev/skills/nvca-chart-release \ No newline at end of file diff --git a/.cursor/skills/nvca-chart-release b/.cursor/skills/nvca-chart-release deleted file mode 120000 index 4faf728bc9..0000000000 --- a/.cursor/skills/nvca-chart-release +++ /dev/null @@ -1 +0,0 @@ -../../ai-tooling/dev/skills/nvca-chart-release \ No newline at end of file diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 447735deef..a5c4e50e47 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -107,20 +107,6 @@ jobs: - name: Lint and render every chart with a CI values file run: ./tools/ci/check-helm-charts - - name: Check NVCA Operator chart is synced with its monorepo source - # Pins the version fields to whatever is already committed in the - # vendored chart, so this only fails on real content drift between - # src/compute-plane-services/nvca/deployments/nvca-operator and - # deploy/helm/nvca-operator/nvca-operator, not on pending version bumps. - run: | - set -euo pipefail - cd deploy/helm/nvca-operator - NVCA_VERSION="$(yq '.selfManaged.nvcaVersion' nvca-operator/values.yaml)" \ - NVCA_OPERATOR_VERSION="$(yq '.appVersion' nvca-operator/Chart.yaml)" \ - NVCA_SHARED_STORAGE_IMAGE_TAG="$(yq '.selfManaged.sharedStorage.imageTag' nvca-operator/values.yaml)" \ - NVCA_OTEL_COLLECTOR_IMAGE_TAG="$(yq '.otelCollector.imageTag' nvca-operator/values.yaml)" \ - make check-vendor-chart - - name: Test NVCA default ownership run: | make -C deploy/helm/nvca-operator test-default-ownership diff --git a/AGENTS.md b/AGENTS.md index ae2026c501..cbd7444825 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -217,7 +217,6 @@ public snapshot; still follow OSS Snapshot Hygiene manually before finishing. |-------|----------|---------| | `documentation-style` | `ai-tooling/dev/skills/` | Public docs, AGENTS, and skill-writing style | | `nvcf-explore-stack` | `ai-tooling/dev/skills/` | Navigate the self-hosted stack topology and dependency graph | -| `nvca-chart-release` | `ai-tooling/dev/skills/` | Release NVCA Operator chart changes from monorepo source to the vendored Helm chart | | `nvca-self-managed-install` | `ai-tooling/dev/skills/` | Install or validate the NVCA Operator chart against a self-managed control plane | | `nvca-values-customization` | `ai-tooling/dev/skills/` | Customize NVCA Operator Helm chart values in the monorepo | | `nvcf-self-managed-cli` | `ai-tooling/user/skills/` | Install, operate, and manage self-managed NVCF through `nvcf-cli` | diff --git a/BUILD.bazel b/BUILD.bazel index 4754ee06fc..41bcd08e0d 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -39,6 +39,18 @@ exports_files( visibility = [":java_notice_consumers"], ) +# The nvca-operator chart lives under deploy/, which is excluded from gazelle +# and so has no package of its own. Its storage-capability catalog is consumed +# by Go tests under src/, so it is exported from the root package here. +filegroup( + name = "nvca-operator-storage-capability-catalog", + srcs = [ + "deploy/helm/nvca-operator/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json", + "deploy/helm/nvca-operator/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml", + ], + visibility = ["//visibility:public"], +) + # Gazelle manages only the native Go subtrees listed in go.work.bazel. # nv-boot-parent and Cloud Tasks are also owned by the root Bazel module, but # their checked-in Java BUILD files are maintained manually and remain outside @@ -58,7 +70,7 @@ exports_files( # gazelle:exclude bazel-out # gazelle:exclude bazel-testlogs # gazelle:exclude ci -# gazelle:exclude deploy +# gazelle:exclude deploy # keeps gazelle out; .bazelignore deliberately does not ignore it # gazelle:exclude docs # gazelle:exclude examples # gazelle:exclude fern diff --git a/ai-tooling/dev/skills/nvca-chart-release/SKILL.md b/ai-tooling/dev/skills/nvca-chart-release/SKILL.md deleted file mode 100644 index d1529ac40d..0000000000 --- a/ai-tooling/dev/skills/nvca-chart-release/SKILL.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -name: nvca-chart-release -description: Release NVCA Operator chart changes from the native monorepo source to the vendored Helm chart. Use when updating the vendored NVCA Operator chart, changing NVCA image refs, publishing helm-nvca-operator, or validating the chart against a self-managed control plane. -license: Apache-2.0 -compatibility: Requires a local checkout of the NVCF monorepo with deploy/helm/nvca-operator/ and src/compute-plane-services/nvca/ present, plus helm and yq. -author: "nvcf-core-eng " -version: "1.0.0" -tags: [nvcf, nvca, helm, chart-release, self-managed] -tools: [Read, Grep, Glob, Shell] -metadata: - internal: false - author: "nvcf-core-eng " - version: "1.0" - tags: [nvcf, nvca, helm, chart-release] - languages: [bash] - frameworks: [helm] - domain: cloud-infrastructure ---- - -# NVCA Operator Chart Release Workflow - -Propagates Helm chart changes through the native monorepo paths: - -| Component | Path | Purpose | -|-----------|------|---------| -| NVCA source | `src/compute-plane-services/nvca` | Operator and agent source plus source chart at `deployments/nvca-operator/` | -| Vendored chart | `deploy/helm/nvca-operator` | Vendors the source chart, applies self-managed defaults, publishes `helm-nvca-operator` | -| Self-managed stack | `deploy/stacks/self-managed` | Control-plane Helmfile deployment and environment defaults | - -## Workflow - -1. Make source changes in `src/compute-plane-services/nvca` when operator, - agent, or source chart behavior changes. Test them there. -2. Vendor from the monorepo source chart at - `src/compute-plane-services/nvca/deployments/nvca-operator/`. -3. Set the version inputs, either in the environment or in - `deploy/helm/nvca-operator/.env`: - - ```bash - NVCA_OPERATOR_VERSION= - NVCA_VERSION= - NVCA_SHARED_STORAGE_IMAGE_TAG= - NVCA_OTEL_COLLECTOR_IMAGE_TAG= - ``` - - `NVCA_OTEL_COLLECTOR_IMAGE_TAG` is normally left alone: it moves on its own - when `byoo-otel-collector` releases, driven by `tools/chart-version-bumper` - against the vendored chart directly rather than through this source-first - flow. Set it explicitly only when vendoring by hand; otherwise pass the - value already committed in `deploy/helm/nvca-operator/nvca-operator/values.yaml` - (`otelCollector.imageTag`) so an unrelated vendor run does not revert it. - -4. Vendor and validate from `deploy/helm/nvca-operator`: - - ```bash - make vendor-chart - make lint - make template - make validate - ``` - -5. If the chart is tested against a local self-managed control plane, render - stack-aware values and install from this chart subtree: - - ```bash - make render-values-from-stack stack_repo=../../../deploy/stacks/self-managed stack_env=local - make install-from-stack stack_repo=../../../deploy/stacks/self-managed stack_env=local - ``` - - Use `additional_values=override.yml` for one-off validation. Do not edit the - stack just to test this chart. - -## CI and Release - -Umbrella CI is declared in `tools/ci/subproject-validations.yaml` with -subproject id `nvca-operator`. Do not add a chart-local `.gitlab-ci.yml`. - -Run the repository-wide Helm validation used by CI: - -```bash -tools/ci/check-helm-charts -``` - -## Local Image Testing - -When testing local images in k3d, build them from `src/compute-plane-services/nvca` -and import them into the test cluster. Keep tags explicit and match them in the -chart values: - -```bash -export NVCA_OPERATOR_VERSION=dev-local -export NVCA_VERSION=dev-local - -# Run from src/compute-plane-services/nvca. -docker build -f docker/Dockerfile.nvca-operator \ - -t nvca-operator:$NVCA_OPERATOR_VERSION . -docker build -f docker/Dockerfile.nvca \ - -t nvca:$NVCA_VERSION . -``` - -Use the local-dev safety guidance before creating or deleting k3d clusters. - -## Gotchas - -- `make vendor-chart` overwrites the vendored `nvca-operator/` chart. -- Chart release generation depends on Conventional Commit semantics at the - umbrella level. Use `feat` or `fix` when a chart release is required. -- Keep `image.*`, `nvcaImage.*`, `ngcConfig.*`, and `selfManaged.*` values in - sync with the stack and source image tags. -- Never commit service keys, kubeconfigs, rendered secrets, or local registry - credentials. diff --git a/deploy/helm/nvca-operator/Makefile b/deploy/helm/nvca-operator/Makefile index 08d62d3841..300be9b496 100644 --- a/deploy/helm/nvca-operator/Makefile +++ b/deploy/helm/nvca-operator/Makefile @@ -44,7 +44,7 @@ OCI_REGISTRY_NAMESPACE ?= CHART_NAME := $(shell yq -r .name $(helm_dir)/Chart.yaml) CHART_VERSION := $(shell yq -r .version $(helm_dir)/Chart.yaml) -.PHONY: install uninstall status lint template validate clean package push-oci sync-chart check-synced-chart render-values-from-stack install-from-stack test-render-values test-vendor-chart-image-tag test-build-release-assets test-release-image-manifest test-release-artifact-permissions test-package-release-assets test-attach-release-assets test-release-sbom-wrapper test-self-managed-nvca-image-reference test-otel-collector-compatibility test-image-pull-secret-defaults test-pod-disruption-budget test-first-class-byoo-values test-first-class-storage-worker-values test-default-ownership +.PHONY: install uninstall status lint template validate clean package push-oci render-values-from-stack install-from-stack test-render-values test-build-release-assets test-release-image-manifest test-release-artifact-permissions test-package-release-assets test-attach-release-assets test-release-sbom-wrapper test-self-managed-nvca-image-reference test-otel-collector-compatibility test-image-pull-secret-defaults test-pod-disruption-budget test-first-class-byoo-values test-first-class-storage-worker-values test-default-ownership install: ifndef values @@ -92,9 +92,6 @@ install-from-stack: render-values-from-stack test-render-values: @bash ./tests/render_values_from_stack_env_test.sh -test-vendor-chart-image-tag: - @bash ./tests/vendor_chart_image_tag_test.sh - test-build-release-assets: @bash ./tests/build_release_assets_test.sh @@ -188,25 +185,3 @@ push-oci: @echo "[push-oci] Cleaning up temporary package directory..." @rm -rf ./packaged-charts @echo "[push-oci] Cleanup complete." - -# Sync NVCA Operator chart from monorepo source chart -vendor-chart: - @echo "🚀 [vendor-chart] Vendor NVCA Operator chart from local monorepo source..." - @./scripts/ci_vendor_nvca_operator_chart - @echo "🎉 [vendor-chart] Vendor complete. Deployments available in $(helm_dir)/" - -check-vendor-chart: vendor-chart - @echo "🚀 [check-vendor-chart] Checking if chart is synced..." - @if ! git diff --ignore-space-at-eol --exit-code -- .; then \ - echo ""; \ - echo "❌ [check-vendor-chart] Chart is NOT synced with source chart!"; \ - echo ""; \ - echo "📋 To fix this issue:"; \ - echo " 1. Run: make vendor-chart"; \ - echo " 2. Review and commit the changes"; \ - echo " 3. Update your merge request"; \ - echo ""; \ - echo "💡 This ensures your deployment matches the monorepo source NVCA Operator chart."; \ - exit 1; \ - fi - @echo "🎉 [check-vendor-chart] Chart is synced." diff --git a/deploy/helm/nvca-operator/nvca-operator/values.yaml b/deploy/helm/nvca-operator/nvca-operator/values.yaml index c7b40c18be..04d96562cd 100644 --- a/deploy/helm/nvca-operator/nvca-operator/values.yaml +++ b/deploy/helm/nvca-operator/nvca-operator/values.yaml @@ -347,7 +347,7 @@ webhook: ## - "self-managed": Cluster configuration managed by the self-hosted compute plane ngcConfig: username: '$oauthtoken' - serviceKey: "dummy-api-key" + serviceKey: "" serviceKeySecretName: "ngc-service-key" serviceKeySecretKeyName: "ngcServiceKey" apiURL: https://api.ngc.nvidia.com @@ -403,7 +403,7 @@ helmManaged: ## All values below are under the 'selfManaged:' key, e.g. 'selfManaged.nvcaVersion' selfManaged: ## @param selfManaged.nvcaVersion (REQUIRED) Version of the NVCFBackend to use - nvcaVersion: "3.10.0" + nvcaVersion: "" ## @param selfManaged.featureGateValues (Optional) List of feature gates to enable. Defaults to ["DynamicGPUDiscovery"] if not specified. featureGateValues: ["DynamicGPUDiscovery"] ## @param selfManaged.gpuManualInstanceConfigB64 (Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required. @@ -425,19 +425,19 @@ selfManaged: imageRepository: "" imageTag: 0.160.0-nv-0.2.5 ## @param selfManaged.icmsServiceURL URL of the ICMS service for self-managed clusters. Override with the endpoint generated during cluster registration. - icmsServiceURL: "http://icms.example.invalid:8080" + icmsServiceURL: "" ## @param selfManaged.icmsServiceHostHeaderOverride Optional Host header override for selfManaged.icmsServiceURL. icmsServiceHostHeaderOverride: "" ## @param selfManaged.revalServiceURL URL of the ReVal service for self-managed clusters. Override with the endpoint generated during cluster registration. - revalServiceURL: "http://reval.example.invalid:8080" + revalServiceURL: "" ## @param selfManaged.revalServiceHostHeaderOverride Optional Host header override for selfManaged.revalServiceURL. revalServiceHostHeaderOverride: "" ## @param selfManaged.natsURL URL of the NATS service for self-managed clusters. Override with the endpoint generated during cluster registration. - natsURL: "nats://nats.example.invalid:4222" + natsURL: "" ## @param selfManaged.natsHostOverride Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL. natsHostOverride: "" sharedStorage: - imageTag: 1.0.5 + imageTag: "" ## @section Node Selector Configuration ## @param nodeSelector.key Node-selector Label key ## @param nodeSelector.value Node-selector Label value @@ -573,5 +573,5 @@ clusterValidator: limits: cpu: 200m memory: 128Mi -nameOverride: nvca-operator -fullnameOverride: nvca-operator +nameOverride: "" +fullnameOverride: "" diff --git a/deploy/helm/nvca-operator/scripts/ci_vendor_nvca_operator_chart b/deploy/helm/nvca-operator/scripts/ci_vendor_nvca_operator_chart deleted file mode 100755 index 3c26285d7c..0000000000 --- a/deploy/helm/nvca-operator/scripts/ci_vendor_nvca_operator_chart +++ /dev/null @@ -1,304 +0,0 @@ -#!/bin/bash -set -euo pipefail -root_dir="$(cd "$(dirname "$0")/.." && pwd)" - -update_yaml_key() { - local yaml_update_expr yaml_file tmp_file - yaml_update_expr="$1" - yaml_file="$2" - tmp_file="$(mktemp)" - echo "Updating $yaml_file with expression '$yaml_update_expr'" - yq eval "${yaml_update_expr}" "$yaml_file" > "$tmp_file" - mv "$tmp_file" "$yaml_file" -} - -update_json_key() { - local json_update_expr json_file tmp_file - json_update_expr="$1" - json_file="$2" - tmp_file="$(mktemp)" - echo "Updating $json_file with expression '$json_update_expr'" - yq eval --output-format=json --indent 4 "${json_update_expr}" "$json_file" > "$tmp_file" - mv "$tmp_file" "$json_file" -} - -replace_text() { - local sed_expr target_file tmp_file - sed_expr="$1" - target_file="$2" - tmp_file="$(mktemp)" - sed "${sed_expr}" "$target_file" > "$tmp_file" - mv "$tmp_file" "$target_file" -} - -insert_after_line_once() { - local target_line inserted_text target_file inserted_first_line tmp_file - target_line="$1" - inserted_text="$2" - target_file="$3" - inserted_first_line="$(printf '%s' "$inserted_text" | sed 's/\\n.*//')" - - if grep -Fxq "$inserted_first_line" "$target_file"; then - return - fi - - tmp_file="$(mktemp)" - awk -v target_line="$target_line" -v inserted_text="$inserted_text" ' - BEGIN { - gsub(/\\n/, "\n", inserted_text) - } - { - print - if (!inserted && $0 == target_line) { - print inserted_text - inserted = 1 - } - } - END { - if (!inserted) { - exit 1 - } - } - ' "$target_file" > "$tmp_file" || { - rm -f "$tmp_file" - echo "Failed to insert text after '${target_line}' in ${target_file}" >&2 - exit 1 - } - mv "$tmp_file" "$target_file" -} - -insert_release_artifact_image_annotations() { - local target_file tmp_file - local nvca_annotation_line image_credential_helper_annotation_line samba_annotation_line - local has_nvca_annotation has_image_credential_helper_annotation has_samba_annotation - target_file="$1" - nvca_annotation_line=' release-artifact-nvca-image: {{ printf "%s:%s" .Values.nvcaImage.repositoryOverride .Values.selfManaged.nvcaVersion | quote }}' - image_credential_helper_annotation_line=' release-artifact-nvcf-image-credential-helper-image: {{ printf "%s:%s" (include "nvcaop.imageCredHelperRepository" (dict "imageRepository" .Values.selfManaged.imageCredHelper.imageRepository "defaultRepository" .Values.image.repository)) .Values.selfManaged.imageCredHelper.imageTag | quote }}' - samba_annotation_line=' release-artifact-samba-image: {{ printf "%s:%s" .Values.selfManaged.sharedStorage.imageRepository .Values.selfManaged.sharedStorage.imageTag | quote }}' - - has_nvca_annotation=0 - has_image_credential_helper_annotation=0 - has_samba_annotation=0 - - if grep -Fq "release-artifact-nvca-image" "$target_file"; then - has_nvca_annotation=1 - fi - - if grep -Fq "release-artifact-nvcf-image-credential-helper-image" "$target_file"; then - has_image_credential_helper_annotation=1 - fi - if grep -Fq "release-artifact-samba-image" "$target_file"; then - has_samba_annotation=1 - fi - - if [ "$has_nvca_annotation" -eq 0 ] && [ "$has_image_credential_helper_annotation" -eq 0 ] && [ "$has_samba_annotation" -eq 0 ]; then - tmp_file="$(mktemp)" - awk ' - { - print - if ($0 == " namespace: {{ .Release.Namespace }}") { - print " {{- $releaseArtifactNvcaImageRepository := .Values.nvcaImage.repositoryOverride }}" - print " {{- $releaseArtifactImageCredentialHelperRepository := (.Values.selfManaged.imageCredHelper | default dict).imageRepository }}" - print " {{- $releaseArtifactSambaRepository := (.Values.selfManaged.sharedStorage | default dict).imageRepository }}" - print " {{- if or $releaseArtifactNvcaImageRepository $releaseArtifactImageCredentialHelperRepository $releaseArtifactSambaRepository }}" - print " annotations:" - print " {{- if $releaseArtifactNvcaImageRepository }}" - print " release-artifact-nvca-image: {{ printf \"%s:%s\" .Values.nvcaImage.repositoryOverride .Values.selfManaged.nvcaVersion | quote }}" - print " {{- end }}" - print " {{- if $releaseArtifactImageCredentialHelperRepository }}" - print " release-artifact-nvcf-image-credential-helper-image: {{ printf \"%s:%s\" (include \"nvcaop.imageCredHelperRepository\" (dict \"imageRepository\" .Values.selfManaged.imageCredHelper.imageRepository \"defaultRepository\" .Values.image.repository)) .Values.selfManaged.imageCredHelper.imageTag | quote }}" - print " {{- end }}" - print " {{- if $releaseArtifactSambaRepository }}" - print " release-artifact-samba-image: {{ printf \"%s:%s\" .Values.selfManaged.sharedStorage.imageRepository .Values.selfManaged.sharedStorage.imageTag | quote }}" - print " {{- end }}" - print " {{- end }}" - inserted = 1 - } - } - END { - if (!inserted) { - exit 1 - } - } - ' "$target_file" > "$tmp_file" || { - rm -f "$tmp_file" - echo "Failed to insert release-artifact image annotations into $target_file" >&2 - exit 1 - } - mv "$tmp_file" "$target_file" - return - fi - - if [ "$has_nvca_annotation" -eq 0 ]; then - insert_after_line_once " annotations:" "${nvca_annotation_line}" "$target_file" - fi - - if [ "$has_image_credential_helper_annotation" -eq 0 ]; then - if grep -Fq "${nvca_annotation_line}" "$target_file"; then - insert_after_line_once "${nvca_annotation_line}" "${image_credential_helper_annotation_line}" "$target_file" - else - insert_after_line_once " annotations:" "${image_credential_helper_annotation_line}" "$target_file" - fi - fi - - if [ "$has_samba_annotation" -eq 0 ]; then - if grep -Fq "${image_credential_helper_annotation_line}" "$target_file"; then - insert_after_line_once "${image_credential_helper_annotation_line}" "${samba_annotation_line}" "$target_file" - elif grep -Fq "${nvca_annotation_line}" "$target_file"; then - insert_after_line_once "${nvca_annotation_line}" "${samba_annotation_line}" "$target_file" - else - insert_after_line_once " annotations:" "${samba_annotation_line}" "$target_file" - fi - fi -} - -apply_license_headers_if_missing() { - local target_dir header_file file - target_dir="$1" - header_file="${root_dir}/.license-header.txt" - - while IFS= read -r -d '' file; do - # Some source templates already carry SPDX headers below Helm guard lines. - # Do not prepend another header when an SPDX marker is already present. - if grep -Fq "SPDX-FileCopyrightText:" "$file"; then - continue - fi - sh "${root_dir}/scripts/apply-license-header.sh" "${header_file}" "${file}" - done < <(find "${target_dir}" -type f \( -name '*.yaml' -o -name '*.yml' \) -print0) -} - -# Vendor NVCA Operator chart from the monorepo source chart -main() { - # Load local defaults when present; CI supplies these values as variables. - if [ -f "${root_dir}/.env" ]; then - source "${root_dir}/.env" - fi - - byoo_otel_collector_image="${BYOO_OTEL_COLLECTOR_IMAGE:-}" - - TARGET_DIR="${root_dir}/nvca-operator" - SOURCE_DIR="${root_dir}/../../../src/compute-plane-services/nvca/deployments/nvca-operator" - - if [ ! -d "${SOURCE_DIR}" ]; then - echo "❌ [vendor] Error: monorepo source chart not found at ${SOURCE_DIR}" - exit 1 - fi - - echo "📂 [vendor] Copying monorepo source chart from ${SOURCE_DIR}..." - SUBDIR_PATH="${SOURCE_DIR}" - SOURCE_LABEL="monorepo source chart" - - # Remove the existing chart only after the replacement source is available. - if [ -d "${TARGET_DIR}" ]; then - echo "🗑️ [vendor] Removing existing ${TARGET_DIR} directory..." - rm -rf "${TARGET_DIR}" - fi - - # Copy only the deployments/nvca-operator subdirectory to target - echo "📂 [vendor] Copying deployments/nvca-operator subdirectory..." - mkdir -p "${TARGET_DIR}" - cp -r "${SUBDIR_PATH}"/* "${TARGET_DIR}/" - - # Default various values so chart works OOTB - echo "🔧 [vendor] Defaulting various values so chart works OOTB..." - update_yaml_key ".ngcConfig.clusterSource = \"ngc-managed\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".ngcConfig.serviceKey = \"dummy-api-key\"" "${TARGET_DIR}/values.yaml" - if [[ -n "${byoo_otel_collector_image}" ]]; then - update_yaml_key ".agent.functionEnvOverrides.BYOO_OTEL_COLLECTOR_CONTAINER = \"${byoo_otel_collector_image}\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".agent.taskEnvOverrides.BYOO_OTEL_COLLECTOR_CONTAINER = \"${byoo_otel_collector_image}\"" "${TARGET_DIR}/values.yaml" - fi - # Keep the source chart's empty tag so the templates fall back to the - # published chart version. A literal default is retained by Helm - # --reuse-values upgrades and pins the operator to an older image. - update_yaml_key ".selfManaged.nvcaVersion = \"${NVCA_VERSION:?"NVCA_VERSION is not set"}\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".selfManaged.icmsServiceURL = \"http://icms.example.invalid:8080\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".selfManaged.revalServiceURL = \"http://reval.example.invalid:8080\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".selfManaged.natsURL = \"nats://nats.example.invalid:4222\"" "${TARGET_DIR}/values.yaml" - insert_after_line_once \ - ' icmsServiceURL: "http://icms.example.invalid:8080"' \ - ' ## @param selfManaged.icmsServiceHostHeaderOverride Optional Host header override for selfManaged.icmsServiceURL.\n icmsServiceHostHeaderOverride: ""' \ - "${TARGET_DIR}/values.yaml" - insert_after_line_once \ - ' revalServiceURL: "http://reval.example.invalid:8080"' \ - ' ## @param selfManaged.revalServiceHostHeaderOverride Optional Host header override for selfManaged.revalServiceURL.\n revalServiceHostHeaderOverride: ""' \ - "${TARGET_DIR}/values.yaml" - insert_after_line_once \ - ' natsURL: "nats://nats.example.invalid:4222"' \ - ' ## @param selfManaged.natsHostOverride Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL.\n natsHostOverride: ""' \ - "${TARGET_DIR}/values.yaml" - replace_text 's|^ ## @param selfManaged\.icmsServiceURL .*| ## @param selfManaged.icmsServiceURL URL of the ICMS service for self-managed clusters. Override with the endpoint generated during cluster registration.|' "${TARGET_DIR}/values.yaml" - replace_text 's#URL of the ReVal service for self-managed clusters\. Required when ngcConfig\.clusterSource is "self-managed"\.#URL of the ReVal service for self-managed clusters. Override with the endpoint generated during cluster registration.#' "${TARGET_DIR}/values.yaml" - replace_text 's#URL of the NATS service for self-managed clusters\. Required when ngcConfig\.clusterSource is "self-managed"\.#URL of the NATS service for self-managed clusters. Override with the endpoint generated during cluster registration.#' "${TARGET_DIR}/values.yaml" - replace_text '/"icmsServiceURL": {/,/}/ s#"description": ".*"#"description": "URL of the ICMS service for self-managed clusters. Override with the endpoint generated during cluster registration."#' "${TARGET_DIR}/values.schema.json" - replace_text 's#URL of the ReVal service for self-managed clusters\. Required when ngcConfig\.clusterSource is \\"self-managed\\"\.#URL of the ReVal service for self-managed clusters. Override with the endpoint generated during cluster registration.#' "${TARGET_DIR}/values.schema.json" - replace_text 's#URL of the NATS service for self-managed clusters\. Required when ngcConfig\.clusterSource is \\"self-managed\\"\.#URL of the NATS service for self-managed clusters. Override with the endpoint generated during cluster registration.#' "${TARGET_DIR}/values.schema.json" - replace_text '/"icmsServiceURL": {/,/}/ s#"default": ""#"default": "http://icms.example.invalid:8080"#' "${TARGET_DIR}/values.schema.json" - replace_text '/"revalServiceURL": {/,/}/ s#"default": ""#"default": "http://reval.example.invalid:8080"#' "${TARGET_DIR}/values.schema.json" - replace_text '/"natsURL": {/,/}/ s#"default": ""#"default": "nats://nats.example.invalid:4222"#' "${TARGET_DIR}/values.schema.json" - update_json_key '.properties.selfManaged.properties.icmsServiceHostHeaderOverride = {"type": "string", "description": "Optional Host header override for selfManaged.icmsServiceURL.", "default": ""}' "${TARGET_DIR}/values.schema.json" - update_json_key '.properties.selfManaged.properties.revalServiceHostHeaderOverride = {"type": "string", "description": "Optional Host header override for selfManaged.revalServiceURL.", "default": ""}' "${TARGET_DIR}/values.schema.json" - update_json_key '.properties.selfManaged.properties.natsHostOverride = {"type": "string", "description": "Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL.", "default": ""}' "${TARGET_DIR}/values.schema.json" - replace_text 's#^| `selfManaged.icmsServiceURL`.*#| `selfManaged.icmsServiceURL` | URL of the ICMS service for self-managed clusters. Override with the endpoint generated during cluster registration. | `http://icms.example.invalid:8080` |#' "${TARGET_DIR}/README.md" - replace_text 's#| `selfManaged.revalServiceURL` | URL of the ReVal service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". | `""` |#| `selfManaged.revalServiceURL` | URL of the ReVal service for self-managed clusters. Override with the endpoint generated during cluster registration. | `http://reval.example.invalid:8080` |#' "${TARGET_DIR}/README.md" - replace_text 's#| `selfManaged.natsURL` | URL of the NATS service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". | `""` |#| `selfManaged.natsURL` | URL of the NATS service for self-managed clusters. Override with the endpoint generated during cluster registration. | `nats://nats.example.invalid:4222` |#' "${TARGET_DIR}/README.md" - insert_after_line_once \ - '| `selfManaged.icmsServiceURL` | URL of the ICMS service for self-managed clusters. Override with the endpoint generated during cluster registration. | `http://icms.example.invalid:8080` |' \ - '| `selfManaged.icmsServiceHostHeaderOverride` | Optional Host header override for selfManaged.icmsServiceURL. | `""` |' \ - "${TARGET_DIR}/README.md" - insert_after_line_once \ - '| `selfManaged.revalServiceURL` | URL of the ReVal service for self-managed clusters. Override with the endpoint generated during cluster registration. | `http://reval.example.invalid:8080` |' \ - '| `selfManaged.revalServiceHostHeaderOverride` | Optional Host header override for selfManaged.revalServiceURL. | `""` |' \ - "${TARGET_DIR}/README.md" - insert_after_line_once \ - '| `selfManaged.natsURL` | URL of the NATS service for self-managed clusters. Override with the endpoint generated during cluster registration. | `nats://nats.example.invalid:4222` |' \ - '| `selfManaged.natsHostOverride` | Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL. | `""` |' \ - "${TARGET_DIR}/README.md" - insert_after_line_once \ - ' icmsServiceURL: {{ .Values.selfManaged.icmsServiceURL | quote }}' \ - ' {{- with .Values.selfManaged.icmsServiceHostHeaderOverride }}\n icmsServiceHostHeaderOverride: {{ . | quote }}\n {{- end }}' \ - "${TARGET_DIR}/templates/self-managed-nvcfbackend-cm.yaml" - insert_after_line_once \ - ' helmReValServiceURL: {{ .Values.selfManaged.revalServiceURL | quote }}' \ - ' {{- with .Values.selfManaged.revalServiceHostHeaderOverride }}\n helmReValServiceHostHeaderOverride: {{ . | quote }}\n {{- end }}' \ - "${TARGET_DIR}/templates/self-managed-nvcfbackend-cm.yaml" - insert_after_line_once \ - ' natsURL: {{ .Values.selfManaged.natsURL | quote }}' \ - ' {{- with .Values.selfManaged.natsHostOverride }}\n natsHostOverride: {{ . | quote }}\n {{- end }}' \ - "${TARGET_DIR}/templates/self-managed-nvcfbackend-cm.yaml" - insert_release_artifact_image_annotations "${TARGET_DIR}/templates/self-managed-nvcfbackend-cm.yaml" - update_yaml_key ".version = \"0.0.0\"" "${TARGET_DIR}/Chart.yaml" - update_yaml_key ".name = \"helm-nvca-operator\"" "${TARGET_DIR}/Chart.yaml" - # appVersion tracks the NVCA Operator binary version this chart deploys, - # not the chart's published semver (which CI sets from the git tag at - # release time). Without this overlay the upstream's stale "1.0" - # placeholder leaks through and helm releases report appVersion="1.0" - # regardless of which NVCA Operator the chart is actually wired to. - update_yaml_key ".appVersion = \"${NVCA_OPERATOR_VERSION:?"NVCA_OPERATOR_VERSION is not set"}\"" "${TARGET_DIR}/Chart.yaml" - update_yaml_key ".selfManaged.sharedStorage.imageTag = \"${NVCA_SHARED_STORAGE_IMAGE_TAG:?"NVCA_SHARED_STORAGE_IMAGE_TAG is not set"}\"" "${TARGET_DIR}/values.yaml" - # The BYOO otel collector sidecar's image tag moves independently of the - # monorepo source, driven by tools/chart-version-bumper against this - # vendored copy directly when byoo-otel-collector releases. Without this - # override the plain source copy above would carry it back to whatever - # the source chart still has, discarding that bump on the next vendor run. - update_yaml_key ".otelCollector.imageTag = \"${NVCA_OTEL_COLLECTOR_IMAGE_TAG:?"NVCA_OTEL_COLLECTOR_IMAGE_TAG is not set"}\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".agent.byooOtelCollector.imageTag = \"${NVCA_OTEL_COLLECTOR_IMAGE_TAG:?"NVCA_OTEL_COLLECTOR_IMAGE_TAG is not set"}\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".helmManaged.otelCollector.imageTag = \"${NVCA_OTEL_COLLECTOR_IMAGE_TAG:?"NVCA_OTEL_COLLECTOR_IMAGE_TAG is not set"}\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".selfManaged.otelCollector.imageTag = \"${NVCA_OTEL_COLLECTOR_IMAGE_TAG:?"NVCA_OTEL_COLLECTOR_IMAGE_TAG is not set"}\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".nameOverride = \"nvca-operator\"" "${TARGET_DIR}/values.yaml" - update_yaml_key ".fullnameOverride = \"nvca-operator\"" "${TARGET_DIR}/values.yaml" - - # Set default validation policy to Unrestricted for self-managed clusters - yq eval -i '.agentConfig.mergeConfig = "cluster:\n validationPolicy:\n name: Unrestricted" | .agentConfig.mergeConfig style="literal"' "${TARGET_DIR}/values.yaml" - - # Apply Apache-2.0 / NVIDIA copyright headers to all yaml files in the - # vendored chart. Skip files that already carry an SPDX marker so this - # step stays idempotent and does not duplicate headers. - echo "📝 [vendor] Applying license headers..." - apply_license_headers_if_missing "${TARGET_DIR}" - - echo "✅ [vendor] Successfully vendored NVCA Operator deployments (${SOURCE_LABEL}) to ${TARGET_DIR}/" - echo "📁 [vendor] Contents:" - ls -la "${TARGET_DIR}/" -} - -main "$@" diff --git a/deploy/helm/nvca-operator/tests/default_ownership_test.sh b/deploy/helm/nvca-operator/tests/default_ownership_test.sh index a9b8825331..b89bc28e13 100755 --- a/deploy/helm/nvca-operator/tests/default_ownership_test.sh +++ b/deploy/helm/nvca-operator/tests/default_ownership_test.sh @@ -15,8 +15,7 @@ fail() { } for schema in \ - "${chart}/values.schema.json" \ - "${chart_root}/../../../src/compute-plane-services/nvca/deployments/nvca-operator/values.schema.json"; do + "${chart}/values.schema.json"; do test "$(yq -r '.properties.selfManaged.properties.region.default' "${schema}")" = "us-west-1" || fail "${schema} does not declare the selfManaged.region default" test "$(yq -r '.properties.helmManaged.properties | has("region")' "${schema}")" = "false" || @@ -28,6 +27,7 @@ render() { shift helm template nvca-operator "${chart}" \ --namespace nvca-operator \ + --set-string "ngcConfig.serviceKey=test-service-key" \ --values "${chart}/values.yaml" \ --set-string ngcConfig.clusterSource=self-managed \ --set-string selfManaged.icmsServiceURL=http://icms.example.invalid:8080 \ diff --git a/deploy/helm/nvca-operator/tests/first_class_byoo_values_test.sh b/deploy/helm/nvca-operator/tests/first_class_byoo_values_test.sh index 2a5937dc2e..0ad6c926e9 100644 --- a/deploy/helm/nvca-operator/tests/first_class_byoo_values_test.sh +++ b/deploy/helm/nvca-operator/tests/first_class_byoo_values_test.sh @@ -21,6 +21,7 @@ render() { shift helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ --namespace nvca-operator \ --values "${repo_root}/nvca-operator/values.yaml" \ --values "${repo_root}/values.release-sbom.yaml" \ @@ -112,6 +113,7 @@ yq eval ' ' "${repo_root}/nvca-operator/values.yaml" > "${legacy_values}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ --namespace nvca-operator \ --values "${legacy_values}" \ --values "${repo_root}/values.release-sbom.yaml" \ diff --git a/deploy/helm/nvca-operator/tests/first_class_storage_worker_values_test.sh b/deploy/helm/nvca-operator/tests/first_class_storage_worker_values_test.sh index c1b5b9b301..57a6369c26 100755 --- a/deploy/helm/nvca-operator/tests/first_class_storage_worker_values_test.sh +++ b/deploy/helm/nvca-operator/tests/first_class_storage_worker_values_test.sh @@ -23,6 +23,7 @@ render() { shift helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ --namespace nvca-operator \ --values "${repo_root}/nvca-operator/values.yaml" \ --values "${repo_root}/values.release-sbom.yaml" \ @@ -166,6 +167,7 @@ yq eval ' ' "${repo_root}/nvca-operator/values.yaml" > "${legacy_values}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ --namespace nvca-operator \ --values "${legacy_values}" \ --values "${repo_root}/values.release-sbom.yaml" \ diff --git a/deploy/helm/nvca-operator/tests/image_pull_secret_defaults_test.sh b/deploy/helm/nvca-operator/tests/image_pull_secret_defaults_test.sh index 699bd686d0..36a5d8d0b3 100755 --- a/deploy/helm/nvca-operator/tests/image_pull_secret_defaults_test.sh +++ b/deploy/helm/nvca-operator/tests/image_pull_secret_defaults_test.sh @@ -29,13 +29,16 @@ manifest_disabled="${tmp_dir}/manifest-disabled.yaml" image_repository="stg.nvcr.io/nvidia/nvcf-byoc/nvca-operator" -# Render with vendored defaults. An ngc-managed install derives its registry -# credentials from ngcConfig.serviceKey, so the chart must generate the pull -# secret without the operator passing an extra flag. +# Render as an ngc-managed install does: the operator supplies its own NGC +# service key, and the chart generates the pull secret from it without any +# extra flag. The chart ships no default key, so the key is set here. helm template nvca-operator "${repo_root}/nvca-operator" \ --namespace nvca-operator \ --values "${repo_root}/nvca-operator/values.yaml" \ --set-string "image.repository=${image_repository}" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ > "${manifest_default}" # Render with the generated secret turned off, as self-hosted installs do when @@ -44,6 +47,8 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ --namespace nvca-operator \ --values "${repo_root}/nvca-operator/values.yaml" \ --set-string "image.repository=${image_repository}" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --set generateImagePullSecret=false \ > "${manifest_disabled}" diff --git a/deploy/helm/nvca-operator/tests/pod_disruption_budget_test.sh b/deploy/helm/nvca-operator/tests/pod_disruption_budget_test.sh index 6d90de6410..a80516205d 100644 --- a/deploy/helm/nvca-operator/tests/pod_disruption_budget_test.sh +++ b/deploy/helm/nvca-operator/tests/pod_disruption_budget_test.sh @@ -5,7 +5,6 @@ set -euo pipefail repo_root="$(cd "$(dirname "$0")/.." && pwd)" -source_chart="${repo_root}/../../../src/compute-plane-services/nvca/deployments/nvca-operator" vendored_chart="${repo_root}/nvca-operator" tmp_dir="$(mktemp -d)" test_service_key="test-service-key" @@ -27,12 +26,8 @@ assert_render_fails() { fi } -for chart in "${source_chart}" "${vendored_chart}"; do - if [[ "${chart}" == "${source_chart}" ]]; then - chart_name="source" - else - chart_name="vendored" - fi +for chart in "${vendored_chart}"; do + chart_name="vendored" default_manifest="${tmp_dir}/${chart_name}-default.yaml" reused_values_chart="${tmp_dir}/${chart_name}-reused-values" reused_values_manifest="${tmp_dir}/${chart_name}-reused-values.yaml" diff --git a/deploy/helm/nvca-operator/tests/self_managed_nvca_image_reference_test.sh b/deploy/helm/nvca-operator/tests/self_managed_nvca_image_reference_test.sh index 733ab35502..365d726b54 100755 --- a/deploy/helm/nvca-operator/tests/self_managed_nvca_image_reference_test.sh +++ b/deploy/helm/nvca-operator/tests/self_managed_nvca_image_reference_test.sh @@ -95,6 +95,9 @@ yq eval ' ' "${repo_root}/nvca-operator/values.yaml" > "${legacy_values}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --set-string ngcConfig.clusterSource=self-managed \ --values "${repo_root}/nvca-operator/values.yaml" \ @@ -108,6 +111,9 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ > "${manifest}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --set-string ngcConfig.clusterSource=self-managed \ --values "${repo_root}/nvca-operator/values.yaml" \ @@ -123,6 +129,9 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ > "${stage_manifest}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --set-string ngcConfig.clusterSource=self-managed \ --values "${repo_root}/nvca-operator/values.yaml" \ @@ -138,6 +147,9 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ > "${manifest_with_byoo_override}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --values "${legacy_values}" \ --set-string selfManaged.icmsServiceURL=http://sis.example.invalid:8080 \ @@ -149,6 +161,9 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ > "${legacy_stage_manifest}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --set-string ngcConfig.clusterSource=self-managed \ --values "${repo_root}/nvca-operator/values.yaml" \ @@ -163,6 +178,9 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ > "${manifest_without_byoo_default}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --set-string ngcConfig.clusterSource=self-managed \ --values "${repo_root}/nvca-operator/values.yaml" \ @@ -177,6 +195,9 @@ helm template nvca-operator "${repo_root}/nvca-operator" \ > "${manifest_without_nvca_image_override}" helm template nvca-operator "${repo_root}/nvca-operator" \ + --set-string "ngcConfig.serviceKey=test-service-key" \ + --set-string "nameOverride=nvca-operator" \ + --set-string "fullnameOverride=nvca-operator" \ --namespace nvca-operator \ --set-string ngcConfig.clusterSource=self-managed \ --values "${repo_root}/nvca-operator/values.yaml" \ diff --git a/deploy/helm/nvca-operator/tests/vendor_chart_image_tag_test.sh b/deploy/helm/nvca-operator/tests/vendor_chart_image_tag_test.sh deleted file mode 100755 index ce11dfbbb5..0000000000 --- a/deploy/helm/nvca-operator/tests/vendor_chart_image_tag_test.sh +++ /dev/null @@ -1,120 +0,0 @@ -#!/usr/bin/env bash -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -euo pipefail - -repo_root="$(cd "$(dirname "$0")/.." && pwd)" -workspace_root="$(cd "${repo_root}/../../.." && pwd)" -tmp_dir="$(mktemp -d)" -fixture_root="${tmp_dir}/fixture" - -cleanup() { - rm -rf "${tmp_dir}" -} -trap cleanup EXIT - -mkdir -p "${fixture_root}/deploy/helm" -cp -R "${repo_root}" "${fixture_root}/deploy/helm/nvca-operator" -mkdir -p "${fixture_root}/src/compute-plane-services/nvca/deployments" -cp -R "${workspace_root}/src/compute-plane-services/nvca/deployments/nvca-operator" \ - "${fixture_root}/src/compute-plane-services/nvca/deployments/nvca-operator" - -chart_root="${fixture_root}/deploy/helm/nvca-operator" -NVCA_OPERATOR_VERSION="3.2.11" \ -NVCA_VERSION="3.2.11" \ -NVCA_SHARED_STORAGE_IMAGE_TAG="1.0.5" \ -NVCA_OTEL_COLLECTOR_IMAGE_TAG="0.160.0-nv-0.2.4" \ - "${chart_root}/scripts/ci_vendor_nvca_operator_chart" >/dev/null - -actual_tag="$(yq -r '.image.tag' "${chart_root}/nvca-operator/values.yaml")" -if [[ -n "${actual_tag}" ]]; then - echo "expected vendored image.tag to remain empty, got ${actual_tag}" >&2 - exit 1 -fi - -# Operator, nvca-mirror, and the pre-delete cleanup job all render -# .Values.image.repository, so a single image reference is expected three -# times. clusterValidator renders a distinct repository (see -# nvcaop.clusterValidatorRepository) in two places: the deployment's -# initContainer and its own CronJob. Checking counts, not just presence, -# means a regression in any one of the five call sites fails the test -# instead of hiding behind the other four. -assert_image_count() { - local manifest="$1" expected_image="$2" expected_count="$3" label="$4" actual_count - # awk, not grep -F: grep matches by substring, so an unexpected tag like - # "3.2.11-hotfix" would still count as a match for "3.2.11" and mask a - # regression. Require the full image field to equal expected_image. - actual_count="$( - awk -v expected="${expected_image}" ' - $1 == "image:" && $2 == expected { count++ } - END { print count + 0 } - ' "${manifest}" - )" - if [[ "${actual_count}" -ne "${expected_count}" ]]; then - echo "expected ${expected_count} occurrence(s) of '${expected_image}' in the ${label} manifest, got ${actual_count}" >&2 - exit 1 - fi -} - -# Chart version deliberately differs from the operator version here, mirroring -# a chart-only republish (EGX_NVCA_OPERATOR_CHART_VERSION override in the -# byocdev job). The image tag must follow appVersion, not this field, or a -# chart-only republish would point at an operator image that was never built. -yq eval -i '.version = "3.2.11-chart-only-1"' "${chart_root}/nvca-operator/Chart.yaml" -manifest="${tmp_dir}/manifest.yaml" -helm template nvca-operator "${chart_root}/nvca-operator" \ - --set-string image.repository=registry.example.test/nvca-operator \ - --set clusterValidator.enabled=true \ - --set-string selfManaged.icmsServiceURL=http://icms.example.invalid:8080 \ - --set-string selfManaged.revalServiceURL=http://reval.example.invalid:8080 \ - --set-string selfManaged.natsURL=nats://nats.example.invalid:4222 \ - > "${manifest}" - -assert_image_count "${manifest}" "registry.example.test/nvca-operator:3.2.11" 3 "fresh" -assert_image_count "${manifest}" "nvcr.io/nvidia/nvcf-byoc/cluster-validator:3.2.11" 2 "fresh" -if grep -Fq ':3.2.11-chart-only-1' "${manifest}"; then - echo "an image fell back to the chart version instead of appVersion -- this is the reuse-values hazard this test guards against" >&2 - exit 1 -fi - -# Regression test: `helm upgrade --reuse-values` carries the -# previous release's fully-resolved values.yaml forward, but never the -# previous release's Chart.yaml. Simulating a prior release that reused an -# empty image.tag (the only value this chart ever ships) must still resolve -# through the new chart's appVersion, proving the fallback survives upgrades, -# not just fresh installs. -reused_values="${tmp_dir}/reused-values.yaml" -cat > "${reused_values}" <<'EOF' -image: - tag: "" -clusterValidator: - image: - tag: "" -EOF -reuse_manifest="${tmp_dir}/reuse-manifest.yaml" -helm template nvca-operator "${chart_root}/nvca-operator" \ - --set-string image.repository=registry.example.test/nvca-operator \ - --set clusterValidator.enabled=true \ - --set-string selfManaged.icmsServiceURL=http://icms.example.invalid:8080 \ - --set-string selfManaged.revalServiceURL=http://reval.example.invalid:8080 \ - --set-string selfManaged.natsURL=nats://nats.example.invalid:4222 \ - --values "${reused_values}" \ - > "${reuse_manifest}" - -assert_image_count "${reuse_manifest}" "registry.example.test/nvca-operator:3.2.11" 3 "reused" -assert_image_count "${reuse_manifest}" "nvcr.io/nvidia/nvcf-byoc/cluster-validator:3.2.11" 2 "reused" - -echo "vendor_chart_image_tag_test.sh keeps image.tag empty for appVersion fallback, including across --reuse-values upgrades" diff --git a/deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl b/deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl index 2210a55468..799d8b4bd1 100644 --- a/deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl +++ b/deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl @@ -84,7 +84,7 @@ helmDefaults: # install/upgrade). Without them, `helm uninstall` gets no --timeout flag at # all and falls back to Helm's 5m hook-wait default, well under the # nvca-operator pre-delete hook's ~10m worst-case cordon-and-drain budget - # (see deployments/nvca-operator/values.yaml gracefulShutdown.cleanupTimeoutSeconds). + # (see the nvca-operator chart values.yaml gracefulShutdown.cleanupTimeoutSeconds). deleteWait: true deleteTimeout: 900 wait: true @@ -273,6 +273,11 @@ releases: webhookConfig: imageConfig: pullPolicy: IfNotPresent + # The published chart is named helm-nvca-operator, so Helm would otherwise + # derive resource names from that. Pin them to the operator's own name so + # this stack and a direct nvca-operator install produce the same objects. + nameOverride: nvca-operator + fullnameOverride: nvca-operator {{- if $agentMergeConfig }} agentConfig: mergeConfig: | diff --git a/docs/dev/sdd-storage-agnostic-cache-architecture.md b/docs/dev/sdd-storage-agnostic-cache-architecture.md index c8628118d0..b088fc4cd3 100644 --- a/docs/dev/sdd-storage-agnostic-cache-architecture.md +++ b/docs/dev/sdd-storage-agnostic-cache-architecture.md @@ -239,8 +239,8 @@ No code change should be needed. If one is, the catalog is missing a fact. ## Source references -- [Catalog](https://github.com/NVIDIA/nvcf/blob/main/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml) -- [Catalog schema](https://github.com/NVIDIA/nvcf/blob/main/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json) +- [Catalog](https://github.com/NVIDIA/nvcf/blob/main/deploy/helm/nvca-operator/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml) +- [Catalog schema](https://github.com/NVIDIA/nvcf/blob/main/deploy/helm/nvca-operator/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json) - [Catalog loader and validator](https://github.com/NVIDIA/nvcf/blob/main/src/compute-plane-services/nvca/pkg/storage/storage_capabilities.go) - [Backend selection on main](https://github.com/NVIDIA/nvcf/blob/main/src/compute-plane-services/nvca/pkg/storage/cachebackend.go) - [Runtime work](https://github.com/NVIDIA/nvcf/issues/1326) diff --git a/src/clis/nvcf-cli/cmd/cluster_registration.go b/src/clis/nvcf-cli/cmd/cluster_registration.go index f589a53fee..6d803d1484 100644 --- a/src/clis/nvcf-cli/cmd/cluster_registration.go +++ b/src/clis/nvcf-cli/cmd/cluster_registration.go @@ -552,7 +552,7 @@ func registeredClusterIDs(resp *client.RegisterClusterResponse) (clusterGroupID, // Schema matches the nvca-operator chart's expected keys. clusterID, // clusterGroupID, and ncaID live at the top level with the mixed-case "ID" // suffix to match the `## @param` annotations in -// `deployments/nvca-operator/values.yaml` (e.g. `## @param clusterID`). Region +// `deploy/helm/nvca-operator/nvca-operator/values.yaml` (e.g. `## @param clusterID`). Region // lives under selfManaged because that is the value the self-managed backend // template consumes. identitySource is lifecycle metadata used by CLI teardown; // the chart deliberately ignores it. diff --git a/src/compute-plane-services/nvca/AGENTS.md b/src/compute-plane-services/nvca/AGENTS.md index f5083788ca..4215c82cd8 100644 --- a/src/compute-plane-services/nvca/AGENTS.md +++ b/src/compute-plane-services/nvca/AGENTS.md @@ -28,7 +28,7 @@ go test ./internal/miniservice/... \ - `internal/miniservice/` - MiniService controller - `internal/gc/` - Garbage collection cleaners - `internal/metrics/` - Prometheus metrics -- `deployments/nvca-operator/` - Operator Helm chart +- Operator Helm chart: `deploy/helm/nvca-operator/nvca-operator/` - `test/` - E2E and integration tests ## Dev Environment Tips @@ -214,7 +214,7 @@ internal/ - Private packages gc/ - Garbage collection metrics/ - Prometheus metrics util/ - Shared utilities -deployments/nvca-operator/ - Operator Helm chart +(chart lives in deploy/helm/nvca-operator/nvca-operator/) ``` ## Commit & PR Instructions diff --git a/src/compute-plane-services/nvca/BUILD.bazel b/src/compute-plane-services/nvca/BUILD.bazel index ac051be48d..1f606d7819 100644 --- a/src/compute-plane-services/nvca/BUILD.bazel +++ b/src/compute-plane-services/nvca/BUILD.bazel @@ -15,22 +15,12 @@ load("@gazelle//:def.bzl", "gazelle") -filegroup( - name = "storage-capability-catalog", - srcs = [ - "deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json", - "deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml", - ], - visibility = ["//src/compute-plane-services/nvca/pkg/storage:__pkg__"], -) - # gazelle:prefix github.com/NVIDIA/nvcf/src/compute-plane-services/nvca # gazelle:proto disable_global # gazelle:go_naming_convention import_alias # Non-Go top-level dirs. # gazelle:exclude docker -# gazelle:exclude deployments # gazelle:exclude examples # gazelle:exclude scripts # gazelle:exclude test diff --git a/src/compute-plane-services/nvca/README.md b/src/compute-plane-services/nvca/README.md index bc37d84efe..42cac44beb 100644 --- a/src/compute-plane-services/nvca/README.md +++ b/src/compute-plane-services/nvca/README.md @@ -45,7 +45,7 @@ Enable self-hosted on an already running cluster: ```bash helm upgrade nvca-operator -n nvca-operator --create-namespace -i --reset-values \ - ./deployments/nvca-operator \ + ../../../deploy/helm/nvca-operator/nvca-operator \ --set ngcConfig.serviceKey=${NGC_KEY} \ --set ngcConfig.clusterSource=self-managed \ --set selfManaged.nvcaVersion=${NVCA_VERSION} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/Chart.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/Chart.yaml deleted file mode 100644 index f3ea30b206..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/Chart.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v2 -appVersion: "1.0" -description: A Helm chart for the NVCF ClusterAgent Operator -name: nvca-operator -version: 0.0.1 \ No newline at end of file diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/README.md b/src/compute-plane-services/nvca/deployments/nvca-operator/README.md deleted file mode 100644 index 2d4607bf2b..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/README.md +++ /dev/null @@ -1,249 +0,0 @@ -# NVCA Operator Helm chart - -NVCF Cluster Agent (NVCA) Operator installs and manages reconfiguration, upgrades, and health checks of NVCA -used in Kubernetes Clusters to run NVCF Workloads. - -## Storage capability catalog - -The chart installs the versioned `nvcf-storage-capabilities` ConfigMap in the Helm release namespace. For each exact CSI provisioner the catalog records only the PVC access modes qualified end to end in an NVCF cache workflow, plus the mount options for reader volumes NVCA creates. Nothing about the flow is declared: NVCA derives it from those modes. An empty `accessModes` list means nothing is qualified yet, so caching stays off for that driver. Container cache is outside NVCA and is not part of this catalog. - -This release does not wire the catalog into backend selection. Runtime use requires a durable cache plan and safe legacy-request migration so retries and agent restarts cannot change backends. Managed deployments will inspect the exact `nvcf-sc` StorageClass when that follow-up is implemented. Editing this ConfigMap does not enable a storage backend today. - -## Parameters - -### NVCA Operator parameters - -| Name | Description | Value | -| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | -| `image.repository` | NVCA Operator container registry path, without tag | `nvcr.io/nvidia/nvcf-byoc/nvca-operator` | -| `image.tag` | NVCA Operator container image tag. This defaults to the chart's appVersion | `""` | -| `image.pullPolicy` | K8s ImagePullPolicy | `IfNotPresent` | -| `nvcaImage.repositoryOverride` | (Optional) Full NVCA container registry path, without tag. Only set this if the default needs to be overridden, for example "stg.nvcr.io/nvidia/nvcf-byoc/nvca". The tag is set in the cluster config | `""` | -| `nvcaImage.pullPolicy` | K8s ImagePullPolicy | `IfNotPresent` | - -### OTel Collector Configuration - -| Name | Description | Value | -| ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | -| `otelCollector.enabled` | Enable OTel collector sidecar for K8s event collection | `false` | -| `otelCollector.imageRepository` | (OPTIONAL) Image repository of OpenTelemetry Collector sidecar. If not specified, it will be calculated based on image.repository (stg vs prod). | `""` | -| `otelCollector.imageTag` | Image tag of OpenTelemetry Collector sidecar. | `0.160.0-nv-0.2.5` | -| `otelCollector.resources.limits.cpu` | CPU limit for the OTel collector container | `1000m` | -| `otelCollector.resources.limits.memory` | Memory limit for the OTel collector container | `1Gi` | -| `otelCollector.resources.requests.cpu` | CPU request for the OTel collector container | `200m` | -| `otelCollector.resources.requests.memory` | Memory request for the OTel collector container | `256Mi` | -| `generateImagePullSecret` | Use the ngcConfig.serviceKey to generate an image pull secret for nvca and nvca-operator Pods | `true` | -| `imagePullSecretName` | Name of the image pull secret to use for nvca and nvca-operator Pods. | `nvca-operator-image-pull` | -| `imagePullSecrets` | List of pre-existing imagePullSecret objects in the nvca-operator namespace to use for nvca and nvca-operator Pods. Each object must have a 'name' field. Example: [{name: "foo-bar"}, {name: "baz"}] | `[]` | -| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `true` | -| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` | -| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` | -| `replicaCount` | Replica count for the operator deployment | `1` | -| `systemNamespace` | Namespace in which NVCFBackend objects are created. | `nvca-operator` | -| `logLevel` | Logging level for the module | `info` | -| `ncaID` | (REQUIRED) NVIDIA Cloud Account ID of the Primary Account | `""` | -| `clusterID` | ID of the Cluster for this NVCA instance to manage | `""` | -| `clusterName` | for metrics & telemetry (REQUIRED when ngcConfig.clusterSource is "helm-managed") | `""` | -| `k8sVersionOverride` | Override the K8s version that NVCA registers with | `""` | -| `priorityClassName` | K8s PriorityClassName for NVCA pods preference during evictions | `""` | -| `nvcaHelmRepositoryPrefix` | Enables Helm repository restrictions to specific org/teams | `""` | -| `enableGXCache` | Enables GXCache Support in NVCA | `true` | -| `ddcsIPAllowList` | provides comma separated CIDR ranges to allowList | `""` | -| `byoo.resources` | Resource requests and limits for the BYOO OTel collector container. Leave unset to keep the agent's built-in defaults. | `{}` | -| `byoo.logChunking.maxPayloadBytes` | Maximum BYOO log chunk payload size in bytes. Zero disables the override. | `0` | -| `byoo.logChunking.dryRun` | Record chunking metrics without modifying log payloads. | `false` | -| `byoo.otelCollector` | Structured BYOO OTel collector rendering overrides. | `{}` | -| `byoo.additionalResourceOverhead` | Cluster capacity reserved for BYOO and related DaemonSet overhead. Leave unset to keep the agent's built-in defaults. | `{}` | -| `byoo.fluentbit.resources` | Resource requests and limits for the BYOO FluentBit container. Leave unset to keep the agent's built-in defaults. | `{}` | -| `utils.resources` | Resource requests and limits for the task pod's utils and init containers. Values are applied as both requests and limits. | See `values.yaml` | -| `storage.sharedStorage.server.image` | Image for the shared-storage pod's SMB container. Leave unset to keep the agent's built-in default. | `""` | -| `storage.sharedStorage.server.resources` | Resource requests and limits for the shared-storage pod's SMB container. Leave unset to keep the agent's built-in defaults. | `{}` | -| `storage.sharedStorage.taskData.storageClassName` | Storage class for task data. Falls back to ephemeral storage if unset. | `""` | -| `storage.sharedStorage.taskData.mountOptions` | Mount options for the provisioned task data PV. | `[]` | -| `storage.sharedStorage.taskData.storageCapacity` | Capacity of the provisioned task data volume. Leave unset to keep the agent's built-in default. | `""` | -| `storage.internalPersistentStorage.storageClassName` | Storage class for the Internal Persistent Storage (IPS) PVC. | `""` | -| `storage.internalPersistentStorage.hardResourceQuota` | Hard storage quota for the IPS PVC, keyed by resource name (e.g. storage). | `{}` | -| `worker.minHealthcheckRefreshWait` | Minimum wait between internal healthchecker refresh calls. Leave unset to keep the agent's built-in default. | `""` | -| `worker.staticGPUCapacity` | Static override of GPU capacity used for registration. Zero disables the override. | `0` | -| `worker.computeBackend` | Selects the compute backend. Leave unset to keep the agent's built-in default. | `""` | -| `worker.requestsNamespace` | Namespace NVCA creates request-scoped resources in. Leave unset to keep the agent's built-in default. | `""` | -| `worker.namespaceLabels` | Labels applied to namespaces NVCA creates. | `{}` | -| `worker.featureFlags` | Feature flags enabled on the NVCA agent. | `[]` | -| `worker.skipSelfDestruct` | Skip self-destruct even if ICMS sends SELF_DESTRUCT. | `false` | -| `worker.forceSelfDestruct` | Force self-destruct mode for testing. | `false` | -| `worker.csiVolumeMountOptions` | CSI PVC mount options for provisioned volumes. | `[]` | -| `worker.timeouts.credRenewInterval` | Interval between credential renewal attempts. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.heartbeatInterval` | Interval between agent heartbeats. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.syncQueueInterval` | Interval between queue sync polls. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.syncRequestStatusInterval` | Interval between request status syncs. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.syncAcknowledgeRequestInterval` | Interval between request acknowledgement syncs. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.periodicInstanceStatusInterval` | Interval between periodic instance status reports. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.icmsRequestAckInterval` | Interval between ICMS request acknowledgement polls. Leave unset to keep the agent's built-in default. | `""` | -| `worker.timeouts.icmsRequestAckRetryTimeout` | Timeout for retrying ICMS request acknowledgement. Leave unset to keep the agent's built-in default. | `""` | -| `agentConfig.mergeConfig` | Merge fields into the generated NVCA config. Deprecated for BYOO, storage, and worker settings; use `byoo`/`storage`/`worker` instead. | `""` | -| `operatorConfig.workload.transportTLS.trustBundle.secretKeyRef.name` | Secret containing the workload transport trust bundle; empty disables the source. Example: `nvcf-trust`. | `""` | -| `operatorConfig.workload.transportTLS.trustBundle.secretKeyRef.key` | Secret data key containing certificate-only PEM. | `ca.crt` | -| `operatorConfig.workload.transportTLS.fingerprint` | Optional SHA-256 pin; empty computes the Secret data fingerprint. | `""` | -| `operatorConfig.workload.transportTLS.installedBundleMountPath` | Optional `llm-worker` mount path for the installed transport trust bundle; empty uses `/etc/ssl/certs`. | `""` | - -### resources Resource requests and limits for the nvca-operator container - -| Name | Description | Value | -| --------------------------- | ---------------------------------------------- | ------- | -| `resources.limits.cpu` | CPU limit for the nvca-operator container | `500m` | -| `resources.limits.memory` | Memory limit for the nvca-operator container | `500Mi` | -| `resources.requests.cpu` | CPU request for the nvca-operator container | `50m` | -| `resources.requests.memory` | Memory request for the nvca-operator container | `50Mi` | - -### PodDisruptionBudget configuration - -| Name | Description | Value | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | ------- | -| `podDisruptionBudget.enabled` | Enable a PodDisruptionBudget for the NVCA Operator. | `false` | -| `podDisruptionBudget.minAvailable` | Minimum available pods. When enabled, set exactly one of this value or `maxUnavailable`. Supports an integer or 0-100%. | `""` | -| `podDisruptionBudget.maxUnavailable` | Maximum unavailable pods. When enabled, set exactly one of this value or `minAvailable`. Supports an integer or 0-100%. | `""` | - -### Agent Container Resource configuration - -| Name | Description | Value | -| ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | -| `agent.resources.limits.cpu` | CPU limit for the nvca agent container | `1000m` | -| `agent.resources.limits.memory` | Memory limit for the nvca agent container | `4Gi` | -| `agent.resources.requests.cpu` | CPU request for the nvca agent container | `100m` | -| `agent.resources.requests.memory` | Memory request for the nvca agent container | `200Mi` | -| `agent.cacheMountOptionsEnabled` | Enable or disable CSI volume mount options for NVCA caches | `true` | -| `agent.cacheMountOptions` | Comma-separated string of CSI volume mount options (e.g., "ro,noatime,nouuid") used when cacheMountOptionsEnabled is true | `ro,norecovery,nouuid` | -| `agent.workerDegradationPeriod` | Duration for determining if a worker is degraded (e.g., "90m", "1h30m") | `""` | -| `agent.secretMirrorNamespace` | Default namespace to mirror custom secrets for nvcf workloads | `nvca-operator` | -| `agent.secretMirrorLabelSelector` | Label selector on the secrets in the sourceNamespace | `""` | -| `agent.customAnnotations` | Map of custom annotations to add to the agent pod | `{}` | -| `agent.gpuProfiling.functionIds` | Comma/space/newline-separated NVCF function IDs (or "*" for all) whose pods NVCA labels for NVIDIA Nsight GPU profiling. Empty disables profiling. | `""` | -| `agent.gpuProfiling.labelKey` | Pod label key NVCA applies to profiled function pods (the label the Nsight Operator watches for). Empty uses the built-in default "nvidia-nsight-profile". | `""` | -| `agent.gpuProfiling.labelValue` | Pod label value NVCA applies to profiled function pods. Empty uses the built-in default "enabled". | `""` | -| `agent.byooOtelCollector.imageRepository` | Optional BYOO OpenTelemetry Collector image repository. If unset, it is derived from image.repository. | `""` | -| `agent.byooOtelCollector.imageTag` | BYOO OpenTelemetry Collector image tag. | `0.160.0-nv-0.2.5` | -| `agent.functionEnvOverrides` | Map of environment variable overrides for function workloads (e.g., {"INIT_CONTAINER": "nvcr.io/custom/init:v1.0", "UTILS_CONTAINER": "nvcr.io/custom/utils:v1.0"}) | `{}` | -| `agent.taskEnvOverrides` | Map of environment variable overrides for task workloads (e.g., {"INIT_CONTAINER": "nvcr.io/custom/init:v1.0", "ESS_AGENT_CONTAINER": "nvcr.io/custom/ess:v1.0"}) | `{}` | -| `agent.overrideEnvironmentVariables` | Map of environment variables to override on the NVCA agent container. These take precedence over default values. Example: {"LOG_LEVEL": "debug", "CUSTOM_FLAG": "enabled"} | `{}` | -| `agent.llm.requestRouterAddress` | Operator default LLM request-router address. Workers read LLM_REQUEST_ROUTER_ADDRESS from the launch environment; not a runtime fallback | `""` | -| `agent.serviceOAuth` | OAuth token and JWKS endpoints used by dependent services | See `values.yaml` | - -### Webhook Container Resource configuration - -| Name | Description | Value | -| ----------------------------------- | --------------------------------------------- | ------- | -| `webhook.resources.limits.memory` | Memory limit for the nvca webhook container | `200Mi` | -| `webhook.resources.requests.cpu` | CPU request for the nvca webhook container | `500m` | -| `webhook.resources.requests.memory` | Memory request for the nvca webhook container | `50Mi` | - -### NGC Configuration - -| Name | Description | Value | -| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -| `ngcConfig.username` | Username for the registry authentication | `$oauthtoken` | -| `ngcConfig.serviceKey` | ServiceKey (password) for authentication. If unset, a Secret with name set to ngcConfig.serviceKeySecretName is expected to exist in the cluster in the release namespace. | `""` | -| `ngcConfig.serviceKeySecretName` | Secret containing NGC ServiceKey (password) for authentication (default: ngc-service-key). If the ngcConfig.serviceKey is not set, the secret with this name must be created manually in the release namespace. | `ngc-service-key` | -| `ngcConfig.serviceKeySecretKeyName` | Key in the secret ngcConfig.serviceKeySecretName containing the NGC ServiceKey (password). | `ngcServiceKey` | -| `ngcConfig.apiURL` | NGC API URL for requesting auth tokens | `https://api.ngc.nvidia.com` | -| `ngcConfig.clusterSource` | Source of the cluster configuration: | `ngc-managed` | - -### Vault Configuration - -| Name | Description | Value | -| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | -| `vaultConfig.address` | Vault server URL for Helm-managed clusters. Required when `helmManaged.oAuthClientID` is set; must be HTTP(S), without credentials, query, or fragment. | `""` | -| `vaultConfig.oAuthClientMountPathTemplate` | Template for constructing the OAuth client mount path in Vault. Use %s as placeholder for clientID. Example: "nvidia/services/oauth/clients/%s/kv/secret" | `""` | -| `vaultConfig.oAuthClientMountPath` | (Optional) Full OAuth client mount path. If set, overrides the computed path from template. | `""` | - -### Helm Managed NVCF Backend Configuration - -| Name | Description | Value | -| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | -| `helmManaged.cloudProvider` | (REQUIRED) Cloud provider for the cluster (e.g., aws, gcp, azure) | `""` | -| `helmManaged.clusterRegion` | (REQUIRED) Region where the cluster is deployed | `""` | -| `helmManaged.clusterGroupID` | (REQUIRED) Group ID for the cluster | `""` | -| `helmManaged.clusterGroupName` | (REQUIRED) Name of the cluster group | `""` | -| `helmManaged.nvcaVersion` | (REQUIRED) Version of the NVCFBackend to use | `""` | -| `helmManaged.oAuthClientID` | (Optional) Client ID for OAuth2/OIDC authentication. Can be blank or omitted. | `""` | -| `helmManaged.oAuthClientSecretKey` | (Optional) Secret key to retrieve the client secret for OAuth2/OIDC client. Leave blank if not needed. | `""` | -| `helmManaged.clusterDescription` | (Optional) Description of the cluster. Defaults to clusterName if not provided. | `""` | -| `helmManaged.featureGateValues` | (Optional) List of feature gates to enable. Defaults to [] if not specified. | `[]` | -| `helmManaged.gpuManualInstanceConfigB64` | (Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required. | `""` | -| `helmManaged.clusterAttributes` | (Optional) List of attributes for the cluster. Defaults to an empty array. | `[]` | -| `helmManaged.imageCredHelper.imageRepository` | (OPTIONAL) Image repository of "nvcf-image-credential-helper". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. | `""` | -| `helmManaged.imageCredHelper.imageTag` | (REQUIRED) Image tag of "nvcf-image-credential-helper". Only override this if you know what you are doing. | `0.11.1` | -| `helmManaged.otelCollector.enabled` | Enable OTel collector sidecar for helm-managed clusters | `false` | -| `helmManaged.otelCollector.imageRepository` | (OPTIONAL) Image repository of "otel-collector". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. | `""` | -| `helmManaged.otelCollector.imageTag` | (REQUIRED) Image tag of "otel-collector". Only override this if you know what you are doing. | `0.160.0-nv-0.2.5` | - -### Self Managed NVCF Backend Configuration - -| Name | Description | Value | -| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | -| `selfManaged.nvcaVersion` | (REQUIRED) Version of the NVCFBackend to use | `""` | -| `selfManaged.featureGateValues` | (Optional) List of feature gates to enable. Defaults to ["DynamicGPUDiscovery"] if not specified. | `["DynamicGPUDiscovery"]` | -| `selfManaged.gpuManualInstanceConfigB64` | (Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required. | `""` | -| `selfManaged.clusterAttributes` | (Optional) List of attributes for the cluster. Defaults to an empty array. | `[]` | -| `selfManaged.region` | Region reported for a self-managed cluster. | `us-west-1` | -| `selfManaged.imageCredHelper.imageRepository` | (OPTIONAL) Image repository of "nvcf-image-credential-helper". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. | `""` | -| `selfManaged.imageCredHelper.imageTag` | (REQUIRED) Image tag of "nvcf-image-credential-helper". Only override this if you know what you are doing. | `0.11.1` | -| `selfManaged.otelCollector.enabled` | Enable OTel collector sidecar for self-managed clusters | `false` | -| `selfManaged.otelCollector.imageRepository` | (OPTIONAL) Image repository of "otel-collector". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. | `""` | -| `selfManaged.otelCollector.imageTag` | (REQUIRED) Image tag of "otel-collector". Only override this if you know what you are doing. | `0.160.0-nv-0.2.5` | -| `selfManaged.icmsServiceURL` | URL of the SIS/ICMS service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". | `""` | -| `selfManaged.icmsServiceHostHeaderOverride` | Optional Host header override for selfManaged.icmsServiceURL. | `""` | -| `selfManaged.revalServiceURL` | URL of the ReVal service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". | `""` | -| `selfManaged.revalServiceHostHeaderOverride` | Optional Host header override for selfManaged.revalServiceURL. | `""` | -| `selfManaged.natsURL` | URL of the NATS service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". | `""` | -| `selfManaged.natsHostOverride` | Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL. | `""` | - -### Node Selector Configuration - -| Name | Description | Value | -| -------------------- | ------------------------- | ---------------------------------- | -| `nodeSelector.key` | Node-selector Label key | `node.kubernetes.io/instance-type` | -| `nodeSelector.value` | Node-selector Label value | `""` | - -### OpenTelemetry configuration - -| Name | Description | Value | -| ---------------------------- | ----------------------------------------------------------- | ------- | -| `otel.enabled` | Enable OpenTelemetry. | `false` | -| `otel.lightstep.serviceName` | the name of the lightstep service to push telemetry data to | `""` | -| `otel.lightstep.accessToken` | the access token for accessing the lightstep API | `""` | - -### Graceful Shutdown Configuration - -| Name | Description | Value | -| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | ----- | -| `gracefulShutdown.terminationGracePeriodSeconds` | Maximum time (in seconds) for pod termination and cleanup (K8s hard limit) | `600` | -| `gracefulShutdown.cleanupTimeoutSeconds` | HTTP handler timeout (in seconds). Must be less than terminationGracePeriodSeconds to ensure response is sent before K8s kills pod. | `540` | - -### Network Policy Configuration - -| Name | Description | Value | -| ----------------------------------- | ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | -| `networkPolicy.clusterNetworkCIDRs` | List of IPv4 CIDRs that workload pods are NOT allowed to access (typically cluster-internal networks) | `["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","100.64.0.0/12"]` | -| `networkPolicy.customPolicies` | Array of custom network policy definitions to apply to nvca function namespaces | `[]` | - -### Cluster Validator Configuration - -| Name | Description | Value | -| ----------------------------------------------------- | --------------------------------------------------------------------------------- | -------------------------------------- | -| `clusterValidator.enabled` | Enable the cluster-validator CronJob and init container | `false` | -| `clusterValidator.image.repository` | Cluster Validator container registry path, without tag | `""` | -| `clusterValidator.image.tag` | Cluster Validator container image tag | `v2.0.0` | -| `clusterValidator.image.pullPolicy` | K8s ImagePullPolicy for cluster-validator | `IfNotPresent` | -| `clusterValidator.schedule` | CronJob schedule (cron expression) | `0 */3 * * *` | -| `clusterValidator.configMapName` | ConfigMap name for user-defined network checks | `cluster-validator-network-checks` | -| `clusterValidator.networkChecks` | Network check configuration (creates the ConfigMap automatically when set) | `{}` | -| `clusterValidator.networkChecks.reachability` | Reachability check config; when set, endpoints replace built-in checks | `{}` | -| `clusterValidator.networkChecks.networkPolicies` | Network policy validation config with namespace pairs | `{}` | -| `clusterValidator.networkChecks.enforcement` | Live enforcement testing config (deploys test pods) | `{}` | -| `clusterValidator.networkChecks.enforcement.enabled` | Enable live enforcement testing | `false` | -| `clusterValidator.networkChecks.enforcement.testImage`| Container image for enforcement test pods | `busybox:1.36` | -| `clusterValidator.networkChecks.enforcement.timeoutSeconds` | Timeout in seconds for enforcement test operations | `90` | -| `clusterValidator.resources.limits.cpu` | CPU limit for the cluster-validator container | `200m` | -| `clusterValidator.resources.limits.memory` | Memory limit for the cluster-validator container | `128Mi` | -| `clusterValidator.resources.requests.cpu` | CPU request for the cluster-validator container | `100m` | -| `clusterValidator.resources.requests.memory` | Memory request for the cluster-validator container | `64Mi` | diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json b/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json deleted file mode 100644 index 3e636a1d56..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.schema.json +++ /dev/null @@ -1,206 +0,0 @@ -{ - "$comment": "SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://nvcf.nvidia.com/schemas/storage-capability-catalog-v1alpha1.json", - "title": "NVCF storage capability catalog", - "description": "PVC access modes qualified per exact CSI provisioner. NVCA derives the cache flow from these modes; nothing about the flow is declared here.", - "type": "object", - "additionalProperties": false, - "required": [ - "apiVersion", - "kind", - "drivers" - ], - "properties": { - "apiVersion": { - "const": "storage.nvcf.nvidia.com/v1alpha1" - }, - "kind": { - "const": "StorageCapabilityCatalog" - }, - "drivers": { - "description": "One entry per exact CSI provisioner, named by that provisioner. Names must be unique; the loader rejects duplicates.", - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/driver" - } - } - }, - "$defs": { - "accessMode": { - "type": "string", - "enum": [ - "ReadWriteOnce", - "ReadOnlyMany", - "ReadWriteMany" - ] - }, - "driver": { - "type": "object", - "additionalProperties": false, - "required": [ - "name", - "provider", - "accessModes", - "readerMountOptions" - ], - "properties": { - "name": { - "description": "Exact StorageClass.provisioner string, the lookup key.", - "type": "string", - "minLength": 1, - "pattern": "\\S" - }, - "provider": { - "type": "string", - "minLength": 1, - "pattern": "\\S" - }, - "accessModes": { - "description": "Modes qualified end to end in an NVCF cache workflow, not modes the driver accepts. Empty means the driver is not enabled.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/$defs/accessMode" - } - }, - "readerMountOptions": { - "description": "Mount options for reader PVs NVCA creates. Only the ReadWriteOnce plus ReadOnlyMany shape creates them, and it must include \"ro\".", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/$defs/readerMountOption" - }, - "allOf": [ - { - "not": { - "allOf": [ - { - "contains": { - "const": "ro" - } - }, - { - "contains": { - "const": "rw" - } - } - ] - } - }, - { - "not": { - "allOf": [ - { - "contains": { - "const": "recovery" - } - }, - { - "contains": { - "const": "norecovery" - } - } - ] - } - }, - { - "not": { - "allOf": [ - { - "contains": { - "const": "uuid" - } - }, - { - "contains": { - "const": "nouuid" - } - } - ] - } - } - ] - }, - "encryptionSupported": { - "description": "An encrypted cache has been qualified on this driver. Absent means false. The ModelCacheEncryption feature flag decides whether to encrypt.", - "type": "boolean" - } - }, - "allOf": [ - { - "$comment": "A driver qualified for the ReadOnlyMany reader shape creates reader PVs, so it must mount them read-only.", - "if": { - "properties": { - "accessModes": { - "allOf": [ - { - "contains": { - "const": "ReadWriteOnce" - } - }, - { - "contains": { - "const": "ReadOnlyMany" - } - } - ] - } - }, - "required": [ - "accessModes" - ] - }, - "then": { - "properties": { - "readerMountOptions": { - "contains": { - "const": "ro" - } - } - } - } - }, - { - "$comment": "ReadOnlyMany describes readers. Without a writer mode alongside it there is nothing to populate the cache.", - "if": { - "properties": { - "accessModes": { - "contains": { - "const": "ReadOnlyMany" - } - } - }, - "required": [ - "accessModes" - ] - }, - "then": { - "properties": { - "accessModes": { - "anyOf": [ - { - "contains": { - "const": "ReadWriteOnce" - } - }, - { - "contains": { - "const": "ReadWriteMany" - } - } - ] - } - } - } - } - ] - }, - "readerMountOption": { - "type": "string", - "minLength": 1, - "pattern": "^\\S(?:.*\\S)?$" - } - } -} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml deleted file mode 100644 index dcc6f93a7e..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/files/nvcf-storage-capabilities-v1alpha1.yaml +++ /dev/null @@ -1,70 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# NVCA owns this catalog and installs it with the NVCA chart. Each entry is -# named by exact CSI provisioner and records the PVC access modes qualified end to end in an -# NVCF cache workflow. Nothing else is declared: NVCA derives how caching runs -# from these modes. -# -# ReadWriteMany one shared claim; readers mount it read-only -# ReadWriteOnce+ReadOnlyMany writer takes the claim; readers get their own -# -# An access mode a driver merely accepts is not a qualification. A claim that -# binds is not either. An empty accessModes list means nothing is qualified -# yet, so caching stays off for that driver, and a provisioner absent from this -# file is unsupported. Enabling a backend is an edit here, backed by a -# qualification run. -apiVersion: storage.nvcf.nvidia.com/v1alpha1 -kind: StorageCapabilityCatalog -drivers: - - name: nvmesh-csi.excelero.com - provider: nvmesh - # Encrypted caches are qualified on NVMesh, through a derived StorageClass - # and a per-sharing-domain Secret. - encryptionSupported: true - accessModes: - - ReadWriteOnce - - ReadOnlyMany - # The reader PV is XFS on the same filesystem as the writer, so it needs - # nouuid and norecovery or the mount fails outright. - readerMountOptions: - - ro - - norecovery - - nouuid - - name: csi.weka.io - provider: weka - # Weka is a shared filesystem: one ReadWriteMany claim per cache handle, - # populated once and mounted read-only by every reader, with no derived - # reader PV, so readerMountOptions stays empty. Enabled so the cache - # workflows can be exercised; record the qualification run in the pull - # request that flips this entry. - accessModes: - - ReadWriteMany - readerMountOptions: [] - - name: fss.csi.oraclecloud.com - provider: ociFss - # OCI File Storage is NFS: one ReadWriteMany claim per cache handle, - # populated once and mounted read-only by every reader, with no derived - # reader PV, so readerMountOptions stays empty. Enabled so the cache - # workflows can be exercised; record the qualification run in the pull - # request that flips this entry. - accessModes: - - ReadWriteMany - readerMountOptions: [] - - name: lustre.csi.oraclecloud.com - provider: ociLustre - # No PVC access mode has been qualified in an NVCF cache workflow. - accessModes: [] - readerMountOptions: [] diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/NOTES.txt b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/NOTES.txt deleted file mode 100644 index 6598e5bb7a..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/NOTES.txt +++ /dev/null @@ -1,141 +0,0 @@ -{{ $.Chart.Name }} has been installed. - -Version information: ------------------- -Chart: {{ $.Chart.Name }} v{{ $.Chart.Version }} -App Version: {{ $.Chart.AppVersion }} - -Release information: -------------------- -Name: {{ $.Release.Name }} -Namespace: {{ $.Release.Namespace }} -{{- if and .Values.imagePullSecrets (gt (len .Values.imagePullSecrets) 0) }} - -Image Pull Secrets: -------------------- -The following pre-existing secrets in the {{ $.Release.Namespace }} namespace will be used for pulling images: -{{- range .Values.imagePullSecrets }} -- name: {{ .name | quote }} -{{- end }} -Note: These secrets must exist in the {{ $.Release.Namespace }} namespace. -{{- end }} - -{{- if eq .Values.ngcConfig.clusterSource "helm-managed" }} -You've installed the NVCA Operator with "helm-managed" as the clusterSource. - -NVCA Configuration: -------------------- - -{{- $clusterGroupID := .Values.helmManaged.clusterGroupID | default .Values.clusterGroupID }} -{{- if and .Values.helmManaged.cloudProvider $clusterGroupID .Values.helmManaged.clusterGroupName .Values.clusterID .Values.clusterName .Values.helmManaged.nvcaVersion .Values.helmManaged.clusterRegion .Values.ncaID }} -A default NVCFBackend resource has been created with the following details: -- Cluster Name: {{ .Values.clusterName }} -- Cloud Provider: {{ .Values.helmManaged.cloudProvider }} -- Region: {{ .Values.helmManaged.clusterRegion }} - -The ResourceQuota limiting to a single NVCFBackend has been disabled. - -You can modify these settings by updating the following values in your Helm release: -- clusterName (REQUIRED) -- clusterID (REQUIRED) -- ncaID (REQUIRED) -- helmManaged.cloudProvider (REQUIRED) -- helmManaged.clusterRegion (REQUIRED) -- helmManaged.clusterGroupID (REQUIRED — falls back to top-level clusterGroupID if unset) -- helmManaged.clusterGroupName (REQUIRED) -- helmManaged.nvcaVersion (REQUIRED) -- helmManaged.oAuthClientID (optional - OAuth2/OIDC client ID, can be blank or omitted) -- helmManaged.oAuthClientSecretKey (optional - secret key name containing OAuth2/OIDC client secret) -- helmManaged.clusterDescription (optional - defaults to clusterName) -- helmManaged.featureGateValues (optional - list of feature gates to enable, defaults to ["LogPosting"]) -- helmManaged.clusterAttributes (optional - list of cluster attributes, defaults to empty array) -- helmManaged.gpuManualInstanceConfigB64 (optional - base64 encoded GPU manual instance configuration) - -To view the created NVCFBackend resource: - kubectl get nvcfbackend -n {{ $.Release.Namespace }} -{{- else }} -WARNING: NVCFBackend resource could not be created! - -The following required parameters were not provided: -{{- if not .Values.helmManaged.cloudProvider }} -- helmManaged.cloudProvider -{{- end }} -{{- if not $clusterGroupID }} -- helmManaged.clusterGroupID (or top-level clusterGroupID) -{{- end }} -{{- if not .Values.helmManaged.clusterGroupName }} -- helmManaged.clusterGroupName -{{- end }} -{{- if not .Values.clusterID }} -- clusterID -{{- end }} -{{- if not .Values.clusterName }} -- clusterName -{{- end }} -{{- if not .Values.helmManaged.nvcaVersion }} -- helmManaged.nvcaVersion -{{- end }} -{{- if not .Values.helmManaged.clusterRegion }} -- helmManaged.clusterRegion -{{- end }} -{{- if not .Values.ncaID }} -- ncaID -{{- end }} - -Please set these parameters and upgrade the helm release. -{{- end }} -{{- end }} - -{{- if eq .Values.ngcConfig.clusterSource "ngc-managed" }} -NVCA Configuration (NGC-Managed): --------------------------------- - -You've installed the NVCA Operator with "ngc-managed" as the clusterSource. -This means your cluster configuration will be managed by NGC. - -The operator will automatically fetch the cluster configuration from NGC. -You should verify that the NGC API URL, username, and service key are correctly set: - -- NGC API URL: {{ .Values.ngcConfig.apiURL }} -- NGC Username: {{ .Values.ngcConfig.username }} -- NGC Service Key: {{ if .Values.ngcConfig.serviceKey }}[Set]{{ else }}[Not Set]{{ end }} - -For upgrades or configuration changes, you can modify these settings through the NGC UI/API. -{{- end }} - -{{- if eq .Values.ngcConfig.clusterSource "self-managed" }} -NVCA Configuration (Self-Managed): ----------------------------------- - -You've installed the NVCA Operator with "self-managed" as the clusterSource. -This means your cluster configuration will be managed locally through Helm values. - -{{- if .Values.selfManaged.nvcaVersion }} -A default NVCFBackend resource has been created with the following details: -- Cluster Name: {{ .Values.clusterName | default "nvcf-default" }} -- NVCA Version: {{ .Values.selfManaged.nvcaVersion }} - -You can modify these settings by updating the following values in your Helm release: -- selfManaged.nvcaVersion (REQUIRED) -- clusterID (REQUIRED) -- clusterGroupID (REQUIRED) -- ncaID (Optional - defaults to empty) -- clusterName (Optional - defaults to "nvcf-default") - -To view the created NVCFBackend resource: - kubectl get nvcfbackend -n {{ $.Release.Namespace }} -{{- else }} -WARNING: NVCFBackend resource could not be created! - -The following required parameters were not provided: -- selfManaged.nvcaVersion - -Please set these parameters and upgrade the helm release. -{{- end }} -{{- end }} - -Useful commands: ---------------- - kubectl get all -n {{ $.Release.Namespace }} -l "app.kubernetes.io/instance={{ $.Release.Name }}" - helm status {{ $.Release.Name }} -n {{ $.Release.Namespace }} - helm get values {{ $.Release.Name }} -n {{ $.Release.Namespace }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/_helpers.tpl b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/_helpers.tpl deleted file mode 100644 index 7519499cb0..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/_helpers.tpl +++ /dev/null @@ -1,458 +0,0 @@ -{{/* -SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/}} -{{/* vim: set filetype=mustache: */}} -{{/* -Expand the name of the chart. -*/}} -{{- define "nvcaop.name" -}} -{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create a default fully qualified app name. -We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). -If release name contains chart name it will be used as a full name. -*/}} -{{- define "nvcaop.fullname" -}} -{{- if .Values.fullnameOverride -}} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- $name := default .Chart.Name .Values.nameOverride -}} -{{- if contains $name .Release.Name -}} -{{- .Release.Name | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "nvcaop.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Common labels -*/}} -{{- define "nvcaop.labels" -}} -helm.sh/chart: {{ include "nvcaop.chart" . }} -app.kubernetes.io/name: {{ include "nvcaop.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end -}} - -{{/* -Base selector labels, to use when defining component selector labels. -*/}} -{{- define "nvcaop.baseSelectorLabels" -}} -app.kubernetes.io/name: {{ include "nvcaop.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -ClusterName truncated at 32 chars -*/}} -{{- define "nvcaop.clustername" -}} -{{- .Values.clustername | trunc 32 | trimSuffix "-" -}} -{{- end -}} - -{{/* -Create the name of the service account to use -*/}} -{{- define "nvcaop.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "nvcaop.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} - -{{/* -Validate imagePullSecretName is specified when generateImagePullSecret is false -*/}} -{{- define "nvcaop.validateImagePullSecret" -}} -{{- if not .Values.generateImagePullSecret -}} -{{- if not .Values.imagePullSecretName -}} -{{- fail "imagePullSecretName must be specified when generateImagePullSecret is false" -}} -{{- end -}} -{{- end -}} -{{- end -}} - -{{/* -Reject transport trust settings that explicitly disable QUIC verification. -*/}} -{{- define "nvcaop.validateTransportTrust" -}} -{{- $agentConfig := .Values.agentConfig | default dict -}} -{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} -{{- $mergeConfig := dict -}} -{{- if $mergeConfigData -}} -{{- $mergeConfig = $mergeConfigData | fromYaml -}} -{{- end -}} -{{- $mergeWorkload := $mergeConfig.workload | default dict -}} -{{- $mergeTransportTLS := $mergeWorkload.transportTLS | default dict -}} -{{- $operatorConfig := .Values.operatorConfig | default dict -}} -{{- $operatorWorkload := $operatorConfig.workload | default dict -}} -{{- $operatorTransportTLS := $operatorWorkload.transportTLS | default dict -}} -{{- $trustBundle := $operatorTransportTLS.trustBundle | default dict -}} -{{- $secretKeyRef := $trustBundle.secretKeyRef | default dict -}} -{{- $bundleConfigured := or (eq ($mergeTransportTLS.trustMode | default "") "bundle") (ne ($secretKeyRef.name | default "") "") -}} -{{- if and ($mergeWorkload.stargateQUICInsecure | default false) $bundleConfigured -}} -{{- fail "workload.stargateQUICInsecure=true cannot be used with workload.transportTLS.trustMode=bundle; set workload.stargateQUICInsecure=false or use trustMode=system" -}} -{{- end -}} -{{- end -}} - -{{/* -Render the effective chart-owned agent configuration. Top-level byoo/utils/ -storage/worker values provide the supported API. agentConfig.mergeConfig -remains a legacy override and takes precedence for one minor-version -transition. -*/}} -{{- define "nvcaop.effectiveAgentConfig" -}} -{{- $byoo := .Values.byoo | default dict -}} -{{- $utils := .Values.utils | default dict -}} -{{- $storage := .Values.storage | default dict -}} -{{- $worker := .Values.worker | default dict -}} -{{- $agent := dict -}} -{{- with $byoo.resources }} -{{- $_ := set $agent "BYOOResources" . -}} -{{- end -}} -{{- with $byoo.logChunking }} -{{- $_ := set $agent "byooLogChunking" . -}} -{{- end -}} -{{- with $byoo.otelCollector }} -{{- $_ := set $agent "byooOtelCollector" . -}} -{{- end -}} -{{- with $byoo.additionalResourceOverhead }} -{{- $_ := set $agent "additionalResourceOverhead" . -}} -{{- end -}} -{{- with ($byoo.fluentbit | default dict).resources }} -{{- $_ := set $agent "BYOOFluentBitResources" . -}} -{{- end -}} -{{- with $utils.resources }} -{{- $_ := set $agent "UtilsResources" . -}} -{{- end -}} -{{- $sharedStorage := $storage.sharedStorage | default dict -}} -{{- $sharedStorageServer := dict -}} -{{- with $sharedStorage.server }} -{{- with .image }} -{{- $_ := set $sharedStorageServer "image" . -}} -{{- end -}} -{{- with .resources }} -{{- $_ := set $sharedStorageServer "containerResources" . -}} -{{- end -}} -{{- end -}} -{{- $sharedStorageTaskData := dict -}} -{{- with $sharedStorage.taskData }} -{{- with .storageClassName }} -{{- $_ := set $sharedStorageTaskData "storageClassName" . -}} -{{- end -}} -{{- with .mountOptions }} -{{- $_ := set $sharedStorageTaskData "pvMountOptions" . -}} -{{- end -}} -{{- with .storageCapacity }} -{{- $_ := set $sharedStorageTaskData "storageCapacity" . -}} -{{- end -}} -{{- end -}} -{{- $sharedStorageEffective := dict -}} -{{- if $sharedStorageServer }} -{{- $_ := set $sharedStorageEffective "server" $sharedStorageServer -}} -{{- end -}} -{{- if $sharedStorageTaskData }} -{{- $_ := set $sharedStorageEffective "taskData" $sharedStorageTaskData -}} -{{- end -}} -{{- if $sharedStorageEffective }} -{{- $_ := set $agent "sharedStorage" $sharedStorageEffective -}} -{{- end -}} -{{- $internalPersistentStorage := $storage.internalPersistentStorage | default dict -}} -{{- $ipsEffective := dict -}} -{{- with $internalPersistentStorage.storageClassName }} -{{- $_ := set $ipsEffective "storageClassName" . -}} -{{- end -}} -{{- with $internalPersistentStorage.hardResourceQuota }} -{{- $_ := set $ipsEffective "hardResourceQuota" . -}} -{{- end -}} -{{- if $ipsEffective }} -{{- $_ := set $agent "internalPersistentStorage" $ipsEffective -}} -{{- end -}} -{{- with $worker.minHealthcheckRefreshWait }} -{{- $_ := set $agent "minHealthcheckRefreshWait" . -}} -{{- end -}} -{{- with $worker.staticGPUCapacity }} -{{- $_ := set $agent "staticGPUCapacity" . -}} -{{- end -}} -{{- with $worker.computeBackend }} -{{- $_ := set $agent "computeBackend" . -}} -{{- end -}} -{{- with $worker.requestsNamespace }} -{{- $_ := set $agent "requestsNamespace" . -}} -{{- end -}} -{{- with $worker.namespaceLabels }} -{{- $_ := set $agent "namespaceLabels" . -}} -{{- end -}} -{{- with $worker.featureFlags }} -{{- $_ := set $agent "featureFlags" . -}} -{{- end -}} -{{- with $worker.skipSelfDestruct }} -{{- $_ := set $agent "skipSelfDestruct" . -}} -{{- end -}} -{{- with $worker.forceSelfDestruct }} -{{- $_ := set $agent "forceSelfDestruct" . -}} -{{- end -}} -{{- with $worker.csiVolumeMountOptions }} -{{- $_ := set $agent "csiVolumeMountOptions" . -}} -{{- end -}} -{{- $timeouts := $worker.timeouts | default dict -}} -{{- with $timeouts.credRenewInterval }} -{{- $_ := set $agent "credRenewInterval" . -}} -{{- end -}} -{{- with $timeouts.heartbeatInterval }} -{{- $_ := set $agent "heartbeatInterval" . -}} -{{- end -}} -{{- with $timeouts.syncQueueInterval }} -{{- $_ := set $agent "syncQueueInterval" . -}} -{{- end -}} -{{- with $timeouts.syncRequestStatusInterval }} -{{- $_ := set $agent "syncRequestStatusInterval" . -}} -{{- end -}} -{{- with $timeouts.syncAcknowledgeRequestInterval }} -{{- $_ := set $agent "syncAcknowledgeRequestInterval" . -}} -{{- end -}} -{{- with $timeouts.periodicInstanceStatusInterval }} -{{- $_ := set $agent "periodicInstanceStatusInterval" . -}} -{{- end -}} -{{- with $timeouts.icmsRequestAckInterval }} -{{- $_ := set $agent "icmsRequestAckInterval" . -}} -{{- end -}} -{{- with $timeouts.icmsRequestAckRetryTimeout }} -{{- $_ := set $agent "icmsRequestAckRetryTimeout" . -}} -{{- end -}} -{{- $config := dict -}} -{{- if $agent }} -{{- $_ := set $config "agent" $agent -}} -{{- end -}} -{{- $agentConfig := .Values.agentConfig | default dict -}} -{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} -{{- if $mergeConfigData }} -{{- $parsedMergeConfig := $mergeConfigData | fromYaml -}} -{{- if hasKey $parsedMergeConfig "Error" -}} -{{- fail (printf "agentConfig.mergeConfig contains invalid YAML: %s" $parsedMergeConfig.Error) -}} -{{- end -}} -{{- $config = mergeOverwrite $config ($parsedMergeConfig | default dict) -}} -{{- end -}} -{{- $finalAgent := $config.agent | default dict -}} -{{- if and $finalAgent.skipSelfDestruct $finalAgent.forceSelfDestruct -}} -{{- fail "worker.skipSelfDestruct and worker.forceSelfDestruct cannot both be true (including via agentConfig.mergeConfig); NVCA rejects this combination at startup" -}} -{{- end -}} -{{- $config | toYaml -}} -{{- end -}} - -{{/* -Keys under agentConfig.mergeConfig's "agent" map that now have first-class -chart values. Used to detect legacy overrides so the operator can emit a -source-aware migration warning. -*/}} -{{- define "nvcaop.firstClassAgentConfigKeys" -}} -{{- list "additionalResourceOverhead" "UtilsResources" "sharedStorage" "internalPersistentStorage" "minHealthcheckRefreshWait" "staticGPUCapacity" "computeBackend" "requestsNamespace" "namespaceLabels" "featureFlags" "skipSelfDestruct" "forceSelfDestruct" "csiVolumeMountOptions" "credRenewInterval" "heartbeatInterval" "syncQueueInterval" "syncRequestStatusInterval" "syncAcknowledgeRequestInterval" "periodicInstanceStatusInterval" "icmsRequestAckInterval" "icmsRequestAckRetryTimeout" | toYaml -}} -{{- end -}} - -{{/* -Return true when legacy agentConfig.mergeConfig configures any field that now -has a first-class chart value (BYOO, utils, storage, or worker). The ConfigMap -annotation lets the operator emit a source-aware migration warning. -*/}} -{{- define "nvcaop.usesLegacyFirstClassConfig" -}} -{{- $agentConfig := .Values.agentConfig | default dict -}} -{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} -{{- $legacyFirstClass := false -}} -{{- if $mergeConfigData }} -{{- $firstClassKeys := include "nvcaop.firstClassAgentConfigKeys" . | fromYamlArray -}} -{{- $config := $mergeConfigData | fromYaml | default dict -}} -{{- $agent := $config.agent | default dict -}} -{{- range $key, $_ := $agent }} -{{- if or (hasPrefix "byoo" (lower $key)) (has $key $firstClassKeys) }} -{{- $legacyFirstClass = true -}} -{{- end -}} -{{- end -}} -{{- end -}} -{{- $legacyFirstClass -}} -{{- end -}} - -{{/* -ImagePullSecret for images. -*/}} -{{- define "nvcaop.generatedImagePullSecret" }} -{{- $username := .Values.ngcConfig.username }} -{{- $serviceKey := .Values.ngcConfig.serviceKey | required "NGC service key is required to create a pull secret" }} -{{- $auths := dict }} -{{- $auth := dict "username" $username "password" $serviceKey "auth" (printf "%s:%s" $username $serviceKey | b64enc) }} -{{- range $i, $repo := (list .Values.image.repository .Values.nvcaImage.repositoryOverride) }} -{{- if $repo }} -{{- $_ := set $auths (splitList "/" $repo | first) $auth }} -{{- end }} -{{- end }} -{{- printf "{\"auths\":%s}" ($auths | toJson) | b64enc }} -{{- end }} - -{{/* -Convert list to JSON if non-empty otherwise return [] -Usage: {{ include "nvcaop.jsonListOrEmpty" }} -*/}} -{{- define "nvcaop.jsonListOrEmpty" -}} -{{- $l := (default (list) .) | compact -}} -{{- if gt (len $l) 0 -}} -{{ $l | toJson }} -{{- else -}} -[] -{{- end -}} -{{- end -}} - -{{/* -Get the imageCredHelper repository based on image.repository -If imageRepository is explicitly set, use it. Otherwise, calculate it based on image.repository prefix. -Usage: {{ include "nvcaop.imageCredHelperRepository" (dict "imageRepository" .Values.helmManaged.imageCredHelper.imageRepository "defaultRepository" .Values.image.repository) }} -*/}} -{{- define "nvcaop.imageCredHelperRepository" -}} -{{- if .imageRepository -}} -{{- .imageRepository -}} -{{- else if hasPrefix "stg.nvcr.io/nvidia/nvcf-byoc" .defaultRepository -}} -stg.nvcr.io/nvidia/nvcf-byoc/nvcf-image-credential-helper -{{- else -}} -nvcr.io/nvidia/nvcf-byoc/nvcf-image-credential-helper -{{- end -}} -{{- end -}} - -{{/* -Get the OTel collector repository based on image.repository -If imageRepository is explicitly set, use it. Otherwise, calculate it based on image.repository prefix. -Usage: {{ include "nvcaop.otelCollectorRepository" (dict "imageRepository" .Values.otelCollector.imageRepository "defaultRepository" .Values.image.repository) }} -*/}} -{{- define "nvcaop.otelCollectorRepository" -}} -{{- if .imageRepository -}} -{{- .imageRepository -}} -{{- else if hasPrefix "stg.nvcr.io/nvidia/nvcf-byoc" .defaultRepository -}} -stg.nvcr.io/nvidia/nvcf-byoc/nvcf-otel-collector -{{- else -}} -nvcr.io/nvidia/nvcf-byoc/nvcf-otel-collector -{{- end -}} -{{- end -}} - -{{/* -Get the BYOO OTel collector repository based on image.repository and -ngcConfig.clusterSource. If imageRepository is explicitly set, use it. -Otherwise, staging resolves by image.repository prefix, and production -resolves by cluster source: BYOC (ngc-managed) clusters authenticate image -pulls with the NGC Cluster Key issued at registration, which is scoped to -nvcf-core, not nvidia/nvcf-byoc (see NVIDIA/nvcf#798 and the follow-up fix -for the staging repository in NVIDIA/nvcf#1040). Other cluster sources keep -the nvidia/nvcf-byoc default pending their own verification. -Usage: {{ include "nvcaop.byooOtelCollectorImage" . }} -*/}} -{{- define "nvcaop.byooOtelCollectorRepository" -}} -{{- if .imageRepository -}} -{{- .imageRepository -}} -{{- else if hasPrefix "stg.nvcr.io/nvidia/nvcf-byoc" .defaultRepository -}} -stg.nvcr.io/nv-cf/nvcf-core/byoo-otel-collector -{{- else if eq .clusterSource "ngc-managed" -}} -nvcr.io/qtfpt1h0bieu/nvcf-core/byoo-otel-collector -{{- else -}} -nvcr.io/nvidia/nvcf-byoc/byoo-otel-collector -{{- end -}} -{{- end -}} - -{{/* -Get the BYOO OTel collector image when its tag is configured. -*/}} -{{- define "nvcaop.byooOtelCollectorImage" -}} -{{- $agent := .Values.agent | default dict -}} -{{- $byooOtelCollector := $agent.byooOtelCollector | default dict -}} -{{- $imageTag := "0.160.0-nv-0.2.5" -}} -{{- if hasKey $byooOtelCollector "imageTag" -}} -{{- $imageTag = $byooOtelCollector.imageTag -}} -{{- end -}} -{{- if $imageTag -}} -{{- printf "%s:%s" (include "nvcaop.byooOtelCollectorRepository" (dict "imageRepository" ($byooOtelCollector.imageRepository | default "") "defaultRepository" .Values.image.repository "clusterSource" ((.Values.ngcConfig).clusterSource | default "ngc-managed"))) $imageTag -}} -{{- end -}} -{{- end -}} - -{{/* -Check if cluster validator is enabled (nil-safe). -Returns non-empty string if enabled, empty string if disabled. -Usage: {{- if (include "nvcaop.clusterValidatorEnabled" .) -}} -*/}} -{{- define "nvcaop.clusterValidatorEnabled" -}} -{{- $cv := .Values.clusterValidator | default dict -}} -{{- if ($cv.enabled | default false) -}}true{{- end -}} -{{- end -}} - -{{/* -Cluster validator config with chart defaults merged in. -Returns YAML; callers `| fromYaml` it and dereference safely. - -Defends against `helm upgrade --reuse-values` from a release created before -clusterValidator existed: in that case the stored values have no -clusterValidator.image / .resources / .schedule sections, and Helm does not -fall back to the new chart's values.yaml defaults for absent keys. Without -this helper, deployment.yaml / cronjob.yaml hit a nil pointer when -dereferencing .Values.clusterValidator.image.repository. - -`merge` keeps existing user values when present and only fills in defaults -for absent keys, so explicit overrides are preserved. - -Usage: {{- $cv := include "nvcaop.clusterValidatorConfig" . | fromYaml -}} -*/}} -{{- define "nvcaop.clusterValidatorConfig" -}} -{{- /* Defaults must mirror values.yaml so a --reuse-values upgrade gets - exactly the same effective config as a fresh install. */ -}} -{{- $defaults := dict - "image" (dict "repository" "" "tag" "" "pullPolicy" "IfNotPresent") - "schedule" "0 */3 * * *" - "configMapName" "cluster-validator-network-checks" - "networkChecks" (dict) - "resources" (dict - "requests" (dict "cpu" "100m" "memory" "64Mi") - "limits" (dict "cpu" "200m" "memory" "128Mi")) --}} -{{- /* `merge (dict) user defaults` writes into a fresh empty dict so - .Values.clusterValidator is never mutated in-place. Sprig's - `merge dst src...` modifies dst; if dst were $cv directly, the - default keys would be written back into .Values.clusterValidator - and any other template that reads .Values directly after the - first render would see merged-in defaults instead of original - user values. */ -}} -{{- $user := .Values.clusterValidator | default dict -}} -{{- merge (dict) $user $defaults | toYaml -}} -{{- end -}} - -{{/* -Get the cluster-validator repository based on image.repository -If imageRepository is explicitly set, use it. Otherwise, calculate it based on image.repository prefix. -Usage: {{ include "nvcaop.clusterValidatorRepository" (dict "imageRepository" .Values.clusterValidator.image.repository "defaultRepository" .Values.image.repository) }} -*/}} -{{- define "nvcaop.clusterValidatorRepository" -}} -{{- if .imageRepository -}} -{{- .imageRepository -}} -{{- else if hasPrefix "stg.nvcr.io/nvidia/nvcf-byoc" .defaultRepository -}} -stg.nvcr.io/nvidia/nvcf-byoc/cluster-validator -{{- else -}} -nvcr.io/nvidia/nvcf-byoc/cluster-validator -{{- end -}} -{{- end -}} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/agent-config-merge-cm.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/agent-config-merge-cm.yaml deleted file mode 100644 index 14e947873a..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/agent-config-merge-cm.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- $effectiveConfig := include "nvcaop.effectiveAgentConfig" . | fromYaml }} -{{- if $effectiveConfig }} -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: agent-config-merge - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - {{- if eq (include "nvcaop.usesLegacyFirstClassConfig" .) "true" }} - annotations: - nvcf.nvidia.com/legacy-first-class-config: "true" - {{- end }} -data: - config.yaml: | -{{ include "nvcaop.effectiveAgentConfig" . | nindent 4 }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/chart-defaults-nvcfbackend-cm.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/chart-defaults-nvcfbackend-cm.yaml deleted file mode 100644 index 3fe903f4d7..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/chart-defaults-nvcfbackend-cm.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvcfbackend-chart-defaults - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -data: - {{- $serviceOAuth := ((.Values.agent).serviceOAuth) | default dict }} - {{- $helmReVal := $serviceOAuth.helmReVal | default dict }} - {{- $helmReValStage := $helmReVal.stage | default dict }} - {{- $helmReValProd := $helmReVal.prod | default dict }} - {{- $fds := $serviceOAuth.functionDeploymentStages | default dict }} - {{- $fdsStage := $fds.stage | default dict }} - {{- $fdsProd := $fds.prod | default dict }} - cluster-dto.yaml: | - agent: - helmReValStageOAuthTokenURL: {{ $helmReValStage.tokenURL | default "" | quote }} - helmReValStageOAuthPublicKeysetEndpoint: {{ $helmReValStage.publicKeysetEndpoint | default "" | quote }} - helmReValProdOAuthTokenURL: {{ $helmReValProd.tokenURL | default "" | quote }} - helmReValProdOAuthPublicKeysetEndpoint: {{ $helmReValProd.publicKeysetEndpoint | default "" | quote }} - functionDeploymentStagesStageOAuthTokenURL: {{ $fdsStage.tokenURL | default "" | quote }} - functionDeploymentStagesStageOAuthPublicKeysetEndpoint: {{ $fdsStage.publicKeysetEndpoint | default "" | quote }} - functionDeploymentStagesProdOAuthTokenURL: {{ $fdsProd.tokenURL | default "" | quote }} - functionDeploymentStagesProdOAuthPublicKeysetEndpoint: {{ $fdsProd.publicKeysetEndpoint | default "" | quote }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/cluster-validator-network-checks-cm.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/cluster-validator-network-checks-cm.yaml deleted file mode 100644 index c46e9705a8..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/cluster-validator-network-checks-cm.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{{- if (include "nvcaop.clusterValidatorEnabled" .) }} -{{- $cv := include "nvcaop.clusterValidatorConfig" . | fromYaml }} ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ $cv.configMapName }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: validation -data: - config.yaml: | - {{- $cv.networkChecks | toYaml | nindent 4 }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/crds/nvidia.io_nvcfbackends_crd.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/crds/nvidia.io_nvcfbackends_crd.yaml deleted file mode 100644 index cb0d433ab8..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/crds/nvidia.io_nvcfbackends_crd.yaml +++ /dev/null @@ -1,60 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: nvcfbackends.nvcf.nvidia.io - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -spec: - group: nvcf.nvidia.io - names: - kind: NVCFBackend - listKind: NVCFBackendList - plural: nvcfbackends - singular: nvcfbackend - scope: Namespaced - versions: - - name: v1 - additionalPrinterColumns: - - name: Age - jsonPath: .metadata.creationTimestamp - description: Age of this resource - type: date - - name: Version - jsonPath: .status.version - type: string - description: Current version of the backend - - name: Health - jsonPath: .status.agentStatus - description: Health status of the backend - type: string - schema: - openAPIV3Schema: - properties: - spec: - type: object - x-kubernetes-preserve-unknown-fields: true - status: - type: object - x-kubernetes-preserve-unknown-fields: true - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/cronjob.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/cronjob.yaml deleted file mode 100644 index aff4ba0a5a..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/cronjob.yaml +++ /dev/null @@ -1,107 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{{- if (include "nvcaop.clusterValidatorEnabled" .) }} -{{- $cv := include "nvcaop.clusterValidatorConfig" . | fromYaml }} ---- -apiVersion: batch/v1 -kind: CronJob -metadata: - name: {{ include "nvcaop.fullname" . }}-cluster-validator - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: validation -spec: - schedule: {{ $cv.schedule | quote }} - concurrencyPolicy: Forbid - successfulJobsHistoryLimit: 3 - failedJobsHistoryLimit: 3 - startingDeadlineSeconds: 300 - jobTemplate: - metadata: - labels: - {{- include "nvcaop.baseSelectorLabels" . | nindent 8 }} - app.kubernetes.io/component: validation - spec: - parallelism: 1 - completions: 1 - backoffLimit: 2 - activeDeadlineSeconds: 600 - template: - metadata: - labels: - {{- include "nvcaop.baseSelectorLabels" . | nindent 12 }} - app.kubernetes.io/component: validation - spec: - serviceAccountName: {{ include "nvcaop.fullname" . }}-cluster-validator - automountServiceAccountToken: true - restartPolicy: Never - securityContext: - runAsUser: 65534 - runAsGroup: 65534 - fsGroup: 65534 - {{- if or .Values.generateImagePullSecret (gt (len .Values.imagePullSecrets) 0) }} - imagePullSecrets: - {{- if .Values.generateImagePullSecret }} - - name: {{ (.Values.imagePullSecretName) | default "nvca-operator-image-pull" | quote }} - {{- end }} - {{- range .Values.imagePullSecrets }} - - name: {{ .name | quote }} - {{- end }} - {{- end }} - containers: - - name: cluster-validator - image: {{ include "nvcaop.clusterValidatorRepository" (dict "imageRepository" $cv.image.repository "defaultRepository" .Values.image.repository) }}:{{ default .Chart.AppVersion $cv.image.tag }} - imagePullPolicy: {{ $cv.image.pullPolicy }} - env: - - name: VALIDATOR_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: VALIDATOR_CONFIG_NAME - value: {{ $cv.configMapName | quote }} - # Namespace the NVCA agent watches for the metrics summary; kept - # separate from the config namespace so a config-namespace - # override can't redirect metrics. - - name: VALIDATOR_SUMMARY_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - resources: - requests: - cpu: {{ $cv.resources.requests.cpu | quote }} - memory: {{ $cv.resources.requests.memory | quote }} - limits: - cpu: {{ $cv.resources.limits.cpu | quote }} - memory: {{ $cv.resources.limits.memory | quote }} - securityContext: - runAsNonRoot: true - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - capabilities: - drop: ["ALL"] - tolerations: - - key: node-role.kubernetes.io/control-plane - operator: Exists - effect: NoSchedule - - key: node-role.kubernetes.io/master - operator: Exists - effect: NoSchedule - {{- if .Values.nodeSelector.value }} - nodeSelector: - {{ .Values.nodeSelector.key }}: {{ .Values.nodeSelector.value }} - {{- end }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/custom-annotations-configmap.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/custom-annotations-configmap.yaml deleted file mode 100644 index da9effec71..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/custom-annotations-configmap.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvca-namespace-pod-annotations - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -data: -{{- if ((.Values.agent).customAnnotations) }} -{{- $annotations := .Values.agent.customAnnotations }} -{{- if kindIs "string" $annotations }} -{{- $annotations = fromYaml $annotations }} -{{- end }} - annotations: | - {{- $annotations | toJson | nindent 4 }} -{{- else }} - annotations: "{}" -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/custom-network-policies-configmap.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/custom-network-policies-configmap.yaml deleted file mode 100644 index f666736935..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/custom-network-policies-configmap.yaml +++ /dev/null @@ -1,37 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvcf-custom-network-policies - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -data: -{{- $networkPolicy := get .Values "networkPolicy" | default dict }} -{{- $customPolicies := get $networkPolicy "customPolicies" | default list }} -{{- if $customPolicies }} -{{- range $index, $policy := $customPolicies }} -{{- if not (empty $policy) }} - nvcf-custom-{{ $index }}: | -{{- if kindIs "string" $policy }} - {{- $policy | nindent 4 }} -{{- else }} - {{- $policy | toYaml | nindent 4 }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/deployment.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/deployment.yaml deleted file mode 100644 index 4f5f0d5514..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/deployment.yaml +++ /dev/null @@ -1,349 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "nvcaop.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -spec: - {{- include "nvcaop.validateImagePullSecret" . | nindent 2 }} - selector: - matchLabels: - {{- include "nvcaop.baseSelectorLabels" . | nindent 6 }} - replicas: {{ .Values.replicaCount }} - template: - metadata: - labels: - {{- include "nvcaop.baseSelectorLabels" . | nindent 8 }} - spec: - serviceAccountName: {{ include "nvcaop.serviceAccountName" . }} - automountServiceAccountToken: true - {{- if or .Values.generateImagePullSecret (gt (len .Values.imagePullSecrets) 0) }} - imagePullSecrets: - {{- if .Values.generateImagePullSecret }} - - name: {{ (.Values.imagePullSecretName) | default "nvca-operator-image-pull" | quote }} - {{- end }} - {{- range .Values.imagePullSecrets }} - - name: {{ .name | quote }} - {{- end }} - {{- end }} - securityContext: - runAsUser: 1000 - runAsGroup: 1010 - fsGroup: 1010 - # Defense in depth: enforce non-root + seccomp at the pod - # level. runAsUser=1000 already guarantees non-root; setting - # runAsNonRoot=true makes the constraint explicit (kubelet - # will reject the pod if the image's USER somehow goes back - # to root). seccompProfile=RuntimeDefault blocks the - # ~40 syscalls Docker's default seccomp profile blocks - # (clock_settime, modify_ldt, …) — safe for any normal Go - # operator. - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName }} - {{- end}} - {{- if .Values.nodeSelector.value }} - nodeSelector: - {{ .Values.nodeSelector.key }}: {{ .Values.nodeSelector.value }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} - terminationGracePeriodSeconds: {{ ((.Values.gracefulShutdown).terminationGracePeriodSeconds) | default 600 }} - {{- if (include "nvcaop.clusterValidatorEnabled" .) }} - {{- $cv := include "nvcaop.clusterValidatorConfig" . | fromYaml }} - # This init container runs under the operator ServiceAccount - # (serviceAccountName above), which already has configmaps - # get/create/update in this namespace via role.yaml, so its summary - # write succeeds. The separate cluster-validator ServiceAccount and - # ClusterRole in rbac.yaml grant the same configmaps verbs for the - # CronJob, which runs as that SA. - initContainers: - - name: cluster-validator - image: {{ include "nvcaop.clusterValidatorRepository" (dict "imageRepository" $cv.image.repository "defaultRepository" .Values.image.repository) }}:{{ default .Chart.AppVersion $cv.image.tag }} - imagePullPolicy: {{ $cv.image.pullPolicy }} - env: - - name: VALIDATOR_CONFIG_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: VALIDATOR_CONFIG_NAME - value: {{ $cv.configMapName | quote }} - # Namespace the NVCA agent watches for the metrics summary; kept - # separate from the config namespace so a config-namespace override - # can't redirect metrics. - - name: VALIDATOR_SUMMARY_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - resources: - requests: - cpu: {{ $cv.resources.requests.cpu | quote }} - memory: {{ $cv.resources.requests.memory | quote }} - limits: - cpu: {{ $cv.resources.limits.cpu | quote }} - memory: {{ $cv.resources.limits.memory | quote }} - securityContext: - runAsNonRoot: true - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - {{- end }} - containers: - - name: {{ include "nvcaop.fullname" . }} - env: - - name: POD_NAME - valueFrom: - fieldRef: - fieldPath: metadata.name - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: DEPLOYMENT_NAME - value: {{ include "nvcaop.fullname" . | quote }} - - name: NGC_API_URL - value: {{ .Values.ngcConfig.apiURL }} - - name: NGC_SERVICE_KEY_FILE - value: /var/run/secrets/ngc-service-key/{{ default "ngcServiceKey" .Values.ngcConfig.serviceKeySecretKeyName }} - - name: NVCA_CLUSTER_SOURCE - value: {{ .Values.ngcConfig.clusterSource | default "ngc-managed" }} - {{- if and .Values.vaultConfig .Values.vaultConfig.oAuthClientMountPathTemplate }} - - name: VAULT_OAUTH_CLIENT_MOUNT_PATH_TEMPLATE - value: {{ .Values.vaultConfig.oAuthClientMountPathTemplate | quote }} - {{- end }} - {{- if and .Values.vaultConfig .Values.vaultConfig.oAuthClientMountPath }} - - name: VAULT_OAUTH_CLIENT_MOUNT_PATH - value: {{ .Values.vaultConfig.oAuthClientMountPath | quote }} - {{- end }} - {{- if .Values.nvcaImage.repositoryOverride }} - - name: NVCA_IMAGE_REPO - value: "{{ .Values.nvcaImage.repositoryOverride }}" - {{- end}} - {{- if and .Values.networkPolicy (hasKey .Values.networkPolicy "clusterNetworkCIDRs") }} - - name: K8S_CLUSTER_NETWORK_CIDRS - value: {{ join "," .Values.networkPolicy.clusterNetworkCIDRs | quote }} - {{- end}} - {{- if ((.Values.agent).resources) }} - - name: AGENT_RESOURCES_B64 - value: {{ .Values.agent.resources | toJson | b64enc | quote }} - {{- end}} - {{- if ((.Values.webhook).resources) }} - - name: WEBHOOK_RESOURCES_B64 - value: {{ .Values.webhook.resources | toJson | b64enc | quote }} - {{- end}} - - name: GENERATE_IMAGE_PULL_SECRET - value: {{ .Values.generateImagePullSecret | quote }} - {{- if and .Values.imagePullSecrets (gt (len .Values.imagePullSecrets) 0) }} - - name: ADDITIONAL_IMAGE_PULL_SECRETS_B64 - value: {{ .Values.imagePullSecrets | toJson | b64enc | quote }} - {{- end}} - {{- if .Values.otelCollector }} - - name: OTEL_COLLECTOR_ENABLED - value: {{ .Values.otelCollector.enabled | quote }} - - name: OTEL_COLLECTOR_IMAGE_REPO - value: {{ include "nvcaop.otelCollectorRepository" (dict "imageRepository" .Values.otelCollector.imageRepository "defaultRepository" .Values.image.repository) | quote }} - - name: OTEL_COLLECTOR_IMAGE_TAG - value: {{ .Values.otelCollector.imageTag | default "0.160.0-nv-0.2.5" | quote }} - {{- if .Values.otelCollector.resources }} - - name: OTEL_COLLECTOR_RESOURCES_B64 - value: {{ .Values.otelCollector.resources | toJson | b64enc | quote }} - {{- end}} - {{- end}} - {{- if ((.Values.agent).overrideEnvironmentVariables) }} - - name: AGENT_OVERRIDE_ENV_VARS_JSON_B64 - value: {{ .Values.agent.overrideEnvironmentVariables | toJson | b64enc | quote }} - {{- end}} - {{- if (((.Values.agent).workload).tolerations) }} - - name: WORKLOAD_TOLERATIONS_B64 - value: {{ .Values.agent.workload.tolerations | toJson | b64enc | quote }} - {{- end}} - {{- if ((.Values.agent).tolerations) }} - - name: AGENT_TOLERATIONS_B64 - value: {{ .Values.agent.tolerations | toJson | b64enc | quote }} - {{- end}} - - name: OPERATOR_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if .Values.otel.enabled }} - envFrom: - - secretRef: - name: otel-{{ include "nvcaop.fullname" . }}-config - {{- end}} - args: - - /usr/bin/nvca-operator - - --nca-id - - "{{ .Values.ncaID }}" - - --system-namespace - - "{{ .Release.Namespace }}" - - --cluster-name - - "{{ .Values.clusterName }}" - - --log-level - - "{{ .Values.logLevel }}" - - --priority-class-name - - {{ (.Values.priorityClassName) | default "" | quote }} - {{- if .Values.k8sVersionOverride }} - - --k8s-version-override - - "{{ .Values.k8sVersionOverride }}" - {{- end}} - - --node-selector-key - - "{{ .Values.nodeSelector.key }}" - - --node-selector-value - - "{{ .Values.nodeSelector.value }}" - - --nvca-helm-repository-prefix - - "{{ .Values.nvcaHelmRepositoryPrefix}}" - - --cluster-id - - "{{ .Values.clusterID }}" - {{- if .Values.enableGXCache }} - - --enable-gxcache - {{- end}} - {{- if .Values.ddcsIPAllowList}} - - --ddcs-ip-allowlist - - "{{ .Values.ddcsIPAllowList }}" - {{- end}} - {{- if ne ((.Values.agent).cacheMountOptionsEnabled) false }} - - --nvca-cache-mount-options-enabled - {{- end }} - - --nvca-cache-mount-options - - {{ ((.Values.agent).cacheMountOptions) | default "ro,norecovery,nouuid" | quote }} - {{- if ((.Values.agent).workerDegradationPeriod) }} - - --nvca-worker-degradation-period - - {{ .Values.agent.workerDegradationPeriod | quote }} - {{- end }} - {{- if and ((.Values.agent).secretMirrorLabelSelector) ((.Values.agent).secretMirrorNamespace) }} - - --nvca-secret-mirror-source-namespace - - "{{ .Values.agent.secretMirrorNamespace }}" - - --nvca-secret-mirror-label-selector - - "{{ .Values.agent.secretMirrorLabelSelector }}" - {{- end }} - {{- $agent := .Values.agent | default dict }} - {{- $byooOtelCollectorImage := include "nvcaop.byooOtelCollectorImage" . }} - {{- $functionEnvOverrides := deepCopy ($agent.functionEnvOverrides | default dict) }} - {{- if and $byooOtelCollectorImage (or (not (hasKey $functionEnvOverrides "BYOO_OTEL_COLLECTOR_CONTAINER")) (eq (get $functionEnvOverrides "BYOO_OTEL_COLLECTOR_CONTAINER") "nvcr.io/nvidia/nvcf-byoc/byoo-otel-collector:0.157.11")) }} - {{- $_ := set $functionEnvOverrides "BYOO_OTEL_COLLECTOR_CONTAINER" $byooOtelCollectorImage }} - {{- end }} - {{- $taskEnvOverrides := deepCopy ($agent.taskEnvOverrides | default dict) }} - {{- if and $byooOtelCollectorImage (or (not (hasKey $taskEnvOverrides "BYOO_OTEL_COLLECTOR_CONTAINER")) (eq (get $taskEnvOverrides "BYOO_OTEL_COLLECTOR_CONTAINER") "nvcr.io/nvidia/nvcf-byoc/byoo-otel-collector:0.157.11")) }} - {{- $_ := set $taskEnvOverrides "BYOO_OTEL_COLLECTOR_CONTAINER" $byooOtelCollectorImage }} - {{- end }} - {{- if $functionEnvOverrides }} - - --function-env-overrides-b64 - - {{ $functionEnvOverrides | toJson | b64enc | quote }} - {{- end }} - {{- if $taskEnvOverrides }} - - --task-env-overrides-b64 - - {{ $taskEnvOverrides | toJson | b64enc | quote }} - {{- end }} - image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - securityContext: - # Set here so openbao injection can copy it upon injection - runAsUser: 1000 - runAsGroup: 1010 - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - lifecycle: - preStop: - httpGet: - path: /shutdown - port: 8002 - volumeMounts: - - name: ngc-service-key - mountPath: /var/run/secrets/ngc-service-key - readOnly: true - - name: nvcfbackend-config - mountPath: /etc/nvcf/backend - readOnly: true - ports: - - name: http - containerPort: 8000 - protocol: TCP - livenessProbe: - httpGet: - path: /healthz - port: 8000 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /version - port: 8000 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - requests: - cpu: {{ ((((.Values.agent).resources).requests).cpu) | default "50m" | quote }} - memory: {{ ((((.Values.agent).resources).requests).memory) | default "50Mi" | quote }} - limits: - cpu: {{ ((((.Values.agent).resources).limits).cpu) | default "500m" | quote }} - memory: {{ ((((.Values.agent).resources).limits).memory) | default "500Mi" | quote }} - - name: nvca-mirror - image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - securityContext: - runAsUser: 1000 - runAsGroup: 1010 - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - args: - - /usr/bin/nvca-mirror - - run - - --target-namespace - - "nvca-system" - - --log-level - - "{{ .Values.logLevel }}" - env: - - name: NVCA_MIRROR_SOURCE_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - {{- if and .Values.imagePullSecrets (gt (len .Values.imagePullSecrets) 0) }} - - name: NVCA_ADDITIONAL_IMAGE_PULL_SECRETS_JSON_BASE64 - value: {{ .Values.imagePullSecrets | toJson | b64enc | quote }} - {{- end }} - resources: - requests: - cpu: "10m" - memory: "32Mi" - limits: - cpu: "100m" - memory: "128Mi" - volumes: - - name: ngc-service-key - secret: - secretName: {{ .Values.ngcConfig.serviceKeySecretName }} - # optional only if clusterSource is self-managed - optional: {{ eq .Values.ngcConfig.clusterSource "self-managed" }} - - name: nvcfbackend-config - configMap: - {{- if eq .Values.ngcConfig.clusterSource "self-managed" }} - name: nvcfbackend-self-managed - {{- else }} - name: nvcfbackend-helm-managed - {{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/gpu-profiling-config-configmap.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/gpu-profiling-config-configmap.yaml deleted file mode 100644 index 6bb8a24d05..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/gpu-profiling-config-configmap.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# nvca-gpu-profiling-config: NVCF function IDs to profile, plus an optional label override -# for the Nsight Operator. Created at deploy/upgrade from .Values.agent.gpuProfiling.* and -# mirrored by the operator into the agent namespace. Empty functionIds profiles nothing; -# empty labelKey/labelValue use the defaults (nvidia-nsight-profile=enabled). -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvca-gpu-profiling-config - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -data: - functionIds: {{ (((.Values.agent).gpuProfiling).functionIds) | default "" | quote }} - labelKey: {{ (((.Values.agent).gpuProfiling).labelKey) | default "" | quote }} - labelValue: {{ (((.Values.agent).gpuProfiling).labelValue) | default "" | quote }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/helm-managed-nvcfbackend-cm.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/helm-managed-nvcfbackend-cm.yaml deleted file mode 100644 index d237738fd0..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/helm-managed-nvcfbackend-cm.yaml +++ /dev/null @@ -1,103 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvcfbackend-helm-managed - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -{{- if eq .Values.ngcConfig.clusterSource "helm-managed" }} -data: - cluster-dto.yaml: | - clusterId: {{ .Values.clusterID | quote }} - clusterName: {{ .Values.clusterName | quote }} - ncaID: {{ .Values.ncaID | quote }} - {{- with .Values.helmManaged }} - {{- $clientID := .oAuthClientID | default "" }} - {{- $mountPath := "" }} - {{- $vaultAddress := "" }} - {{- if $.Values.vaultConfig }} - {{- $mountPath = $.Values.vaultConfig.oAuthClientMountPath }} - {{- $vaultAddress = $.Values.vaultConfig.address | default "" }} - {{- if and (not $mountPath) $clientID $.Values.vaultConfig.oAuthClientMountPathTemplate }} - {{- $mountPath = printf $.Values.vaultConfig.oAuthClientMountPathTemplate $clientID }} - {{- end }} - {{- end }} - {{- if $clientID }} - {{- if or (ne $vaultAddress (trim $vaultAddress)) (contains "@" $vaultAddress) (contains "?" $vaultAddress) (contains "#" $vaultAddress) }} - {{- fail "vaultConfig.address must be an absolute HTTP(S) URL with no credentials, query, or fragment when helmManaged.oAuthClientID is set" }} - {{- end }} - {{- $parsedVaultAddress := urlParse $vaultAddress }} - {{- $vaultScheme := $parsedVaultAddress.scheme | default "" }} - {{- $vaultHost := $parsedVaultAddress.host | default "" }} - {{- $vaultUserInfo := $parsedVaultAddress.userinfo | default "" }} - {{- $vaultQuery := $parsedVaultAddress.query | default "" }} - {{- $vaultFragment := $parsedVaultAddress.fragment | default "" }} - {{- if or (not (has $vaultScheme (list "http" "https"))) (not (regexMatch `^(\[[^]]+\]|[^:[:space:]]+)(:[0-9]+)?$` $vaultHost)) $vaultUserInfo $vaultQuery $vaultFragment }} - {{- fail "vaultConfig.address must be an absolute HTTP(S) URL with no credentials, query, or fragment when helmManaged.oAuthClientID is set" }} - {{- end }} - {{- end }} - clusterDescription: {{ .clusterDescription | default $.Values.clusterName | quote }} - clusterGroupName: {{ .clusterGroupName | quote }} - clusterGroupId: {{ .clusterGroupID | default $.Values.clusterGroupID | quote }} - nvcaVersion: {{ .nvcaVersion | quote }} - oAuthClientId: {{ $clientID | quote }} - oAuthClientMountPath: {{ $mountPath | quote }} - vaultConfig: - address: {{ $vaultAddress | quote }} - cloudProvider: {{ .cloudProvider | quote }} - region: {{ .clusterRegion | quote }} - attributes: {{ include "nvcaop.jsonListOrEmpty" .clusterAttributes }} - capabilities: {{ include "nvcaop.jsonListOrEmpty" .featureGateValues }} - gpusB64: {{ .gpuManualInstanceConfigB64 | quote }} - {{- with .imageCredHelper }} - imageCredentialHelper: - imageConfig: - repository: {{ include "nvcaop.imageCredHelperRepository" (dict "imageRepository" .imageRepository "defaultRepository" $.Values.image.repository) | quote }} - tag: {{ .imageTag | quote }} - {{- end }} - {{- with .otelCollector }} - otelCollector: - enabled: {{ .enabled }} - imageConfig: - repository: {{ include "nvcaop.otelCollectorRepository" (dict "imageRepository" .imageRepository "defaultRepository" $.Values.image.repository) | quote }} - tag: {{ .imageTag | quote }} - {{- end }} - {{- end }} - {{- $serviceOAuth := ((.Values.agent).serviceOAuth) | default dict }} - {{- $helmReVal := $serviceOAuth.helmReVal | default dict }} - {{- $helmReValStage := $helmReVal.stage | default dict }} - {{- $helmReValProd := $helmReVal.prod | default dict }} - {{- $fds := $serviceOAuth.functionDeploymentStages | default dict }} - {{- $fdsStage := $fds.stage | default dict }} - {{- $fdsProd := $fds.prod | default dict }} - agent: - helmReValStageOAuthTokenURL: {{ $helmReValStage.tokenURL | default "" | quote }} - helmReValStageOAuthPublicKeysetEndpoint: {{ $helmReValStage.publicKeysetEndpoint | default "" | quote }} - helmReValProdOAuthTokenURL: {{ $helmReValProd.tokenURL | default "" | quote }} - helmReValProdOAuthPublicKeysetEndpoint: {{ $helmReValProd.publicKeysetEndpoint | default "" | quote }} - functionDeploymentStagesStageOAuthTokenURL: {{ $fdsStage.tokenURL | default "" | quote }} - functionDeploymentStagesStageOAuthPublicKeysetEndpoint: {{ $fdsStage.publicKeysetEndpoint | default "" | quote }} - functionDeploymentStagesProdOAuthTokenURL: {{ $fdsProd.tokenURL | default "" | quote }} - functionDeploymentStagesProdOAuthPublicKeysetEndpoint: {{ $fdsProd.publicKeysetEndpoint | default "" | quote }} - {{- with .Values.agent.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -{{- else }} -data: {} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/image-pull-secret.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/image-pull-secret.yaml deleted file mode 100644 index cc3d9128d8..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/image-pull-secret.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- if .Values.generateImagePullSecret }} -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: Secret -metadata: - name: {{ default "nvca-operator-image-pull" .Values.imagePullSecretName }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -type: kubernetes.io/dockerconfigjson -data: - .dockerconfigjson: {{ template "nvcaop.generatedImagePullSecret" . }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/ngc-service-key.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/ngc-service-key.yaml deleted file mode 100644 index 3468ffe672..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/ngc-service-key.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- if .Values.ngcConfig.serviceKey }} -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: Secret -metadata: - name: ngc-service-key - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -data: - ngcServiceKey: {{ .Values.ngcConfig.serviceKey | b64enc }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/nvca-operator_rq.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/nvca-operator_rq.yaml deleted file mode 100644 index 24f09df69b..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/nvca-operator_rq.yaml +++ /dev/null @@ -1,28 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ResourceQuota -metadata: - name: nvca-operator - namespace: nvca-operator -spec: - scopeSelector: - matchExpressions: - - operator: In - scopeName: PriorityClass - values: - - system-node-critical - - system-cluster-critical diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/operator-config-cm.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/operator-config-cm.yaml deleted file mode 100644 index 7af5318c6e..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/operator-config-cm.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -{{- include "nvcaop.validateTransportTrust" . }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvca-operator-config - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -{{- $operatorConfig := .Values.operatorConfig | default dict }} -{{- $workload := $operatorConfig.workload | default dict }} -{{- $transportTLS := $workload.transportTLS | default dict }} -{{- $trustBundle := $transportTLS.trustBundle | default dict }} -{{- $secretKeyRef := $trustBundle.secretKeyRef | default dict }} -data: - config.yaml: | - workload: - transportTLS: - trustBundle: - secretKeyRef: - name: {{ $secretKeyRef.name | default "" | quote }} - key: {{ $secretKeyRef.key | default "ca.crt" | quote }} - fingerprint: {{ $transportTLS.fingerprint | default "" | quote }} - {{- if $transportTLS.installedBundleMountPath }} - installedBundleMountPath: {{ $transportTLS.installedBundleMountPath | quote }} - {{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/operator-networkpolicy.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/operator-networkpolicy.yaml deleted file mode 100644 index 00e159c944..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/operator-networkpolicy.yaml +++ /dev/null @@ -1,139 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# NetworkPolicy hardening for the nvca-operator pod itself. -# -# By default, in clusters with a default-deny CNI (Calico, Cilium with -# enforcement, etc.) operator egress is wide open inside the cluster. -# This template adds explicit allow rules for the three legitimate -# egress targets: -# -# 1. kube-apiserver — for CRD watches, leader-election leases, -# child-resource create/update, status writes. CIDR-based rule -# because the apiserver IP is cluster-specific; default -# 0.0.0.0/0 - cluster-internal-CIDRs covers the public apiserver -# endpoint without leaking to other pods. -# -# 2. nvsnap-server (default nvsnap-system/nvsnap-server, port 8080) — -# for L2 promote-state polls and audit calls. Namespace + label -# selector keeps it tight. -# -# 3. kube-dns — for service name resolution. Without this rule the -# operator can't resolve nvsnap-server.nvsnap-system.svc anyway. -# -# Ingress: container port 8000 (health/metrics) and 8002 (auxiliary). -# Allowed from any namespace by default (Prometheus scraping + -# kubelet probes can come from anywhere). Operators can tighten by -# overriding ingressFrom. -# -# Opt-in via .Values.networkPolicy.operator.enabled (default false) -# so existing deployments keep working unchanged. Turn on after -# verifying the rule set fits your cluster. -# -# Note: K8s NetworkPolicy semantics are "deny by default IF any -# NetworkPolicy selects the pod." Creating this policy WITHOUT a -# matching cluster CNI may have no effect (e.g. some kindnet -# configs); test in your environment before relying on it. -{{- if and (hasKey .Values "networkPolicy") (hasKey .Values.networkPolicy "operator") .Values.networkPolicy.operator.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "nvcaop.fullname" . }}-operator-egress - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - annotations: - nvca.io/purpose: | - Default-deny operator egress + explicit allows for - kube-apiserver, nvsnap-server, and kube-dns. Tighten further by - overriding .Values.networkPolicy.operator.* — see the template - header for the rule shape. -spec: - podSelector: - matchLabels: - {{- include "nvcaop.baseSelectorLabels" . | nindent 6 }} - policyTypes: - - Egress - - Ingress - egress: - # 1. Kube-DNS — needed for nvsnap-server.nvsnap-system.svc resolution. - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: kube-system - podSelector: - matchLabels: - k8s-app: kube-dns - ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - # 2. nvsnap-server (L2 promote-state, audit log writes). - - to: - - namespaceSelector: - matchLabels: - kubernetes.io/metadata.name: {{ .Values.networkPolicy.operator.nvsnapServerNamespace | default "nvsnap-system" }} - podSelector: - matchLabels: - app: {{ .Values.networkPolicy.operator.nvsnapServerPodLabel | default "nvsnap-server" }} - ports: - - protocol: TCP - port: {{ .Values.networkPolicy.operator.nvsnapServerPort | default 8080 }} - # 3. Kube-apiserver. K8s doesn't expose the apiserver via a - # Service selector inside this NetworkPolicy syntax (kubernetes.default - # isn't a "podSelector" target — it's a virtual endpoint). The cleanest - # portable shape is an IP-based egress to the apiserver port. - # Operators with a known apiserver CIDR can pin it; default 0.0.0.0/0:443 - # allows the apiserver but ALSO any other 443 target, which is - # accepted because this rule list is additive (deny-by-default - # elsewhere blocks anything not explicitly allowed). - {{- with .Values.networkPolicy.operator.apiServerCIDRs }} - - to: - {{- range . }} - - ipBlock: - cidr: {{ . | quote }} - {{- end }} - ports: - - protocol: TCP - port: {{ $.Values.networkPolicy.operator.apiServerPort | default 443 }} - {{- end }} - # 4. Operator-defined extra egress rules (private registries, - # OTLP collectors, anything cluster-specific). Render verbatim. - {{- with .Values.networkPolicy.operator.extraEgress }} - {{- toYaml . | nindent 4 }} - {{- end }} - ingress: - # Allow scraping + probes from anywhere by default. Tighten via - # .Values.networkPolicy.operator.ingressFrom (list of NetworkPolicyPeer). - {{- with .Values.networkPolicy.operator.ingressFrom }} - - from: - {{- toYaml . | nindent 8 }} - ports: - - protocol: TCP - port: 8000 - - protocol: TCP - port: 8002 - {{- else }} - - ports: - - protocol: TCP - port: 8000 - - protocol: TCP - port: 8002 - {{- end }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/otel_config_secret.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/otel_config_secret.yaml deleted file mode 100644 index 1a73b96b72..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/otel_config_secret.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- if and .Values.otel.enabled (not (empty .Values.otel.lightstep.serviceName)) (not (empty .Values.otel.lightstep.accessToken)) }} -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -data: - OTEL_EXPORTER: {{ "lightstep" | b64enc }} - OTEL_ENDPOINT: {{ .Values.otel.endpoint | b64enc }} - LS_SERVICE_NAME: {{ .Values.otel.lightstep.serviceName | b64enc }} - LS_ACCESS_TOKEN: {{ .Values.otel.lightstep.accessToken | b64enc }} -kind: Secret -metadata: - name: otel-{{ include "nvcaop.fullname" . }}-config -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/poddisruptionbudget.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/poddisruptionbudget.yaml deleted file mode 100644 index 10344bba7d..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/poddisruptionbudget.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{{- if and .Values.podDisruptionBudget .Values.podDisruptionBudget.enabled }} -{{- $pdbMinAvail := .Values.podDisruptionBudget.minAvailable | toString }} -{{- $pdbMaxUnavail := .Values.podDisruptionBudget.maxUnavailable | toString }} -{{- if and (ne $pdbMinAvail "") (ne $pdbMaxUnavail "") }} -{{- fail "podDisruptionBudget: set exactly one of minAvailable or maxUnavailable, not both" }} -{{- end }} -{{- if and (eq $pdbMinAvail "") (eq $pdbMaxUnavail "") }} -{{- fail "podDisruptionBudget: set exactly one of minAvailable or maxUnavailable" }} -{{- end }} -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: {{ include "nvcaop.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: {{- include "nvcaop.labels" . | nindent 4 }} -spec: - {{- if ne $pdbMinAvail "" }} - minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} - {{- else }} - maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} - {{- end }} - selector: - matchLabels: {{- include "nvcaop.baseSelectorLabels" . | nindent 6 }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/pre-delete-cleanup-job.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/pre-delete-cleanup-job.yaml deleted file mode 100644 index c440ef7d1c..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/pre-delete-cleanup-job.yaml +++ /dev/null @@ -1,89 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: cleanup - annotations: - "helm.sh/hook": pre-delete - "helm.sh/hook-weight": "-10" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded -spec: - backoffLimit: 0 - activeDeadlineSeconds: {{ add (((.Values.gracefulShutdown).cleanupTimeoutSeconds) | default 540) 60 }} - template: - metadata: - labels: - {{- include "nvcaop.baseSelectorLabels" . | nindent 8 }} - app.kubernetes.io/component: cleanup - spec: - serviceAccountName: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup - automountServiceAccountToken: true - {{- if or .Values.generateImagePullSecret (gt (len .Values.imagePullSecrets) 0) }} - imagePullSecrets: - {{- if .Values.generateImagePullSecret }} - - name: {{ (.Values.imagePullSecretName) | default "nvca-operator-image-pull" | quote }} - {{- end }} - {{- range .Values.imagePullSecrets }} - - name: {{ .name | quote }} - {{- end }} - {{- end }} - restartPolicy: Never - securityContext: - runAsUser: 1000 - runAsGroup: 1010 - fsGroup: 1010 - {{- if .Values.priorityClassName }} - priorityClassName: {{ .Values.priorityClassName }} - {{- end }} - {{- if .Values.nodeSelector.value }} - nodeSelector: - {{ .Values.nodeSelector.key }}: {{ .Values.nodeSelector.value }} - {{- end }} - containers: - - name: cleanup - image: {{ .Values.image.repository }}:{{ default .Chart.AppVersion .Values.image.tag }} - imagePullPolicy: {{ .Values.image.pullPolicy }} - securityContext: - runAsUser: 1000 - runAsGroup: 1010 - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - args: - - /usr/bin/nvca-operator-cleanup - - --namespace - - "{{ .Release.Namespace }}" - - --cluster-role-name - - "{{ include "nvcaop.fullname" . }}" - - --cluster-role-binding-name - - "{{ include "nvcaop.fullname" . }}" - - --service-account-name - - "{{ include "nvcaop.serviceAccountName" . }}" - - --log-level - - "{{ .Values.logLevel }}" - resources: - requests: - cpu: "10m" - memory: "32Mi" - limits: - cpu: "100m" - memory: "128Mi" diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/pre-delete-cleanup-rbac.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/pre-delete-cleanup-rbac.yaml deleted file mode 100644 index b573f3096f..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/pre-delete-cleanup-rbac.yaml +++ /dev/null @@ -1,144 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: cleanup - annotations: - "helm.sh/hook": pre-delete - "helm.sh/hook-weight": "-20" - "helm.sh/hook-delete-policy": before-hook-creation -automountServiceAccountToken: false ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: cleanup - annotations: - "helm.sh/hook": pre-delete - "helm.sh/hook-weight": "-20" - "helm.sh/hook-delete-policy": before-hook-creation -rules: -- apiGroups: ["nvcf.nvidia.io"] - resources: ["nvcfbackends", "nvcfbackends/finalizers", "nvcfbackends/status"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: [""] - resources: - - configmaps - - secrets - - services - - serviceaccounts - - namespaces - - pods - - pods/log - - pods/status - - resourcequotas - - events - - configmaps/status - - persistentvolumes - - persistentvolumes/status - - persistentvolumeclaims - - persistentvolumeclaims/status - - nodes - verbs: ["get", "list", "watch", "patch", "create", "update", "delete", "deletecollection"] -- apiGroups: [""] - resources: ["serviceaccounts"] - verbs: ["impersonate"] -- apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["apps"] - resources: ["deployments", "replicasets", "statefulsets"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["rbac.authorization.k8s.io"] - resources: ["roles", "rolebindings"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["icmsrequests"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["icmsrequests/status"] - verbs: ["get", "update", "patch"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["modelcachebindings", "modelcachebindings/status", "storagerequests", "storagerequests/status"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["batch"] - resources: ["jobs", "cronjobs"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["networking.k8s.io"] - resources: ["networkpolicies"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete", "deletecollection"] -- apiGroups: ["rbac.authorization.k8s.io"] - resources: ["clusterroles", "clusterrolebindings"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["coordination.k8s.io"] - resources: ["leases"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] -- apiGroups: ["storage.k8s.io"] - resources: ["csidrivers", "storageclasses", "volumeattachments"] - verbs: ["get", "list", "watch", "create", "update", "delete", "patch", "deletecollection"] -- apiGroups: ["node.k8s.io"] - resources: ["runtimeclasses"] - verbs: ["get", "list", "watch"] -- apiGroups: ["security.openshift.io"] - resources: ["securitycontextconstraints"] - resourceNames: ["nonroot"] - verbs: ["use"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["miniservices", "miniservices/status"] - verbs: ["get", "list", "watch", "create", "update", "delete", "patch"] -- apiGroups: ["resource.nvidia.com"] - resources: ["computedomains"] - verbs: ["get", "list", "watch", "create", "update", "delete", "deletecollection", "patch"] -- apiGroups: ["resource.k8s.io"] - resources: ["resourceclaims", "resourceclaimtemplates", "deviceclasses", "resourceslices"] - verbs: ["get", "list", "watch", "create", "update", "delete", "deletecollection", "patch"] -- apiGroups: ["apps"] - resources: ["daemonsets"] - verbs: ["get", "list", "watch"] -- apiGroups: ["scheduling.run.ai"] - resources: ["queues"] - verbs: ["get", "list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: cleanup - annotations: - "helm.sh/hook": pre-delete - "helm.sh/hook-weight": "-20" - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup -subjects: -- kind: ServiceAccount - name: {{ include "nvcaop.fullname" . }}-pre-delete-cleanup - namespace: {{ .Release.Namespace }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac.yaml deleted file mode 100644 index 1aec060d85..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac.yaml +++ /dev/null @@ -1,95 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{{- if (include "nvcaop.clusterValidatorEnabled" .) }} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "nvcaop.fullname" . }}-cluster-validator - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: validation ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "nvcaop.fullname" . }}-cluster-validator - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: validation -rules: - - apiGroups: [""] - resources: - - nodes - - services - verbs: ["get", "list"] - - apiGroups: [""] - resources: ["namespaces"] - verbs: ["get", "list", "create", "delete"] - - apiGroups: [""] - resources: ["pods"] - verbs: ["get", "list", "create", "delete", "watch"] - - apiGroups: [""] - resources: ["configmaps"] - # get: read the network-checks config ConfigMap (its name is overridable - # via VALIDATOR_CONFIG_NAME, so this cannot be scoped by resourceName). - # create: write the cluster-validator-summary ConfigMap on the first run - # (create cannot be restricted by resourceName in Kubernetes RBAC). - verbs: ["get", "create"] - - apiGroups: [""] - resources: ["configmaps"] - # update only ever targets the cluster-validator-summary ConfigMap the - # NVCA agent watches for the metrics pipeline, so scope it by name to - # limit blast radius if the validator ServiceAccount is compromised. - resourceNames: ["cluster-validator-summary"] - verbs: ["update"] - - apiGroups: ["apps"] - resources: ["deployments", "daemonsets"] - verbs: ["get", "list"] - - apiGroups: ["admissionregistration.k8s.io"] - resources: - - mutatingwebhookconfigurations - - validatingwebhookconfigurations - verbs: ["get", "list"] - - apiGroups: ["networking.k8s.io"] - resources: ["networkpolicies"] - verbs: ["get", "list", "create", "update", "delete"] - - apiGroups: ["storage.k8s.io"] - resources: ["csidrivers"] - verbs: ["get", "list"] - - apiGroups: ["nvidia.com"] - resources: ["clusterpolicies"] - verbs: ["get", "list"] - - nonResourceURLs: ["/readyz", "/version", "/healthz"] - verbs: ["get"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "nvcaop.fullname" . }}-cluster-validator - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - app.kubernetes.io/component: validation -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "nvcaop.fullname" . }}-cluster-validator -subjects: - - kind: ServiceAccount - name: {{ include "nvcaop.fullname" . }}-cluster-validator - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac_allowed_extra_types.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac_allowed_extra_types.yaml deleted file mode 100644 index 1e170c2799..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac_allowed_extra_types.yaml +++ /dev/null @@ -1,48 +0,0 @@ -{{- $effectiveConfig := include "nvcaop.effectiveAgentConfig" . | fromYaml }} -{{- if $effectiveConfig }} -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{{- $mc := $effectiveConfig }} -{{- if and $mc.cluster ($mc.cluster.validationPolicy).allowedExtraKubernetesTypes }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "nvcaop.fullname" . }}-allowed-extra-types - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -rules: -{{- range $i, $kt := $mc.cluster.validationPolicy.allowedExtraKubernetesTypes }} -- apiGroups: ["{{ $kt.group | required "allowed extra type group must be set" }}"] - resources: ["{{ $kt.resource | required "allowed extra type resource must be set" }}"] - verbs: ["get", "list", "watch", "create", "update", "delete", "patch"] -{{- end }} ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "nvcaop.fullname" . }}-allowed-extra-types - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "nvcaop.fullname" . }}-allowed-extra-types -subjects: -- kind: ServiceAccount - name: {{ include "nvcaop.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -{{- end }} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/role.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/role.yaml deleted file mode 100644 index e565043fdd..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/role.yaml +++ /dev/null @@ -1,123 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "nvcaop.fullname" . }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - finalizers: - - nvca.nvcf.nvidia.io/operator-cleanup -rules: -- apiGroups: ["nvcf.nvidia.io"] - resources: ["nvcfbackends", "nvcfbackends/finalizers", "nvcfbackends/status"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: [""] - resources: - - configmaps - - secrets - - services - - serviceaccounts - - namespaces - - pods - - pods/log - - pods/status - - resourcequotas - - events - - configmaps/status - - persistentvolumes - - persistentvolumes/status - - persistentvolumeclaims - - persistentvolumeclaims/status - - nodes - verbs: ["get", "list", "watch", "patch", "create", "update", "delete", "deletecollection"] -- apiGroups: [""] - resources: ["serviceaccounts"] - verbs: ["impersonate"] -- apiGroups: ["apiextensions.k8s.io"] - resources: ["customresourcedefinitions"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["apps"] - resources: ["deployments", "replicasets", "statefulsets"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["rbac.authorization.k8s.io"] - resources: ["roles", "rolebindings"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["icmsrequests"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["icmsrequests/status"] - verbs: ["get", "update", "patch"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["modelcachebindings", "modelcachebindings/status", "storagerequests", "storagerequests/status"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["batch"] - resources: ["jobs", "cronjobs"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["admissionregistration.k8s.io"] - resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["networking.k8s.io"] - resources: ["networkpolicies"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete", "deletecollection"] -- apiGroups: ["rbac.authorization.k8s.io"] - resources: ["clusterroles", "clusterrolebindings"] - verbs: ["get", "list", "watch", "patch", "create", "update", "delete"] -- apiGroups: ["coordination.k8s.io"] - resources: ["leases"] - verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] -- apiGroups: ["storage.k8s.io"] - resources: ["csidrivers", "storageclasses", "volumeattachments"] - verbs: ["get", "list", "watch", "create", "update", "delete", "patch", "deletecollection"] -- apiGroups: ["node.k8s.io"] - resources: ["runtimeclasses"] - verbs: ["get", "list", "watch"] -- apiGroups: ["security.openshift.io"] - resources: ["securitycontextconstraints"] - resourceNames: ["nonroot"] - verbs: ["use"] -- apiGroups: ["nvca.nvcf.nvidia.io"] - resources: ["miniservices", "miniservices/status"] - verbs: ["get", "list", "watch", "create", "update", "delete", "patch"] -# NvSnapFunctionState is granted unconditionally, NOT behind the NvSnap -# feature flag. The operator generates the agent ClusterRole with this rule on -# every reconcile, and Kubernetes escalation prevention rejects a role that -# grants permissions the granting account does not itself hold: -# clusterroles "nvca" is forbidden: user "system:serviceaccount: -# nvca-operator:nvca-operator" is attempting to grant RBAC permissions not -# currently held: {APIGroups:["nvsnap.nvcf.nvidia.io"], ...} -# That check runs at install time on the delegation itself, long before any -# feature flag is consulted, so a default-off flag does not avoid it and a -# clean install cannot reconcile its NVCFBackend without this. -- apiGroups: ["nvsnap.nvcf.nvidia.io"] - resources: ["nvsnapfunctionstates", "nvsnapfunctionstates/status"] - verbs: ["get", "list", "watch", "create", "update", "delete", "patch"] -- apiGroups: ["resource.nvidia.com"] - resources: ["computedomains"] - verbs: ["get", "list", "watch", "create", "update", "delete", "deletecollection", "patch"] -- apiGroups: ["resource.k8s.io"] - resources: ["resourceclaims", "resourceclaimtemplates", "deviceclasses", "resourceslices"] - verbs: ["get", "list", "watch", "create", "update", "delete", "deletecollection", "patch"] -- apiGroups: ["apps"] - resources: ["daemonsets"] - verbs: ["get", "list", "watch"] -- apiGroups: ["scheduling.run.ai"] - resources: ["queues"] - verbs: ["get", "list", "watch"] -- apiGroups: ["run.ai"] - resources: ["kartas"] - verbs: ["get", "list", "watch"] diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/role_binding.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/role_binding.yaml deleted file mode 100644 index a0883d53b7..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/role_binding.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "nvcaop.fullname" . }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - finalizers: - - nvca.nvcf.nvidia.io/operator-cleanup -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "nvcaop.fullname" . }} -subjects: -- kind: ServiceAccount - name: {{ include "nvcaop.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/sa.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/sa.yaml deleted file mode 100644 index c1812d49d1..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/sa.yaml +++ /dev/null @@ -1,29 +0,0 @@ -{{- if .Values.serviceAccount.create -}} -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "nvcaop.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -automountServiceAccountToken: false -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/self-managed-nvcfbackend-cm.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/self-managed-nvcfbackend-cm.yaml deleted file mode 100644 index 9e304f860f..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/self-managed-nvcfbackend-cm.yaml +++ /dev/null @@ -1,100 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvcfbackend-self-managed - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -{{- if eq .Values.ngcConfig.clusterSource "self-managed" }} -data: - cluster-dto.yaml: | - clusterId: {{ .Values.clusterID | quote }} - clusterGroupId: {{ .Values.clusterGroupID | quote }} - clusterName: {{ .Values.clusterName | default "nvcf-default" | quote }} - clusterDescription: {{ .Values.clusterName | default "nvcf-default" | quote }} - clusterGroupName: "nvcf-default" - ncaID: {{ .Values.ncaID | default "nvcf-default" | quote }} - nvcaVersion: {{ .Values.selfManaged.nvcaVersion | quote }} - oAuthClientId: "" - cloudProvider: "NCP" - region: {{ .Values.selfManaged.region | default "us-west-1" | quote }} - attributes: {{ include "nvcaop.jsonListOrEmpty" .Values.selfManaged.clusterAttributes }} - capabilities: {{ include "nvcaop.jsonListOrEmpty" .Values.selfManaged.featureGateValues }} - gpusB64: {{ .Values.selfManaged.gpuManualInstanceConfigB64 | default "" | quote }} - cacheCSIVolumeMountOptionsConfig: - disabled: false - mountOptions: "ro,norecovery,nouuid" - icmsConfig: - publicKeysetEndpoint: "" - tokenURL: "" - icmsServiceURL: {{ .Values.selfManaged.icmsServiceURL | quote }} - {{- with .Values.selfManaged.icmsServiceHostHeaderOverride }} - icmsServiceHostHeaderOverride: {{ . | quote }} - {{- end }} - miniService: - helmReValServiceURL: {{ .Values.selfManaged.revalServiceURL | quote }} - {{- with .Values.selfManaged.revalServiceHostHeaderOverride }} - helmReValServiceHostHeaderOverride: {{ . | quote }} - {{- end }} - {{- with .Values.selfManaged.imageCredHelper }} - imageCredentialHelper: - imageConfig: - repository: {{ include "nvcaop.imageCredHelperRepository" (dict "imageRepository" .imageRepository "defaultRepository" $.Values.image.repository) | quote }} - tag: {{ .imageTag | quote }} - {{- end }} - {{- with .Values.selfManaged.sharedStorage }} - sharedStorage: - imageRepository: {{ .imageRepository | quote }} - imageTag: {{ .imageTag | quote }} - {{- end }} - {{- with .Values.selfManaged.otelCollector }} - otelCollector: - enabled: {{ .enabled }} - imageConfig: - repository: {{ include "nvcaop.otelCollectorRepository" (dict "imageRepository" .imageRepository "defaultRepository" $.Values.image.repository) | quote }} - tag: {{ .imageTag | quote }} - {{- end }} - {{- $serviceOAuth := ((.Values.agent).serviceOAuth) | default dict }} - {{- $helmReVal := $serviceOAuth.helmReVal | default dict }} - {{- $helmReValStage := $helmReVal.stage | default dict }} - {{- $helmReValProd := $helmReVal.prod | default dict }} - {{- $fds := $serviceOAuth.functionDeploymentStages | default dict }} - {{- $fdsStage := $fds.stage | default dict }} - {{- $fdsProd := $fds.prod | default dict }} - agent: - natsURL: {{ .Values.selfManaged.natsURL | quote }} - {{- with .Values.selfManaged.natsHostOverride }} - natsHostOverride: {{ . | quote }} - {{- end }} - {{- with .Values.agent.llm.requestRouterAddress }} - llmRequestRouterAddress: {{ . | quote }} - {{- end }} - helmReValStageOAuthTokenURL: {{ $helmReValStage.tokenURL | default "" | quote }} - helmReValStageOAuthPublicKeysetEndpoint: {{ $helmReValStage.publicKeysetEndpoint | default "" | quote }} - helmReValProdOAuthTokenURL: {{ $helmReValProd.tokenURL | default "" | quote }} - helmReValProdOAuthPublicKeysetEndpoint: {{ $helmReValProd.publicKeysetEndpoint | default "" | quote }} - functionDeploymentStagesStageOAuthTokenURL: {{ $fdsStage.tokenURL | default "" | quote }} - functionDeploymentStagesStageOAuthPublicKeysetEndpoint: {{ $fdsStage.publicKeysetEndpoint | default "" | quote }} - functionDeploymentStagesProdOAuthTokenURL: {{ $fdsProd.tokenURL | default "" | quote }} - functionDeploymentStagesProdOAuthPublicKeysetEndpoint: {{ $fdsProd.publicKeysetEndpoint | default "" | quote }} - {{- with .Values.agent.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -{{- else }} -data: {} -{{- end }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/shutdown-sentinel.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/shutdown-sentinel.yaml deleted file mode 100644 index 82b9e7a657..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/shutdown-sentinel.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvca-operator-shutdown-sentinel - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} - finalizers: - - nvca.nvcf.nvidia.io/operator-cleanup -data: - purpose: "Sentinel for graceful operator shutdown. When this ConfigMap is deleted, the operator will clean up all managed resources before terminating." diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/storage-capabilities-configmap.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/storage-capabilities-configmap.yaml deleted file mode 100644 index 83bb433c0a..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/storage-capabilities-configmap.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -{{- $catalogPath := "files/nvcf-storage-capabilities-v1alpha1.yaml" -}} -{{- $catalog := .Files.Get $catalogPath -}} -{{- if not $catalog -}} -{{- fail (printf "required NVCF storage capability catalog %s is missing" $catalogPath) -}} -{{- end }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: nvcf-storage-capabilities - namespace: {{ .Release.Namespace }} - labels: - {{- include "nvcaop.labels" . | nindent 4 }} -data: - storage-provider-capabilities.yaml: | -{{- $catalog | nindent 4 }} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/values.schema.json b/src/compute-plane-services/nvca/deployments/nvca-operator/values.schema.json deleted file mode 100644 index b12235c4fa..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/values.schema.json +++ /dev/null @@ -1,1465 +0,0 @@ -{ - "title": "Chart Values", - "type": "object", - "properties": { - "image": { - "type": "object", - "properties": { - "repository": { - "type": "string", - "description": "NVCA Operator container registry path, without tag", - "default": "nvcr.io/nvidia/nvcf-byoc/nvca-operator" - }, - "tag": { - "type": "string", - "description": "NVCA Operator container image tag. This defaults to the chart version", - "default": "" - }, - "pullPolicy": { - "type": "string", - "description": "K8s ImagePullPolicy", - "default": "IfNotPresent" - } - } - }, - "nvcaImage": { - "type": "object", - "properties": { - "repositoryOverride": { - "type": "string", - "description": "(Optional) Full NVCA container registry path, without tag. Only set this if the default needs to be overridden, for example \"stg.nvcr.io/nvidia/nvcf-byoc/nvca\". The tag is set in the cluster config", - "default": "" - }, - "pullPolicy": { - "type": "string", - "description": "K8s ImagePullPolicy", - "default": "IfNotPresent" - } - } - }, - "otelCollector": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable OTel collector sidecar for K8s event collection", - "default": false - }, - "imageRepository": { - "type": "string", - "description": "(OPTIONAL) Image repository of OpenTelemetry Collector sidecar. If not specified, it will be calculated based on image.repository (stg vs prod).", - "default": "" - }, - "imageTag": { - "type": "string", - "description": "Image tag of OpenTelemetry Collector sidecar.", - "default": "0.160.0-nv-0.2.5" - }, - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU limit for the OTel collector container", - "default": "1000m" - }, - "memory": { - "type": "string", - "description": "Memory limit for the OTel collector container", - "default": "1Gi" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU request for the OTel collector container", - "default": "200m" - }, - "memory": { - "type": "string", - "description": "Memory request for the OTel collector container", - "default": "256Mi" - } - } - } - } - } - } - }, - "generateImagePullSecret": { - "type": "boolean", - "description": "Use the ngcConfig.serviceKey to generate an image pull secret for nvca and nvca-operator Pods", - "default": true - }, - "imagePullSecretName": { - "type": "string", - "description": "Name of the image pull secret to use for nvca and nvca-operator Pods.", - "default": "nvca-operator-image-pull" - }, - "imagePullSecrets": { - "type": "array", - "description": "List of pre-existing imagePullSecret objects in the nvca-operator namespace to use for nvca and nvca-operator Pods. Each object must have a 'name' field. Example: [{name: \"foo-bar\"}, {name: \"baz\"}]", - "default": [], - "items": {} - }, - "serviceAccount": { - "type": "object", - "properties": { - "create": { - "type": "boolean", - "description": "Specifies whether a ServiceAccount should be created", - "default": true - }, - "annotations": { - "type": "object", - "description": "Additional custom annotations for the ServiceAccount", - "default": {} - }, - "name": { - "type": "string", - "description": "The name of the ServiceAccount to use.", - "default": "" - } - } - }, - "replicaCount": { - "type": "number", - "description": "Replica count for the operator deployment", - "default": 1 - }, - "podDisruptionBudget": { - "type": "object", - "description": "PodDisruptionBudget configuration for the NVCA Operator.", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable a PodDisruptionBudget for the NVCA Operator.", - "default": false - }, - "minAvailable": { - "description": "Minimum available pods. Set exactly one of minAvailable or maxUnavailable when enabled.", - "default": "", - "oneOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "string", - "enum": [ - "" - ] - }, - { - "type": "string", - "pattern": "^(100|[1-9]?[0-9])%$" - } - ] - }, - "maxUnavailable": { - "description": "Maximum unavailable pods. Set exactly one of minAvailable or maxUnavailable when enabled.", - "default": "", - "oneOf": [ - { - "type": "integer", - "minimum": 0 - }, - { - "type": "string", - "enum": [ - "" - ] - }, - { - "type": "string", - "pattern": "^(100|[1-9]?[0-9])%$" - } - ] - } - } - }, - "systemNamespace": { - "type": "string", - "description": "Namespace in which NVCFBackend objects are created.", - "default": "nvca-operator" - }, - "logLevel": { - "type": "string", - "description": "Logging level for the module", - "default": "info" - }, - "ncaID": { - "type": "string", - "description": "(REQUIRED) NVIDIA Cloud Account ID of the Primary Account", - "default": "" - }, - "clusterID": { - "type": "string", - "description": "ID of the Cluster for this NVCA instance to manage (REQUIRED when ngcConfig.clusterSource is \"self-managed\")", - "default": "" - }, - "clusterGroupID": { - "type": "string", - "description": "Group ID of the Cluster for this NVCA instance to manage (REQUIRED when ngcConfig.clusterSource is \"self-managed\"; for \"helm-managed\" prefer helmManaged.clusterGroupID — this top-level value is used as a fallback if helmManaged.clusterGroupID is unset)", - "default": "" - }, - "clusterName": { - "type": "string", - "description": "for metrics & telemetry (REQUIRED when ngcConfig.clusterSource is \"helm-managed\")", - "default": "" - }, - "k8sVersionOverride": { - "type": "string", - "description": "Override the K8s version that NVCA registers with", - "default": "" - }, - "priorityClassName": { - "type": "string", - "description": "K8s PriorityClassName for NVCA pods preference during evictions", - "default": "" - }, - "tolerations": { - "type": "array", - "description": "K8s tolerations for the nvca-operator pod", - "default": [], - "items": { - "type": "object" - } - }, - "nvcaHelmRepositoryPrefix": { - "type": "string", - "description": "Enables Helm repository restrictions to specific org/teams", - "default": "" - }, - "enableGXCache": { - "type": "boolean", - "description": "Enables GXCache Support in NVCA", - "default": true - }, - "ddcsIPAllowList": { - "type": "string", - "description": "provides comma separated CIDR ranges to allowList", - "default": "" - }, - "agentConfig": { - "type": "object", - "properties": { - "mergeConfig": { - "type": "string", - "description": "Merge fields into the generated NVCA config. Must be a string. Deprecated for BYOO settings; use byoo instead.", - "default": "" - } - } - }, - "byoo": { - "type": "object", - "description": "First-class BYOO OTel collector and capacity configuration.", - "properties": { - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "requests": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - }, - "logChunking": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "maxPayloadBytes": { - "type": "integer", - "minimum": 0 - }, - "dryRun": { - "type": "boolean" - } - } - }, - "otelCollector": { - "type": "object", - "description": "Structured BYOO OTel collector rendering overrides." - }, - "additionalResourceOverhead": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "fluentbit": { - "type": "object", - "properties": { - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "requests": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - } - } - }, - "utils": { - "type": "object", - "properties": { - "resources": { - "type": "object", - "description": "Resource requests and limits for the task pod's utils and init containers. Values are applied as both requests and limits.", - "additionalProperties": { - "type": "string" - } - } - } - }, - "storage": { - "type": "object", - "description": "First-class shared-storage and internal persistent storage configuration.", - "properties": { - "sharedStorage": { - "type": "object", - "properties": { - "server": { - "type": "object", - "properties": { - "image": { - "type": "string", - "default": "" - }, - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "requests": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "taskData": { - "type": "object", - "properties": { - "storageClassName": { - "type": "string", - "default": "" - }, - "mountOptions": { - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "storageCapacity": { - "type": "string", - "default": "" - } - } - } - } - }, - "internalPersistentStorage": { - "type": "object", - "properties": { - "storageClassName": { - "type": "string", - "default": "" - }, - "hardResourceQuota": { - "type": "object", - "additionalProperties": { - "type": "string" - } - } - } - } - } - }, - "worker": { - "type": "object", - "description": "First-class worker/agent behavior, scheduling, and timeout configuration.", - "properties": { - "minHealthcheckRefreshWait": { - "type": "string", - "default": "" - }, - "staticGPUCapacity": { - "type": "integer", - "minimum": 0, - "default": 0 - }, - "computeBackend": { - "type": "string", - "default": "" - }, - "requestsNamespace": { - "type": "string", - "default": "" - }, - "namespaceLabels": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "featureFlags": { - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "skipSelfDestruct": { - "type": "boolean", - "default": false - }, - "forceSelfDestruct": { - "type": "boolean", - "default": false - }, - "csiVolumeMountOptions": { - "type": "array", - "default": [], - "items": { - "type": "string" - } - }, - "timeouts": { - "type": "object", - "properties": { - "credRenewInterval": { - "type": "string", - "default": "" - }, - "heartbeatInterval": { - "type": "string", - "default": "" - }, - "syncQueueInterval": { - "type": "string", - "default": "" - }, - "syncRequestStatusInterval": { - "type": "string", - "default": "" - }, - "syncAcknowledgeRequestInterval": { - "type": "string", - "default": "" - }, - "periodicInstanceStatusInterval": { - "type": "string", - "default": "" - }, - "icmsRequestAckInterval": { - "type": "string", - "default": "" - }, - "icmsRequestAckRetryTimeout": { - "type": "string", - "default": "" - } - } - } - } - }, - "operatorConfig": { - "type": "object", - "properties": { - "workload": { - "type": "object", - "properties": { - "transportTLS": { - "type": "object", - "properties": { - "trustBundle": { - "type": "object", - "properties": { - "secretKeyRef": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Secret containing the workload transport trust bundle. Empty disables Secret-backed transport trust.", - "default": "" - }, - "key": { - "type": "string", - "description": "Secret data key containing certificate-only PEM.", - "default": "ca.crt" - } - } - } - } - }, - "fingerprint": { - "type": "string", - "description": "Optional sha256 fingerprint pin for the trust bundle. Empty computes the fingerprint from the selected Secret data.", - "default": "" - }, - "installedBundleMountPath": { - "type": "string", - "description": "Optional llm-worker mount path for the installed transport trust bundle. Empty uses /etc/ssl/certs.", - "default": "" - } - } - } - } - } - } - }, - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU limit for the nvca-operator container", - "default": "500m" - }, - "memory": { - "type": "string", - "description": "Memory limit for the nvca-operator container", - "default": "500Mi" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU request for the nvca-operator container", - "default": "50m" - }, - "memory": { - "type": "string", - "description": "Memory request for the nvca-operator container", - "default": "50Mi" - } - } - } - } - }, - "agent": { - "type": "object", - "properties": { - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU limit for the nvca agent container", - "default": "1000m" - }, - "memory": { - "type": "string", - "description": "Memory limit for the nvca agent container", - "default": "4Gi" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU request for the nvca agent container", - "default": "100m" - }, - "memory": { - "type": "string", - "description": "Memory request for the nvca agent container", - "default": "200Mi" - } - } - } - } - }, - "cacheMountOptionsEnabled": { - "type": "boolean", - "description": "Enable or disable CSI volume mount options for NVCA caches", - "default": true - }, - "cacheMountOptions": { - "type": "string", - "description": "Comma-separated string of CSI volume mount options (e.g., \"ro,noatime,nouuid\") used when cacheMountOptionsEnabled is true", - "default": "ro,norecovery,nouuid" - }, - "workerDegradationPeriod": { - "type": "string", - "description": "Duration for determining if a worker is degraded (e.g., \"90m\", \"1h30m\")", - "default": "" - }, - "secretMirrorNamespace": { - "type": "string", - "description": "Default namespace to mirror custom secrets for nvcf workloads", - "default": "nvca-operator" - }, - "secretMirrorLabelSelector": { - "type": "string", - "description": "Label selector on the secrets in the sourceNamespace", - "default": "" - }, - "customAnnotations": { - "type": "object", - "description": "Map of custom annotations to add to the agent pod", - "default": {} - }, - "gpuProfiling": { - "type": "object", - "description": "NVIDIA Nsight GPU profiling opt-in for NVCF function pods", - "properties": { - "functionIds": { - "type": "string", - "description": "Comma/space/newline-separated NVCF function IDs (or \"*\" for all) to enable Nsight GPU profiling for. Empty disables profiling.", - "default": "" - }, - "labelKey": { - "type": "string", - "description": "Pod label key NVCA applies to profiled function pods. Empty uses the built-in default \"nvidia-nsight-profile\".", - "default": "" - }, - "labelValue": { - "type": "string", - "description": "Pod label value NVCA applies to profiled function pods. Empty uses the built-in default \"enabled\".", - "default": "" - } - } - }, - "byooOtelCollector": { - "type": "object", - "description": "Default BYOO OpenTelemetry Collector image. The repository is derived from image.repository when not set.", - "properties": { - "imageRepository": { - "type": "string", - "description": "Optional BYOO OpenTelemetry Collector image repository. If unset, it is derived from image.repository.", - "default": "" - }, - "imageTag": { - "type": "string", - "description": "BYOO OpenTelemetry Collector image tag.", - "default": "0.160.0-nv-0.2.5" - } - } - }, - "functionEnvOverrides": { - "type": "object", - "description": "Map of environment variable overrides for function workloads (e.g., {\"INIT_CONTAINER\": \"nvcr.io/custom/init:v1.0\", \"UTILS_CONTAINER\": \"nvcr.io/custom/utils:v1.0\"})", - "default": {} - }, - "taskEnvOverrides": { - "type": "object", - "description": "Map of environment variable overrides for task workloads (e.g., {\"INIT_CONTAINER\": \"nvcr.io/custom/init:v1.0\", \"ESS_AGENT_CONTAINER\": \"nvcr.io/custom/ess:v1.0\"})", - "default": {} - }, - "tolerations": { - "type": "array", - "description": "K8s tolerations for the NVCA agent pod", - "default": [], - "items": { - "type": "object" - } - }, - "overrideEnvironmentVariables": { - "type": "object", - "description": "Map of environment variables to override on the NVCA agent container. These take precedence over default values. Example: {\"LOG_LEVEL\": \"debug\", \"CUSTOM_FLAG\": \"enabled\"}", - "default": {} - }, - "llm": { - "type": "object", - "properties": { - "requestRouterAddress": { - "type": "string", - "description": "Default LLM request-router worker address rendered as STARGATE_ADDRESS for LLM workers", - "default": "" - } - } - }, - "serviceOAuth": { - "type": "object", - "description": "OAuth token and JWKS endpoints used by dependent services", - "properties": { - "helmReVal": { - "type": "object", - "properties": { - "stage": { - "type": "object", - "properties": { - "tokenURL": { - "type": "string", - "default": "" - }, - "publicKeysetEndpoint": { - "type": "string", - "default": "" - } - } - }, - "prod": { - "type": "object", - "properties": { - "tokenURL": { - "type": "string", - "default": "" - }, - "publicKeysetEndpoint": { - "type": "string", - "default": "" - } - } - } - } - }, - "functionDeploymentStages": { - "type": "object", - "properties": { - "stage": { - "type": "object", - "properties": { - "tokenURL": { - "type": "string", - "default": "" - }, - "publicKeysetEndpoint": { - "type": "string", - "default": "" - } - } - }, - "prod": { - "type": "object", - "properties": { - "tokenURL": { - "type": "string", - "default": "" - }, - "publicKeysetEndpoint": { - "type": "string", - "default": "" - } - } - } - } - } - } - } - } - }, - "workload": { - "type": "object", - "properties": { - "tolerations": { - "type": "array", - "description": "K8s tolerations for NVCA-created workload pods", - "default": [], - "items": { - "type": "object" - } - } - } - }, - "webhook": { - "type": "object", - "properties": { - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU limit for the nvca webhook container", - "default": "200m" - }, - "memory": { - "type": "string", - "description": "Memory limit for the nvca webhook container", - "default": "200Mi" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU request for the nvca webhook container", - "default": "50m" - }, - "memory": { - "type": "string", - "description": "Memory request for the nvca webhook container", - "default": "50Mi" - } - } - } - } - } - } - }, - "ngcConfig": { - "type": "object", - "properties": { - "username": { - "type": "string", - "description": "Username for the registry authentication", - "default": "$oauthtoken" - }, - "serviceKey": { - "type": "string", - "description": "ServiceKey (password) for authentication. If unset, a Secret with name set to ngcConfig.serviceKeySecretName is expected to exist in the cluster in the release namespace.", - "default": "" - }, - "serviceKeySecretName": { - "type": "string", - "description": "Secret containing NGC ServiceKey (password) for authentication (default: ngc-service-key). If the ngcConfig.serviceKey is not set, the secret with this name must be created manually in the release namespace.", - "default": "ngc-service-key" - }, - "serviceKeySecretKeyName": { - "type": "string", - "description": "Key in the secret ngcConfig.serviceKeySecretName containing the NGC ServiceKey (password).", - "default": "ngcServiceKey" - }, - "apiURL": { - "type": "string", - "description": "NGC API URL for requesting auth tokens", - "default": "https://api.ngc.nvidia.com" - }, - "clusterSource": { - "type": "string", - "description": "Source of the cluster configuration:", - "default": "ngc-managed" - } - } - }, - "vaultConfig": { - "type": "object", - "properties": { - "address": { - "type": "string", - "description": "Vault server URL for Helm-managed clusters. Required when helmManaged.oAuthClientID is set; must be HTTP(S), without credentials, query, or fragment.", - "default": "" - }, - "oAuthClientMountPathTemplate": { - "type": "string", - "description": "Template for constructing the OAuth client mount path in Vault. Use %s as placeholder for clientID. Example: \"nvidia/services/oauth/clients/%s/kv/secret\"", - "default": "" - }, - "oAuthClientMountPath": { - "type": "string", - "description": "(Optional) Full OAuth client mount path. If set, overrides the computed path from template.", - "default": "" - } - } - }, - "helmManaged": { - "type": "object", - "properties": { - "cloudProvider": { - "type": "string", - "description": "(REQUIRED) Cloud provider for the cluster (e.g., aws, gcp, azure)", - "default": "" - }, - "clusterRegion": { - "type": "string", - "description": "(REQUIRED) Region where the cluster is deployed", - "default": "" - }, - "clusterGroupID": { - "type": "string", - "description": "(REQUIRED) Group ID for the cluster", - "default": "" - }, - "clusterGroupName": { - "type": "string", - "description": "(REQUIRED) Name of the cluster group", - "default": "" - }, - "nvcaVersion": { - "type": "string", - "description": "(REQUIRED) Version of the NVCFBackend to use", - "default": "" - }, - "oAuthClientID": { - "type": "string", - "description": "(Optional) Client ID for OAuth2/OIDC authentication. Can be blank or omitted.", - "default": "" - }, - "oAuthClientSecretKey": { - "type": "string", - "description": "(Optional) Secret key to retrieve the client secret for OAuth2/OIDC client. Leave blank if not needed.", - "default": "" - }, - "clusterDescription": { - "type": "string", - "description": "(Optional) Description of the cluster. Defaults to clusterName if not provided.", - "default": "" - }, - "featureGateValues": { - "type": "array", - "description": "(Optional) List of feature gates to enable. Defaults to [] if not specified.", - "default": [], - "items": {} - }, - "gpuManualInstanceConfigB64": { - "type": "string", - "description": "(Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required.", - "default": "" - }, - "clusterAttributes": { - "type": "array", - "description": "(Optional) List of attributes for the cluster. Defaults to an empty array.", - "default": [], - "items": {} - }, - "imageCredHelper": { - "type": "object", - "properties": { - "imageRepository": { - "type": "string", - "description": "(OPTIONAL) Image repository of \"nvcf-image-credential-helper\". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository.", - "default": "" - }, - "imageTag": { - "type": "string", - "description": "(REQUIRED) Image tag of \"nvcf-image-credential-helper\". Only override this if you know what you are doing.", - "default": "0.11.1" - } - } - }, - "otelCollector": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable OTel collector sidecar for helm-managed clusters", - "default": false - }, - "imageRepository": { - "type": "string", - "description": "(OPTIONAL) Image repository of \"otel-collector\". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository.", - "default": "" - }, - "imageTag": { - "type": "string", - "description": "(REQUIRED) Image tag of \"otel-collector\". Only override this if you know what you are doing.", - "default": "0.160.0-nv-0.2.5" - } - } - } - } - }, - "selfManaged": { - "type": "object", - "properties": { - "nvcaVersion": { - "type": "string", - "description": "(REQUIRED) Version of the NVCFBackend to use", - "default": "" - }, - "featureGateValues": { - "type": "array", - "description": "(Optional) List of feature gates to enable. Defaults to [\"DynamicGPUDiscovery\"] if not specified.", - "default": [ - "DynamicGPUDiscovery" - ], - "items": { - "type": "string" - } - }, - "gpuManualInstanceConfigB64": { - "type": "string", - "description": "(Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required.", - "default": "" - }, - "clusterAttributes": { - "type": "array", - "description": "(Optional) List of attributes for the cluster. Defaults to an empty array.", - "default": [], - "items": {} - }, - "region": { - "type": "string", - "description": "Region reported for a self-managed cluster.", - "default": "us-west-1" - }, - "imageCredHelper": { - "type": "object", - "properties": { - "imageRepository": { - "type": "string", - "description": "(OPTIONAL) Image repository of \"nvcf-image-credential-helper\". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository.", - "default": "" - }, - "imageTag": { - "type": "string", - "description": "(REQUIRED) Image tag of \"nvcf-image-credential-helper\". Only override this if you know what you are doing.", - "default": "0.11.1" - } - } - }, - "otelCollector": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable OTel collector sidecar for self-managed clusters", - "default": false - }, - "imageRepository": { - "type": "string", - "description": "(OPTIONAL) Image repository of \"otel-collector\". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository.", - "default": "" - }, - "imageTag": { - "type": "string", - "description": "(REQUIRED) Image tag of \"otel-collector\". Only override this if you know what you are doing.", - "default": "0.160.0-nv-0.2.5" - } - } - }, - "icmsServiceURL": { - "type": "string", - "description": "URL of the SIS/ICMS service for self-managed clusters. Required when ngcConfig.clusterSource is \"self-managed\".", - "default": "" - }, - "icmsServiceHostHeaderOverride": { - "type": "string", - "description": "Optional Host header override for selfManaged.icmsServiceURL.", - "default": "" - }, - "revalServiceURL": { - "type": "string", - "description": "URL of the ReVal service for self-managed clusters. Required when ngcConfig.clusterSource is \"self-managed\".", - "default": "" - }, - "revalServiceHostHeaderOverride": { - "type": "string", - "description": "Optional Host header override for selfManaged.revalServiceURL.", - "default": "" - }, - "natsURL": { - "type": "string", - "description": "URL of the NATS service for self-managed clusters. Required when ngcConfig.clusterSource is \"self-managed\".", - "default": "" - }, - "natsHostOverride": { - "type": "string", - "description": "Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL.", - "default": "" - } - } - }, - "nodeSelector": { - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "Node-selector Label key", - "default": "node.kubernetes.io/instance-type" - }, - "value": { - "type": "string", - "description": "Node-selector Label value", - "default": "" - } - } - }, - "otel": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable OpenTelemetry.", - "default": false - }, - "lightstep": { - "type": "object", - "properties": { - "serviceName": { - "type": "string", - "description": "the name of the lightstep service to push telemetry data to", - "default": "" - }, - "accessToken": { - "type": "string", - "description": "the access token for accessing the lightstep API", - "default": "" - } - } - } - } - }, - "gracefulShutdown": { - "type": "object", - "properties": { - "terminationGracePeriodSeconds": { - "type": "number", - "description": "Maximum time (in seconds) for pod termination and cleanup (K8s hard limit)", - "default": 600 - }, - "cleanupTimeoutSeconds": { - "type": "number", - "description": "HTTP handler timeout (in seconds). Must be less than terminationGracePeriodSeconds to ensure response is sent before K8s kills pod.", - "default": 540 - } - } - }, - "networkPolicy": { - "type": "object", - "properties": { - "clusterNetworkCIDRs": { - "type": "array", - "description": "List of IPv4 CIDRs that workload pods are NOT allowed to access (typically cluster-internal networks)", - "default": [ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - "100.64.0.0/12" - ], - "items": { - "type": "string" - } - }, - "customPolicies": { - "type": "array", - "description": "Array of custom network policy definitions to apply to nvca function namespaces", - "default": [], - "items": {} - } - } - }, - "clusterValidator": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable the cluster-validator CronJob and init container", - "default": false - }, - "image": { - "type": "object", - "properties": { - "repository": { - "type": "string", - "description": "Cluster Validator container registry path, without tag", - "default": "" - }, - "tag": { - "type": "string", - "description": "Cluster Validator container image tag", - "default": "v2.0.0" - }, - "pullPolicy": { - "type": "string", - "description": "K8s ImagePullPolicy for cluster-validator", - "default": "IfNotPresent" - } - } - }, - "schedule": { - "type": "string", - "description": "CronJob schedule (cron expression)", - "default": "0 */3 * * *" - }, - "configMapName": { - "type": "string", - "description": "ConfigMap name for user-defined network checks (reachability + network policy validation)", - "default": "cluster-validator-network-checks" - }, - "networkChecks": { - "type": [ - "object", - "null" - ], - "description": "Network check configuration (creates the ConfigMap automatically when set). Set to null or omit to skip configurable checks.", - "properties": { - "reachability": { - "type": "object", - "description": "Reachability check configuration. When set, endpoints replace the built-in egress/NVCF/monitoring checks.", - "properties": { - "endpoints": { - "type": "array", - "description": "List of endpoints to check for reachability", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Human-readable name of the endpoint" - }, - "host": { - "type": "string", - "description": "Hostname for TCP/TLS checks" - }, - "port": { - "type": "integer", - "description": "Port number for TCP/TLS checks" - }, - "url": { - "type": "string", - "description": "Full URL for HTTP/HTTPS checks" - }, - "protocol": { - "type": "string", - "description": "Protocol to use: https, tcp, tcp+tls", - "enum": [ - "https", - "tcp", - "tcp+tls" - ] - }, - "critical": { - "type": "boolean", - "description": "If true, failure makes the cluster not-ready. If false, failure is a warning only.", - "default": false - } - }, - "required": [ - "name", - "protocol" - ] - } - } - } - }, - "networkPolicies": { - "type": "object", - "description": "Network policy validation configuration", - "properties": { - "pairs": { - "type": "array", - "description": "List of namespace pairs to validate network policy coverage", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Human-readable name for this pair" - }, - "a": { - "type": "object", - "description": "Source side of the pair", - "properties": { - "namespace": { - "type": "string", - "description": "Namespace name" - }, - "podSelector": { - "type": "object", - "description": "Optional pod label selector to narrow scope" - } - }, - "required": [ - "namespace" - ] - }, - "b": { - "type": "object", - "description": "Destination side of the pair", - "properties": { - "namespace": { - "type": "string", - "description": "Namespace name" - }, - "podSelector": { - "type": "object", - "description": "Optional pod label selector to narrow scope" - } - }, - "required": [ - "namespace" - ] - }, - "port": { - "type": "integer", - "description": "Port number to check" - }, - "protocol": { - "type": "string", - "description": "Protocol (TCP or UDP)", - "enum": [ - "TCP", - "UDP" - ] - }, - "critical": { - "type": "boolean", - "description": "If true, failure for this pair marks the cluster as not-ready instead of just a warning", - "default": false - } - }, - "required": [ - "name", - "a", - "b", - "port", - "protocol" - ] - } - } - } - }, - "enforcement": { - "type": "object", - "description": "Network policy enforcement testing configuration", - "properties": { - "enabled": { - "type": "boolean", - "description": "Enable live enforcement testing by deploying test pods", - "default": false - }, - "testImage": { - "type": "string", - "description": "Container image for test pods", - "default": "busybox:1.36" - }, - "timeoutSeconds": { - "type": "integer", - "description": "Timeout in seconds for enforcement test operations", - "default": 90 - }, - "critical": { - "type": "boolean", - "description": "If true, enforcement failure marks the cluster as not-ready instead of just a warning", - "default": false - } - } - } - } - }, - "resources": { - "type": "object", - "properties": { - "limits": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU limit for the cluster-validator container", - "default": "200m" - }, - "memory": { - "type": "string", - "description": "Memory limit for the cluster-validator container", - "default": "128Mi" - } - } - }, - "requests": { - "type": "object", - "properties": { - "cpu": { - "type": "string", - "description": "CPU request for the cluster-validator container", - "default": "100m" - }, - "memory": { - "type": "string", - "description": "Memory request for the cluster-validator container", - "default": "64Mi" - } - } - } - } - } - } - } - }, - "allOf": [ - { - "if": { - "properties": { - "ngcConfig": { - "type": "object", - "required": [ - "clusterSource" - ], - "properties": { - "clusterSource": { - "enum": [ - "self-managed" - ] - } - } - } - }, - "required": [ - "ngcConfig" - ] - }, - "then": { - "required": [ - "selfManaged" - ], - "properties": { - "selfManaged": { - "type": "object", - "required": [ - "icmsServiceURL", - "revalServiceURL", - "natsURL" - ], - "properties": { - "icmsServiceURL": { - "type": "string", - "minLength": 1 - }, - "revalServiceURL": { - "type": "string", - "minLength": 1 - }, - "natsURL": { - "type": "string", - "minLength": 1 - } - } - } - } - } - } - ] -} diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml deleted file mode 100644 index 73a3c510fb..0000000000 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml +++ /dev/null @@ -1,606 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -## @section NVCA Operator parameters - -## @param image.repository NVCA Operator container registry path, without tag -## @param image.tag NVCA Operator container image tag. This defaults to the chart's appVersion -## @param image.pullPolicy K8s ImagePullPolicy -image: - repository: "nvcr.io/nvidia/nvcf-byoc/nvca-operator" - tag: "" - pullPolicy: IfNotPresent - -## @param nvcaImage.repositoryOverride (Optional) Full NVCA container registry path, without tag. Only set this if the default needs to be overridden, for example "stg.nvcr.io/nvidia/nvcf-byoc/nvca". The tag is set in the cluster config -## @param nvcaImage.pullPolicy K8s ImagePullPolicy -nvcaImage: - repositoryOverride: "" - pullPolicy: IfNotPresent - -## @section OTel Collector Configuration -## @param otelCollector.enabled Enable OTel collector sidecar for K8s event collection -## @param otelCollector.imageRepository (OPTIONAL) Image repository of OpenTelemetry Collector sidecar. If not specified, it will be calculated based on image.repository (stg vs prod). -## @param otelCollector.imageTag Image tag of OpenTelemetry Collector sidecar. -## @param otelCollector.resources.limits.cpu CPU limit for the OTel collector container -## @param otelCollector.resources.limits.memory Memory limit for the OTel collector container -## @param otelCollector.resources.requests.cpu CPU request for the OTel collector container -## @param otelCollector.resources.requests.memory Memory request for the OTel collector container -otelCollector: - enabled: false - imageRepository: "" - imageTag: 0.160.0-nv-0.2.5 - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 200m - memory: 256Mi - -## @param generateImagePullSecret Use the ngcConfig.serviceKey to generate an image pull secret for nvca and nvca-operator Pods -generateImagePullSecret: true -## @param imagePullSecretName Name of the image pull secret to use for nvca and nvca-operator Pods. -imagePullSecretName: "nvca-operator-image-pull" -## @param imagePullSecrets List of pre-existing imagePullSecret objects in the nvca-operator namespace to use for nvca and nvca-operator Pods. Each object must have a 'name' field. Example: [{name: "foo-bar"}, {name: "baz"}] -imagePullSecrets: [] - -## Service Account configuration -serviceAccount: - ## @param serviceAccount.create Specifies whether a ServiceAccount should be created - ## - create: true - ## @param serviceAccount.annotations Additional custom annotations for the ServiceAccount - ## - annotations: {} - ## @param serviceAccount.name The name of the ServiceAccount to use. - ## If not set and create is true, a name is generated using the common.names.fullname template - ## - name: "" - -## @param replicaCount Replica count for the operator deployment -replicaCount: 1 - -## @param podDisruptionBudget PodDisruptionBudget configuration -podDisruptionBudget: - enabled: false - # minAvailable and maxUnavailable are mutually exclusive; set exactly one. - # Accepts an integer or a percentage string from 0% to 100%. - minAvailable: "" - maxUnavailable: "" - -## @param systemNamespace Namespace in which NVCFBackend objects are created. -systemNamespace: nvca-operator - -## @param logLevel Logging level for the module -logLevel: info - -## @param ncaID (REQUIRED) NVIDIA Cloud Account ID of the Primary Account -ncaID: "" - -## @param clusterID ID of the Cluster for this NVCA instance to manage (REQUIRED when ngcConfig.clusterSource is "self-managed") -clusterID: "" - -## @param clusterGroupID Group ID of the Cluster for this NVCA instance to manage (REQUIRED when ngcConfig.clusterSource is "self-managed"; for "helm-managed" prefer helmManaged.clusterGroupID — this top-level value is used as a fallback if helmManaged.clusterGroupID is unset) -clusterGroupID: "" - -## @param clusterName for metrics & telemetry (REQUIRED when ngcConfig.clusterSource is "helm-managed") -clusterName: "" - -## @param k8sVersionOverride Override the K8s version that NVCA registers with -k8sVersionOverride: "" - -## @param priorityClassName K8s PriorityClassName for NVCA pods preference during evictions -priorityClassName: "" - -## @param tolerations K8s tolerations for the nvca-operator pod -tolerations: [] - -## @param nvcaHelmRepositoryPrefix Enables Helm repository restrictions to specific org/teams -nvcaHelmRepositoryPrefix: "" - -## @param enableGXCache Enables GXCache Support in NVCA -enableGXCache: true - -## @param ddcsIPAllowList provides comma separated CIDR ranges to allowList -ddcsIPAllowList: "" - -## @section BYOO Configuration -## @param byoo.resources Resource requests and limits for the BYOO OTel collector container. Leave unset to keep the agent's built-in defaults. -## @param byoo.logChunking.maxPayloadBytes Maximum log chunk payload size in bytes. Zero disables the override. -## @param byoo.logChunking.dryRun Record chunking metrics without modifying log payloads. -## @param byoo.otelCollector Structured BYOO OTel collector rendering overrides. -## @param byoo.additionalResourceOverhead Cluster capacity reserved for BYOO and related DaemonSet overhead. Leave unset to keep the agent's built-in defaults. -## @param byoo.fluentbit.resources Resource requests and limits for the BYOO FluentBit container. Leave unset to keep the agent's built-in defaults. -byoo: - resources: {} - logChunking: - maxPayloadBytes: 0 - dryRun: false - otelCollector: {} - additionalResourceOverhead: {} - fluentbit: - resources: {} - -## @param utils.resources Resource requests and limits for the task pod's utils and init containers. Values are applied as both requests and limits. -utils: - resources: - cpu: "4" - memory: 4Gi - -## @section Storage Configuration -## @param storage.sharedStorage.server.image Image for the shared-storage pod's SMB container. Leave unset to keep the agent's built-in default. -## @param storage.sharedStorage.server.resources Resource requests and limits for the shared-storage pod's SMB container. Leave unset to keep the agent's built-in defaults. -## @param storage.sharedStorage.taskData.storageClassName Storage class for task data. Falls back to ephemeral storage if unset. -## @param storage.sharedStorage.taskData.mountOptions Mount options for the provisioned task data PV. -## @param storage.sharedStorage.taskData.storageCapacity Capacity of the provisioned task data volume. Leave unset to keep the agent's built-in default. -## @param storage.internalPersistentStorage.storageClassName Storage class for the Internal Persistent Storage (IPS) PVC. -## @param storage.internalPersistentStorage.hardResourceQuota Hard storage quota for the IPS PVC, keyed by resource name (e.g. storage). -storage: - sharedStorage: - server: - image: "" - resources: {} - taskData: - storageClassName: "" - mountOptions: [] - storageCapacity: "" - internalPersistentStorage: - storageClassName: "" - hardResourceQuota: {} - -## @section Worker/Agent Behavior Configuration -## @param worker.minHealthcheckRefreshWait Minimum wait between internal healthchecker refresh calls. Leave unset to keep the agent's built-in default. -## @param worker.staticGPUCapacity Static override of GPU capacity used for registration. Zero disables the override. -## @param worker.computeBackend Selects the compute backend. Leave unset to keep the agent's built-in default. -## @param worker.requestsNamespace Namespace NVCA creates request-scoped resources in. Leave unset to keep the agent's built-in default. -## @param worker.namespaceLabels Labels applied to namespaces NVCA creates. -## @param worker.featureFlags Feature flags enabled on the NVCA agent. -## @param worker.skipSelfDestruct Skip self-destruct even if ICMS sends SELF_DESTRUCT. -## @param worker.forceSelfDestruct Force self-destruct mode for testing. -## @param worker.csiVolumeMountOptions CSI PVC mount options for provisioned volumes. -## @param worker.timeouts.credRenewInterval Interval between credential renewal attempts. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.heartbeatInterval Interval between agent heartbeats. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.syncQueueInterval Interval between queue sync polls. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.syncRequestStatusInterval Interval between request status syncs. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.syncAcknowledgeRequestInterval Interval between request acknowledgement syncs. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.periodicInstanceStatusInterval Interval between periodic instance status reports. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.icmsRequestAckInterval Interval between ICMS request acknowledgement polls. Leave unset to keep the agent's built-in default. -## @param worker.timeouts.icmsRequestAckRetryTimeout Timeout for retrying ICMS request acknowledgement. Leave unset to keep the agent's built-in default. -worker: - minHealthcheckRefreshWait: "" - staticGPUCapacity: 0 - computeBackend: "" - requestsNamespace: "" - namespaceLabels: {} - featureFlags: [] - skipSelfDestruct: false - forceSelfDestruct: false - csiVolumeMountOptions: [] - timeouts: - credRenewInterval: "" - heartbeatInterval: "" - syncQueueInterval: "" - syncRequestStatusInterval: "" - syncAcknowledgeRequestInterval: "" - periodicInstanceStatusInterval: "" - icmsRequestAckInterval: "" - icmsRequestAckRetryTimeout: "" - -## @param agentConfig.mergeConfig Merge fields into the generated NVCA config. Must be a string. -## @description agentConfig.mergeConfig is deprecated for BYOO, storage, and worker settings. Use the top-level byoo/storage/worker values instead. -agentConfig: - mergeConfig: "" - # Example: - # - # mergeConfig: | - # agent: - # logLevel: debug - # - # Legacy BYOO example (deprecated): - # - # mergeConfig: | - # agent: - # byooMetricSubset: - # enabled: true - # filterConfig: | - # error_mode: ignore - # metric_conditions: - # - 'metric.name != "BpsInstrument"' - # byooWorkloadMetrics: - # dropLabels: - # - metric_subset_enabled - # - custom_label - # byooOtelCollector: - # logSampling: - # samplingPercentage: 10 - # mode: hash_seed - # hashSeed: 1234 - # failClosed: true - # traceSampling: - # samplingPercentage: 1 - # mode: hash_seed - # hashSeed: 1234 - # failClosed: true - -## @section Workload transport trust configuration -## @param operatorConfig.workload.transportTLS.trustBundle.secretKeyRef.name Secret containing the workload transport trust bundle. Empty disables Secret-backed transport trust. Example: nvcf-trust. -## @param operatorConfig.workload.transportTLS.trustBundle.secretKeyRef.key Secret data key containing certificate-only PEM. -## @param operatorConfig.workload.transportTLS.fingerprint Optional sha256 fingerprint pin for the trust bundle. Empty computes the fingerprint from the selected Secret data. -## @param operatorConfig.workload.transportTLS.installedBundleMountPath Optional llm-worker mount path for the installed transport trust bundle. Empty uses /etc/ssl/certs. -operatorConfig: - workload: - transportTLS: - trustBundle: - secretKeyRef: - name: "" - key: ca.crt - fingerprint: "" - installedBundleMountPath: "" - -## @section resources Resource requests and limits for the nvca-operator container -## @param resources.limits.cpu CPU limit for the nvca-operator container -## @param resources.limits.memory Memory limit for the nvca-operator container -## @param resources.requests.cpu CPU request for the nvca-operator container -## @param resources.requests.memory Memory request for the nvca-operator container -resources: - limits: - cpu: 500m - memory: 500Mi - requests: - cpu: 50m - memory: 50Mi - -## @section Agent Container Resource configuration -## @param agent.resources.limits.cpu CPU limit for the nvca agent container -## @param agent.resources.limits.memory Memory limit for the nvca agent container -## @param agent.resources.requests.cpu CPU request for the nvca agent container -## @param agent.resources.requests.memory Memory request for the nvca agent container -## @param agent.cacheMountOptionsEnabled Enable or disable CSI volume mount options for NVCA caches -## @param agent.cacheMountOptions Comma-separated string of CSI volume mount options (e.g., "ro,noatime,nouuid") used when cacheMountOptionsEnabled is true -## @param agent.workerDegradationPeriod Duration for determining if a worker is degraded (e.g., "90m", "1h30m") -## @param agent.secretMirrorNamespace Default namespace to mirror custom secrets for nvcf workloads -## @param agent.secretMirrorLabelSelector Label selector on the secrets in the sourceNamespace -## @param agent.customAnnotations Map of custom annotations to add to the agent pod -## @param agent.gpuProfiling.functionIds Comma/space/newline-separated NVCF function IDs (or "*" for all) whose pods NVCA labels for NVIDIA Nsight GPU profiling. Empty disables profiling. The operator creates and mirrors the nvca-gpu-profiling-config ConfigMap from this value at deploy/upgrade time. -## @param agent.gpuProfiling.labelKey Pod label key NVCA applies to profiled function pods (the label the Nsight Operator watches for). Empty uses the built-in default "nvidia-nsight-profile". -## @param agent.gpuProfiling.labelValue Pod label value NVCA applies to profiled function pods. Empty uses the built-in default "enabled". -## @param agent.workload.tolerations K8s tolerations for NVCA-created workload pods -## @param agent.functionEnvOverrides Map of environment variable overrides for function workloads (e.g., {"INIT_CONTAINER": "nvcr.io/custom/init:v1.0", "UTILS_CONTAINER": "nvcr.io/custom/utils:v1.0"}) -## @param agent.taskEnvOverrides Map of environment variable overrides for task workloads (e.g., {"INIT_CONTAINER": "nvcr.io/custom/init:v1.0", "ESS_AGENT_CONTAINER": "nvcr.io/custom/ess:v1.0"}) -## @param agent.overrideEnvironmentVariables Map of environment variables to override on the NVCA agent container. These take precedence over default values. Example: {"LOG_LEVEL": "debug", "CUSTOM_FLAG": "enabled"} -## @param agent.tolerations K8s tolerations for the NVCA agent pod -## @param agent.llm.requestRouterAddress Operator default LLM request-router address. Workers read LLM_REQUEST_ROUTER_ADDRESS from the launch environment; not a runtime fallback -## @param agent.serviceOAuth OAuth token and JWKS endpoints used by dependent services -agent: - cacheMountOptionsEnabled: true - cacheMountOptions: "ro,norecovery,nouuid" - workerDegradationPeriod: "" - secretMirrorNamespace: nvca-operator - secretMirrorLabelSelector: "" - customAnnotations: {} - ## GPU (Nsight) profiling opt-in: NVCF function IDs to profile ("*" = all) and an optional - ## label override (defaults to nvidia-nsight-profile=enabled). Drives the - ## nvca-gpu-profiling-config ConfigMap the operator mirrors into the agent namespace. - gpuProfiling: - functionIds: "" - labelKey: "" - labelValue: "" - tolerations: [] - llm: - requestRouterAddress: "" - workload: - tolerations: [] - ## @param agent.byooOtelCollector.imageRepository Optional BYOO OpenTelemetry Collector image repository. If unset, it is derived from image.repository. - ## @param agent.byooOtelCollector.imageTag BYOO OpenTelemetry Collector image tag. - byooOtelCollector: - imageRepository: "" - imageTag: "0.160.0-nv-0.2.5" - ## Environment variable overrides for function workloads. - ## Available keys: INIT_CONTAINER, UTILS_CONTAINER, OTEL_CONTAINER, BYOO_OTEL_COLLECTOR_CONTAINER, - ## NICLLS_CONTAINER, ESS_AGENT_CONTAINER, INFERENCE_CONTAINER - ## Example: - ## functionEnvOverrides: - ## INIT_CONTAINER: "nvcr.io/qtfpt1h0bieu/nvcf-core/nvcf_worker_init:2.97.2" - ## UTILS_CONTAINER: "nvcr.io/qtfpt1h0bieu/nvcf-core/nvcf_worker_utils:2.94.0" - ## ESS_AGENT_CONTAINER: "nvcr.io/qtfpt1h0bieu/nvcf-core/ess-agent:1.0.5" - functionEnvOverrides: {} - ## Environment variable overrides for task workloads. - ## Available keys: INIT_CONTAINER, UTILS_CONTAINER, OTEL_CONTAINER, BYOO_OTEL_COLLECTOR_CONTAINER, - ## ESS_AGENT_CONTAINER, TASK_CONTAINER - ## Example: - ## taskEnvOverrides: - ## INIT_CONTAINER: "nvcr.io/qtfpt1h0bieu/nvcf-core/nvcf_worker_init:2.97.2" - ## UTILS_CONTAINER: "nvcr.io/qtfpt1h0bieu/nvcf-core/nvcf_worker_utils:2.94.0" - taskEnvOverrides: {} - overrideEnvironmentVariables: {} - serviceOAuth: - helmReVal: - stage: - tokenURL: "" - publicKeysetEndpoint: "" - prod: - tokenURL: "" - publicKeysetEndpoint: "" - functionDeploymentStages: - stage: - tokenURL: "" - publicKeysetEndpoint: "" - prod: - tokenURL: "" - publicKeysetEndpoint: "" - resources: - limits: - cpu: 1000m - memory: 4Gi - requests: - cpu: 100m - memory: 200Mi -## @section Webhook Container Resource configuration -## @param webhook.resources.limits.memory Memory limit for the nvca webhook container -## @param webhook.resources.requests.cpu CPU request for the nvca webhook container -## @param webhook.resources.requests.memory Memory request for the nvca webhook container -webhook: - resources: - limits: - memory: 200Mi - requests: - cpu: 500m - memory: 50Mi - -## @section NGC Configuration - -## @param ngcConfig.username Username for the registry authentication -## @param ngcConfig.serviceKey ServiceKey (password) for authentication. If unset, a Secret with name set to ngcConfig.serviceKeySecretName is expected to exist in the cluster in the release namespace. -## @param ngcConfig.serviceKeySecretName Secret containing NGC ServiceKey (password) for authentication (default: ngc-service-key). If the ngcConfig.serviceKey is not set, the secret with this name must be created manually in the release namespace. -## @param ngcConfig.serviceKeySecretKeyName Key in the secret ngcConfig.serviceKeySecretName containing the NGC ServiceKey (password). -## @param ngcConfig.apiURL NGC API URL for requesting auth tokens -## @param ngcConfig.clusterSource Source of the cluster configuration: -## - "ngc-managed": Cluster configuration managed by NGC (default) -## - "helm-managed": Cluster configuration managed by the Helm chart -## - "self-managed": Cluster configuration managed by the self-hosted compute plane -ngcConfig: - username: '$oauthtoken' - serviceKey: "" - serviceKeySecretName: "ngc-service-key" - serviceKeySecretKeyName: "ngcServiceKey" - apiURL: https://api.ngc.nvidia.com - clusterSource: ngc-managed - - -## @section Vault Configuration -## @param vaultConfig.address Vault server URL for Helm-managed clusters. Required when helmManaged.oAuthClientID is set; must be HTTP(S), without credentials, query, or fragment. -## @param vaultConfig.oAuthClientMountPathTemplate Template for constructing the OAuth client mount path in Vault. Use %s as placeholder for clientID. Example: "nvidia/services/oauth/clients/%s/kv/secret" -## @param vaultConfig.oAuthClientMountPath (Optional) Full OAuth client mount path. If set, overrides the computed path from template. -vaultConfig: - address: "" - oAuthClientMountPathTemplate: "" - oAuthClientMountPath: "" - -## @section Helm Managed NVCF Backend Configuration -## Only used when ngcConfig.clusterSource is "helm-managed" -## All values below are now under the 'helmManaged:' key, e.g. 'helmManaged.cloudProvider' -helmManaged: - ## @param helmManaged.cloudProvider (REQUIRED) Cloud provider for the cluster (e.g., aws, gcp, azure) - cloudProvider: "" - ## @param helmManaged.clusterRegion (REQUIRED) Region where the cluster is deployed - clusterRegion: "" - ## @param helmManaged.clusterGroupID (REQUIRED unless top-level clusterGroupID is set as a fallback) Group ID for the cluster - clusterGroupID: "" - ## @param helmManaged.clusterGroupName (REQUIRED) Name of the cluster group - clusterGroupName: "" - ## @param helmManaged.nvcaVersion (REQUIRED) Version of the NVCFBackend to use - nvcaVersion: "" - ## @param helmManaged.oAuthClientID (Optional) Client ID for OAuth2/OIDC authentication. Can be blank or omitted. - oAuthClientID: "" - ## @param helmManaged.oAuthClientSecretKey (Optional) Secret key to retrieve the client secret for OAuth2/OIDC client. Leave blank if not needed. - oAuthClientSecretKey: "" - ## @param helmManaged.clusterDescription (Optional) Description of the cluster. Defaults to clusterName if not provided. - clusterDescription: "" - ## @param helmManaged.featureGateValues (Optional) List of feature gates to enable. Defaults to [] if not specified. - featureGateValues: [] - ## @param helmManaged.gpuManualInstanceConfigB64 (Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required. - gpuManualInstanceConfigB64: "" - ## @param helmManaged.clusterAttributes (Optional) List of attributes for the cluster. Defaults to an empty array. - clusterAttributes: [] - ## @param helmManaged.imageCredHelper.imageRepository (OPTIONAL) Image repository of "nvcf-image-credential-helper". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. - ## @param helmManaged.imageCredHelper.imageTag (REQUIRED) Image tag of "nvcf-image-credential-helper". Only override this if you know what you are doing. - imageCredHelper: - imageRepository: "" - imageTag: 0.11.1 - ## @param helmManaged.otelCollector.enabled Enable OTel collector sidecar for helm-managed clusters - ## @param helmManaged.otelCollector.imageRepository (OPTIONAL) Image repository of "otel-collector". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. - ## @param helmManaged.otelCollector.imageTag (REQUIRED) Image tag of "otel-collector". Only override this if you know what you are doing. - otelCollector: - enabled: false - imageRepository: "" - imageTag: 0.160.0-nv-0.2.5 - -## @section Self Managed NVCF Backend Configuration -## Only used when ngcConfig.clusterSource is "self-managed" -## All values below are under the 'selfManaged:' key, e.g. 'selfManaged.nvcaVersion' -selfManaged: - ## @param selfManaged.nvcaVersion (REQUIRED) Version of the NVCFBackend to use - nvcaVersion: "" - ## @param selfManaged.featureGateValues (Optional) List of feature gates to enable. Defaults to ["DynamicGPUDiscovery"] if not specified. - featureGateValues: ["DynamicGPUDiscovery"] - ## @param selfManaged.gpuManualInstanceConfigB64 (Optional) Base64 encoded GPU manual instance configuration. Leave blank if not required. - gpuManualInstanceConfigB64: "" - ## @param selfManaged.clusterAttributes (Optional) List of attributes for the cluster. Defaults to an empty array. - clusterAttributes: [] - ## @param selfManaged.region Region reported for a self-managed cluster. - region: us-west-1 - ## @param selfManaged.imageCredHelper.imageRepository (OPTIONAL) Image repository of "nvcf-image-credential-helper". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. - ## @param selfManaged.imageCredHelper.imageTag (REQUIRED) Image tag of "nvcf-image-credential-helper". Only override this if you know what you are doing. - imageCredHelper: - imageRepository: "" - imageTag: 0.11.1 - ## @param selfManaged.otelCollector.enabled Enable OTel collector sidecar for self-managed clusters - ## @param selfManaged.otelCollector.imageRepository (OPTIONAL) Image repository of "otel-collector". Only override this if you know what you are doing. If not specified, it will be calculated based on image.repository. - ## @param selfManaged.otelCollector.imageTag (REQUIRED) Image tag of "otel-collector". Only override this if you know what you are doing. - otelCollector: - enabled: false - imageRepository: "" - imageTag: 0.160.0-nv-0.2.5 - ## @param selfManaged.icmsServiceURL URL of the SIS/ICMS service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". - icmsServiceURL: "" - ## @param selfManaged.icmsServiceHostHeaderOverride Optional Host header override for selfManaged.icmsServiceURL. - icmsServiceHostHeaderOverride: "" - ## @param selfManaged.revalServiceURL URL of the ReVal service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". - revalServiceURL: "" - ## @param selfManaged.revalServiceHostHeaderOverride Optional Host header override for selfManaged.revalServiceURL. - revalServiceHostHeaderOverride: "" - ## @param selfManaged.natsURL URL of the NATS service for self-managed clusters. Required when ngcConfig.clusterSource is "self-managed". - natsURL: "" - ## @param selfManaged.natsHostOverride Optional TLS SNI host override for selfManaged.natsURL when using a tls or wss NATS URL. - natsHostOverride: "" -## @section Node Selector Configuration -## @param nodeSelector.key Node-selector Label key -## @param nodeSelector.value Node-selector Label value -nodeSelector: - key: node.kubernetes.io/instance-type - value: "" - -## @section OpenTelemetry configuration - -## @param otel.enabled Enable OpenTelemetry. -## @param otel.endpoint OTLP exporter endpoint. Required when otel is enabled. -## When upgrading from a version that did not have this field, pass it once: -## helm upgrade ... --set otel.endpoint="" -## Subsequent upgrades with --reuse-values will preserve it. -## @param otel.lightstep.serviceName the name of the lightstep service to push telemetry data to -## @param otel.lightstep.accessToken the access token for accessing the lightstep API -otel: - enabled: false - endpoint: "" - lightstep: - serviceName: "" - accessToken: "" - -## @section Graceful Shutdown Configuration - -## @param gracefulShutdown.terminationGracePeriodSeconds Maximum time (in seconds) for pod termination and cleanup (K8s hard limit) -## @param gracefulShutdown.cleanupTimeoutSeconds HTTP handler timeout (in seconds). Must be less than terminationGracePeriodSeconds to ensure response is sent before K8s kills pod. -## Time budget: poll (~15s) + NVCA rollout (~2min) + drain (~6min) + cleanup (~1min) = ~9min total -gracefulShutdown: - terminationGracePeriodSeconds: 600 - cleanupTimeoutSeconds: 540 - -## @section Network Policy Configuration - -## @param networkPolicy.clusterNetworkCIDRs List of IPv4 CIDRs that workload pods are NOT allowed to access (typically cluster-internal networks) -## @param networkPolicy.customPolicies Array of custom network policy definitions to apply to nvca function namespaces -networkPolicy: - clusterNetworkCIDRs: ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "100.64.0.0/12"] - customPolicies: [] - - ## @param networkPolicy.operator.enabled Apply a default-deny NetworkPolicy to the operator pod itself, with explicit allows for kube-dns, nvsnap-server, and the kube-apiserver. Opt-in: false by default so existing deployments are unchanged. - ## @param networkPolicy.operator.nvsnapServerNamespace Namespace where nvsnap-server runs (egress target for L2 promote-state polls + audit writes) - ## @param networkPolicy.operator.nvsnapServerPodLabel app= label value matched against pods in the nvsnap-server namespace - ## @param networkPolicy.operator.nvsnapServerPort nvsnap-server REST port - ## @param networkPolicy.operator.apiServerCIDRs CIDR list containing the kube-apiserver. Empty = no apiserver allow (operator loses K8s API access). Pin to your cluster's apiserver range. - ## @param networkPolicy.operator.apiServerPort kube-apiserver port - ## @param networkPolicy.operator.ingressFrom Optional NetworkPolicyPeer list for ingress (Prometheus, kubelet probes). Empty = allow from anywhere. - ## @param networkPolicy.operator.extraEgress Additional egress rule entries appended verbatim (private registries, OTLP collector, …) - operator: - enabled: false - nvsnapServerNamespace: "nvsnap-system" - nvsnapServerPodLabel: "nvsnap-server" - nvsnapServerPort: 8080 - apiServerCIDRs: [] - apiServerPort: 443 - ingressFrom: [] - extraEgress: [] - -## @section Cluster Validator Configuration - -## @param clusterValidator.enabled Enable the cluster-validator CronJob and init container -## @param clusterValidator.image.repository Cluster Validator container registry path, without tag -## @param clusterValidator.image.tag Cluster Validator container image tag -## @param clusterValidator.image.pullPolicy K8s ImagePullPolicy for cluster-validator -## @param clusterValidator.schedule CronJob schedule (cron expression) -## @param clusterValidator.configMapName ConfigMap name for user-defined network checks (reachability + network policy validation) -## @param clusterValidator.resources.limits.cpu CPU limit for the cluster-validator container -## @param clusterValidator.resources.limits.memory Memory limit for the cluster-validator container -## @param clusterValidator.resources.requests.cpu CPU request for the cluster-validator container -## @param clusterValidator.resources.requests.memory Memory request for the cluster-validator container -## @param clusterValidator.networkChecks [object] Network check configuration (creates the ConfigMap automatically when set) -clusterValidator: - enabled: false - image: - repository: "" - tag: "" # defaults to .Chart.AppVersion (same as nvca-operator) - pullPolicy: IfNotPresent - schedule: "0 */3 * * *" - configMapName: "cluster-validator-network-checks" - ## networkChecks is intentionally left empty in the default values. - ## The appropriate endpoints will be discovered at runtime for self-hosted - ## and managed clusters. To override, uncomment the example below and - ## customize the endpoints, network policy pairs, and enforcement settings. - ## - ## Sections: - ## reachability.endpoints — connectivity checks (HTTPS/TCP/TLS). - ## Set critical: true to make a failure block cluster readiness. - ## networkPolicies.pairs — bidirectional NetworkPolicy coverage checks. - ## Set critical: true per pair to make a failure block readiness. - ## enforcement — active testing with ephemeral pods to verify the CNI - ## enforces policies. Set critical: true to block readiness on failure. - ## - ## Example: - ## networkChecks: - ## reachability: - ## endpoints: - ## - name: "Container Registry" - ## host: "registry.example.com" - ## port: 443 - ## protocol: "tcp+tls" - ## critical: true - ## - name: "NGC API" - ## host: "api.example.com" - ## port: 443 - ## protocol: "https" - ## url: "https://api.example.com" - ## critical: true - ## - name: "Monitoring - Thanos" - ## host: "monitoring.example.com" - ## port: 443 - ## protocol: "https" - ## url: "https://monitoring.example.com" - ## networkPolicies: - ## pairs: - ## - name: "app-to-db" - ## a: - ## namespace: "app-ns" - ## podSelector: - ## app: myapp - ## b: - ## namespace: "db-ns" - ## podSelector: - ## app: postgres - ## port: 5432 - ## protocol: "TCP" - ## critical: true - ## enforcement: - ## enabled: true - ## testImage: "busybox:1.36" # override if Docker Hub is unreachable - ## timeoutSeconds: 90 - ## critical: false - - networkChecks: {} - resources: - requests: - cpu: 100m - memory: 64Mi - limits: - cpu: 200m - memory: 128Mi diff --git a/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_rbac_delegation_test.go b/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_rbac_delegation_test.go index a6e640de1f..9374297fcb 100644 --- a/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_rbac_delegation_test.go +++ b/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_rbac_delegation_test.go @@ -33,9 +33,9 @@ import ( nvcaoptypes "github.com/NVIDIA/nvcf/src/compute-plane-services/nvca/pkg/operator/types" ) -// chartPath is the operator chart this test renders. Source of truth; the -// vendored copy under deploy/helm is generated from it. -const chartPath = "../../../deployments/nvca-operator" +// chartPath is the operator chart this test renders. It is the single +// published chart; there is no second copy to keep in step. +const chartPath = "../../../../../../deploy/helm/nvca-operator/nvca-operator" // Test_NVCAAgentRBAC_OperatorCanDelegate asserts the operator holds every // permission it grants to the agent. diff --git a/src/compute-plane-services/nvca/pkg/storage/BUILD.bazel b/src/compute-plane-services/nvca/pkg/storage/BUILD.bazel index 79e3397ab7..114b662512 100644 --- a/src/compute-plane-services/nvca/pkg/storage/BUILD.bazel +++ b/src/compute-plane-services/nvca/pkg/storage/BUILD.bazel @@ -123,7 +123,7 @@ go_test( "storage_resolution_test.go", ], data = [ - "//src/compute-plane-services/nvca:storage-capability-catalog", + "//:nvca-operator-storage-capability-catalog", "//src/compute-plane-services/nvca/internal/envtest:crds", ], embed = [":storage"], diff --git a/src/compute-plane-services/nvca/pkg/storage/storage_capabilities_test.go b/src/compute-plane-services/nvca/pkg/storage/storage_capabilities_test.go index f08edd16ce..b05645cac8 100644 --- a/src/compute-plane-services/nvca/pkg/storage/storage_capabilities_test.go +++ b/src/compute-plane-services/nvca/pkg/storage/storage_capabilities_test.go @@ -358,16 +358,19 @@ func TestValidateStorageCapabilityCatalogAllowsNothingQualified(t *testing.T) { require.NoError(t, validateStorageCapabilityCatalog(catalog)) } -// shippedChartDir locates the operator chart: Bazel runs the test from the -// runfiles root with the storage-capability-catalog data dependency, go test -// runs it from the package directory. +// shippedChartDir locates the operator chart. Bazel runs the test from the +// runfiles root with the catalog staged as a data dependency; go test runs it +// from the package directory. The chart lives outside this package either way. func shippedChartDir(t *testing.T) string { t.Helper() - chartDir := filepath.Join("src", "compute-plane-services", "nvca", "deployments", "nvca-operator") - if _, err := os.Stat(chartDir); os.IsNotExist(err) { - chartDir = filepath.Join("..", "..", "deployments", "nvca-operator") + rel := filepath.Join("deploy", "helm", "nvca-operator", "nvca-operator") + for _, candidate := range []string{rel, filepath.Join("_main", rel), filepath.Join("..", "..", "..", "..", "..", rel)} { + if _, err := os.Stat(filepath.Join(candidate, "files", "nvcf-storage-capabilities-v1alpha1.yaml")); err == nil { + return candidate + } } - return chartDir + t.Fatal("could not locate the nvca-operator chart files") + return "" } func TestShippedStorageCapabilityCatalog(t *testing.T) { diff --git a/src/compute-plane-services/nvca/scripts/ci_check_dotenv_dependencies b/src/compute-plane-services/nvca/scripts/ci_check_dotenv_dependencies index 044965073a..7415a1baae 100755 --- a/src/compute-plane-services/nvca/scripts/ci_check_dotenv_dependencies +++ b/src/compute-plane-services/nvca/scripts/ci_check_dotenv_dependencies @@ -1,7 +1,7 @@ #!/usr/bin/env bash # # This script exits non-zero if .env image tags are out of sync with the -# vendored operator chart defaults in deployments/nvca-operator/. +# operator chart defaults in deploy/helm/nvca-operator/nvca-operator/. set -eu -o pipefail repo_root="$(cd "$(dirname "$0")/.." && pwd)" cd "${repo_root}" diff --git a/src/compute-plane-services/nvca/scripts/ci_dotenv_dependencies_update b/src/compute-plane-services/nvca/scripts/ci_dotenv_dependencies_update index f4fbd3c8bc..93c633dc5c 100755 --- a/src/compute-plane-services/nvca/scripts/ci_dotenv_dependencies_update +++ b/src/compute-plane-services/nvca/scripts/ci_dotenv_dependencies_update @@ -1,8 +1,8 @@ #!/usr/bin/env bash # -# Sync .env image tags into the vendored operator chart defaults. +# Sync .env image tags into the operator chart defaults. # -# Keeps deployments/nvca-operator/{values.yaml,values.schema.json} +# Keeps the published operator chart's {values.yaml,values.schema.json} # consistent with the canonical tags declared in .env. Run via: # # make dotenv-dependencies-update @@ -12,7 +12,7 @@ set -eu -o pipefail repo_root="$(cd "$(dirname "$0")/.." && pwd)" cd "${repo_root}" -chart_dir="deployments/nvca-operator" +chart_dir="../../../deploy/helm/nvca-operator/nvca-operator" values_file="${chart_dir}/values.yaml" schema_file="${chart_dir}/values.schema.json" diff --git a/src/compute-plane-services/nvca/scripts/lint_helm.sh b/src/compute-plane-services/nvca/scripts/lint_helm.sh index a5de5424df..3ca7c42ec5 100755 --- a/src/compute-plane-services/nvca/scripts/lint_helm.sh +++ b/src/compute-plane-services/nvca/scripts/lint_helm.sh @@ -17,9 +17,13 @@ install_kubeconform() { run_lint() { local chart_name=${1} shift - local chart_dir="${repo_root}/deployments/${chart_name}" - local values_file="${repo_root}/deployments/${chart_name}/values.yaml" - local args=() + local chart_dir="${repo_root}/../../../deploy/helm/nvca-operator/${chart_name}" + local values_file="${repo_root}/../../../deploy/helm/nvca-operator/${chart_name}/values.yaml" + # The chart ships no default NGC service key. An ngc-managed install supplies + # one, and the generated image pull secret requires it, so lint renders the + # way a real install does. Helm applies --set-string after --set, so a caller + # cannot replace this with --set; pass --set-string to override it. + local args=(--set-string "ngcConfig.serviceKey=lint-service-key") # Process arguments while [[ $# -gt 0 ]]; do @@ -63,37 +67,43 @@ assert_pre_delete_cleanup_rbac() { trap 'rm -f "${rendered}"' RETURN local release_name="test-release" - local cleanup_name="${release_name}-nvca-operator-pre-delete-cleanup" - helm template "${release_name}" "${repo_root}/deployments/nvca-operator" \ + helm template "${release_name}" "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ --set "ngcConfig.serviceKey=fakekey" >"${rendered}" + # Read the hook object name from the render rather than rebuilding it here. + # The chart may set fullnameOverride, so a name assembled from the release + # name does not necessarily match what the chart emits. + local cleanup_name + cleanup_name="$(yq -r 'select(.kind == "Job" and (.metadata.name | test("pre-delete-cleanup"))) | .metadata.name' "${rendered}")" + test -n "${cleanup_name}" || { echo "pre-delete cleanup Job not rendered" >&2; return 1; } + export CLEANUP_NAME="${cleanup_name}" + assert_eq "${cleanup_name}" \ - "$(yq 'select(.kind == "Job" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .spec.template.spec.serviceAccountName' "${rendered}")" \ + "$(yq 'select(.kind == "Job" and .metadata.name == strenv(CLEANUP_NAME)) | .spec.template.spec.serviceAccountName' "${rendered}")" \ "pre-delete cleanup Job uses hook-scoped ServiceAccount" assert_eq "pre-delete" \ - "$(yq 'select(.kind == "ServiceAccount" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .metadata.annotations."helm.sh/hook"' "${rendered}")" \ + "$(yq 'select(.kind == "ServiceAccount" and .metadata.name == strenv(CLEANUP_NAME)) | .metadata.annotations."helm.sh/hook"' "${rendered}")" \ "pre-delete cleanup ServiceAccount is a pre-delete hook" assert_eq "-20" \ - "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .metadata.annotations."helm.sh/hook-weight"' "${rendered}")" \ + "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == strenv(CLEANUP_NAME)) | .metadata.annotations."helm.sh/hook-weight"' "${rendered}")" \ "pre-delete cleanup RBAC runs before cleanup Job" assert_eq "${cleanup_name}" \ - "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .subjects[0].name' "${rendered}")" \ + "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == strenv(CLEANUP_NAME)) | .subjects[0].name' "${rendered}")" \ "pre-delete cleanup ClusterRoleBinding binds hook ServiceAccount" assert_eq "${cleanup_name}" \ - "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .roleRef.name' "${rendered}")" \ + "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == strenv(CLEANUP_NAME)) | .roleRef.name' "${rendered}")" \ "pre-delete cleanup ClusterRoleBinding uses hook ClusterRole" assert_eq "before-hook-creation,hook-succeeded" \ - "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .metadata.annotations."helm.sh/hook-delete-policy"' "${rendered}")" \ + "$(yq 'select(.kind == "ClusterRoleBinding" and .metadata.name == strenv(CLEANUP_NAME)) | .metadata.annotations."helm.sh/hook-delete-policy"' "${rendered}")" \ "pre-delete cleanup ClusterRoleBinding is removed by Helm after job succeeds" assert_eq "before-hook-creation" \ - "$(yq 'select(.kind == "ClusterRole" and .metadata.name == "test-release-nvca-operator-pre-delete-cleanup") | .metadata.annotations."helm.sh/hook-delete-policy"' "${rendered}")" \ + "$(yq 'select(.kind == "ClusterRole" and .metadata.name == strenv(CLEANUP_NAME)) | .metadata.annotations."helm.sh/hook-delete-policy"' "${rendered}")" \ "pre-delete cleanup hook RBAC is kept for the running Job" } assert_storage_capability_catalog() ( - local service_chart="${repo_root}/deployments/nvca-operator" - local release_chart="${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" + local chart="${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" local catalog="files/nvcf-storage-capabilities-v1alpha1.yaml" local schema="files/nvcf-storage-capabilities-v1alpha1.schema.json" local template="templates/storage-capabilities-configmap.yaml" @@ -107,31 +117,27 @@ assert_storage_capability_catalog() ( --requirement "${repo_root}/scripts/requirements-lint.txt" schema_check='import json,sys,yaml,jsonschema; schema=json.load(open(sys.argv[1])); jsonschema.Draft202012Validator.check_schema(schema); jsonschema.Draft202012Validator(schema).validate(yaml.safe_load(open(sys.argv[2])))' - for relative in "${catalog}" "${schema}" "${template}"; do - diff -u "${service_chart}/${relative}" "${release_chart}/${relative}" - done - rendered="${tmpdir}/rendered.yaml" - helm template test-release "${service_chart}" --namespace nvca-system \ - --set "ngcConfig.serviceKey=fakekey" \ + helm template test-release "${chart}" --namespace nvca-system \ + --set-string "ngcConfig.serviceKey=lint-service-key" \ --show-only "${template}" >"${rendered}" assert_eq "nvcf-storage-capabilities" "$(yq -r ".metadata.name" "${rendered}")" \ "storage capability ConfigMap uses the stable name" assert_eq "nvca-system" "$(yq -r ".metadata.namespace" "${rendered}")" \ "storage capability ConfigMap is owned by the chart release namespace" - assert_eq "$(<"${service_chart}/${catalog}")" \ + assert_eq "$(<"${chart}/${catalog}")" \ "$(yq -r ".data.\"storage-provider-capabilities.yaml\"" "${rendered}")" \ "storage capability ConfigMap embeds the exact catalog payload" "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${service_chart}/${catalog}" + "${chart}/${schema}" "${chart}/${catalog}" invalid_catalog="${tmpdir}/invalid-catalog.yaml" for mutation in \ 'del((.drivers[] | select(.name == "csi.weka.io")).accessModes)' \ '(.drivers[] | select(.name == "csi.weka.io")).accessModes = null'; do - yq "${mutation}" "${service_chart}/${catalog}" >"${invalid_catalog}" + yq "${mutation}" "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject missing or null accessModes" >&2 return 1 fi @@ -141,9 +147,9 @@ assert_storage_capability_catalog() ( for mutation in \ 'del((.drivers[] | select(.name == "csi.weka.io")).readerMountOptions)' \ '(.drivers[] | select(.name == "csi.weka.io")).readerMountOptions = null'; do - yq "${mutation}" "${service_chart}/${catalog}" >"${invalid_catalog}" + yq "${mutation}" "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject missing or null readerMountOptions" >&2 return 1 fi @@ -151,18 +157,18 @@ assert_storage_capability_catalog() ( echo "PASS: schema rejects missing and null readerMountOptions" yq '(.drivers[] | select(.name == "nvmesh-csi.excelero.com")).readerMountOptions = ["ro", " norecovery", "nouuid"]' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject readerMountOptions with surrounding whitespace" >&2 return 1 fi echo "PASS: schema rejects readerMountOptions with surrounding whitespace" yq '(.drivers[] | select(.name == "csi.weka.io")).transitions.regularModelCache = "roxReadOnly"' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject a declared transition" >&2 return 1 fi @@ -173,9 +179,9 @@ assert_storage_capability_catalog() ( '["ro", "recovery", "norecovery", "nouuid"]' \ '["ro", "norecovery", "uuid", "nouuid"]'; do yq "(.drivers[] | select(.name == \"nvmesh-csi.excelero.com\")).readerMountOptions = ${options}" \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject conflicting readerMountOptions" >&2 return 1 fi @@ -184,67 +190,59 @@ assert_storage_capability_catalog() ( yq '((.drivers[] | select(.name == "csi.weka.io")).accessModes = ["ReadWriteOnce", "ReadOnlyMany"]) | ((.drivers[] | select(.name == "csi.weka.io")).readerMountOptions = [])' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject a ReadOnlyMany reader shape without ro" >&2 return 1 fi echo "PASS: schema rejects a ReadOnlyMany reader shape without a read-only mount" yq '(.drivers[] | select(.name == "csi.weka.io")).accessModes = ["ReadOnlyMany"]' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject ReadOnlyMany with no writer mode" >&2 return 1 fi echo "PASS: schema rejects ReadOnlyMany with no writer mode" yq '(.drivers[] | select(.name == "csi.weka.io")).accessModes = ["ReadWriteMany"]' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if ! "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}"; then + "${chart}/${schema}" "${invalid_catalog}"; then echo "Expected schema to accept a shared claim driver with no reader options" >&2 return 1 fi echo "PASS: schema accepts a shared claim driver with no reader options" yq 'del((.drivers[] | select(.name == "csi.weka.io")).name)' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject a driver with no name" >&2 return 1 fi echo "PASS: schema rejects a driver with no name" yq '(.drivers[] | select(.name == "csi.weka.io")).unexpected = true' \ - "${service_chart}/${catalog}" >"${invalid_catalog}" + "${chart}/${catalog}" >"${invalid_catalog}" if "${schema_python}" -c "${schema_check}" \ - "${service_chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then + "${chart}/${schema}" "${invalid_catalog}" 2>/dev/null; then echo "Expected schema to reject an unknown driver field" >&2 return 1 fi echo "PASS: schema rejects an unknown driver field" - helm template test-release "${release_chart}" --namespace nvca-system \ - --show-only "${template}" >"${rendered}" - assert_eq "nvca-system" "$(yq -r ".metadata.namespace" "${rendered}")" \ - "release-chart storage capability ConfigMap is owned by the release namespace" - assert_eq "$(<"${service_chart}/${catalog}")" \ - "$(yq -r ".data.\"storage-provider-capabilities.yaml\"" "${rendered}")" \ - "release chart embeds the exact catalog payload" - mkdir -p "${missing_chart}" - cp -a "${service_chart}/." "${missing_chart}/" + cp -a "${chart}/." "${missing_chart}/" rm -f "${missing_chart}/${catalog}" if helm template test-release "${missing_chart}" --set "ngcConfig.serviceKey=fakekey" >"${rendered}" 2>&1; then echo "Expected rendering without the storage capability catalog to fail" >&2 return 1 fi grep -q "required NVCF storage capability catalog" "${rendered}" - echo "PASS: storage capability catalog schema, render, payload, and chart parity" + echo "PASS: storage capability catalog schema, render, and payload" ) assert_storage_capability_catalog @@ -272,16 +270,37 @@ assert_distroless_operator_commands() { "${chart_label} cleanup Job starts the packaged binary directly" } -assert_distroless_operator_commands "${repo_root}/deployments/nvca-operator" "service chart" assert_distroless_operator_commands "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" "release chart" install_kubeconform assert_pre_delete_cleanup_rbac -run_lint nvca-operator --set "ngcConfig.serviceKey=fakekey" +run_lint nvca-operator run_lint nvca-operator --set "generateImagePullSecret=false" --set "imagePullSecretName=foo-bar-image-pull" +# The chart ships no default service key, so generating the pull secret has +# nothing to authenticate with. Refusing to render is what keeps a placeholder +# from reaching a cluster as a pull secret that fails at image pull instead. +echo -e "\nTesting NGC service key validation..." +missing_key_output="$(mktemp)" +if helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ + --set-string "ngcConfig.serviceKey=" \ + > "${missing_key_output}" 2>&1; then + echo "Expected render without an NGC service key to fail" + cat "${missing_key_output}" + rm -f "${missing_key_output}" + exit 1 +fi +if ! grep -q "NGC service key is required to create a pull secret" "${missing_key_output}"; then + echo "Expected the missing service key to be reported by name" + cat "${missing_key_output}" + rm -f "${missing_key_output}" + exit 1 +fi +rm -f "${missing_key_output}" +echo -e "Test passed" + echo -e "\nTesting self-managed endpoint validation..." missing_endpoint_output="$(mktemp)" -if helm template test-release "${repo_root}/deployments/nvca-operator" \ +if helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ --set "generateImagePullSecret=false" \ --set "ngcConfig.clusterSource=self-managed" \ --set-string "clusterID=id" \ @@ -328,7 +347,7 @@ assert_service_oauth_nil_safe() { shift local render_output render_output="$(mktemp)" - if ! helm template test-release "${repo_root}/deployments/nvca-operator" "$@" \ + if ! helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" "$@" \ --values "${reuse_values_file}" > "${render_output}" 2>&1; then echo "Expected ${label} cluster-dto to render without agent.serviceOAuth defaults" cat "${render_output}" @@ -392,7 +411,6 @@ assert_helm_managed_vault_address() { done } -assert_helm_managed_vault_address "${repo_root}/deployments/nvca-operator" "service chart" assert_helm_managed_vault_address "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" "release chart" assert_service_oauth_nil_safe "self-managed" \ @@ -420,7 +438,7 @@ assert_transport_trust_config() { local render_output render_output="$(mktemp)" - if ! helm template test-release "${repo_root}/deployments/nvca-operator" "$@" \ + if ! helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" "$@" \ --show-only templates/operator-config-cm.yaml > "${render_output}" 2>&1; then echo "Expected ${label} workload transport trust ConfigMap to render" cat "${render_output}" @@ -466,36 +484,35 @@ bash "${repo_root}/scripts/test_transport_trust_validation.sh" # Test secret mirroring feature # Test with only source namespace (should not add args) -run_lint nvca-operator --set "agent.secretMirror.sourceNamespace=custom-ns" --set "ngcConfig.serviceKey=fakekey" +run_lint nvca-operator --set "agent.secretMirror.sourceNamespace=custom-ns" # Test with both source namespace and label selector (should add args) -run_lint nvca-operator --set "agent.secretMirror.sourceNamespace=custom-ns" --set "agent.secretMirror.labelSelector=mirror=true" --set "ngcConfig.serviceKey=fakekey" +run_lint nvca-operator --set "agent.secretMirror.sourceNamespace=custom-ns" --set "agent.secretMirror.labelSelector=mirror=true" # Test custom annotations feature -run_lint nvca-operator --values "${repo_root}/test/test-custom-annotations.yaml" --set "ngcConfig.serviceKey=fakekey" +run_lint nvca-operator --values "${repo_root}/test/test-custom-annotations.yaml" # Test both features together run_lint nvca-operator \ --set "agent.secretMirror.sourceNamespace=custom-ns" \ --set "agent.secretMirror.labelSelector=mirror=true" \ - --values "${repo_root}/test/test-custom-annotations.yaml" \ - --set "ngcConfig.serviceKey=fakekey" + --values "${repo_root}/test/test-custom-annotations.yaml" # Test network policies feature -run_lint nvca-operator --values "${repo_root}/test/test-network-policies.yaml" --set "ngcConfig.serviceKey=fakekey" +run_lint nvca-operator --values "${repo_root}/test/test-network-policies.yaml" # Test network policies with annotations -run_lint nvca-operator --values "${repo_root}/test/test-network-policies.yaml" --values "${repo_root}/test/test-custom-annotations.yaml" --set "ngcConfig.serviceKey=fakekey" +run_lint nvca-operator --values "${repo_root}/test/test-network-policies.yaml" --values "${repo_root}/test/test-custom-annotations.yaml" # Test ConfigMaps contain expected structure when custom values provided echo "Testing ConfigMap structure with custom values..." -helm template test-release "${repo_root}/deployments/nvca-operator" \ +helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ --values "${repo_root}/test/test-network-policies.yaml" \ --set "ngcConfig.serviceKey=fakekey" \ --show-only templates/custom-network-policies-configmap.yaml \ | grep -q "nvcf-custom-network-policies" && echo "ok Network policies ConfigMap created" || echo "FAIL Network policies ConfigMap missing" -helm template test-release "${repo_root}/deployments/nvca-operator" \ +helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ --values "${repo_root}/test/test-custom-annotations.yaml" \ --set "ngcConfig.serviceKey=fakekey" \ --show-only templates/custom-annotations-configmap.yaml \ @@ -503,12 +520,12 @@ helm template test-release "${repo_root}/deployments/nvca-operator" \ # Test ConfigMaps are created even without custom values (always created behavior) echo "Testing ConfigMaps are always created..." -helm template test-release "${repo_root}/deployments/nvca-operator" \ +helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ --set "ngcConfig.serviceKey=fakekey" \ --show-only templates/custom-annotations-configmap.yaml \ | grep -q "nvca-namespace-pod-annotations" && echo "ok Annotations ConfigMap always created" || echo "FAIL Annotations ConfigMap not created" -helm template test-release "${repo_root}/deployments/nvca-operator" \ +helm template test-release "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator" \ --set "ngcConfig.serviceKey=fakekey" \ --show-only templates/custom-network-policies-configmap.yaml \ | grep -q "nvcf-custom-network-policies" && echo "ok Network policies ConfigMap always created" || echo "FAIL Network policies ConfigMap not created" diff --git a/src/compute-plane-services/nvca/scripts/test_transport_trust_validation.sh b/src/compute-plane-services/nvca/scripts/test_transport_trust_validation.sh index c5293839fd..f78a271bbe 100755 --- a/src/compute-plane-services/nvca/scripts/test_transport_trust_validation.sh +++ b/src/compute-plane-services/nvca/scripts/test_transport_trust_validation.sh @@ -116,7 +116,6 @@ assert_valid() { } for chart_dir in \ - "${repo_root}/deployments/nvca-operator" \ "${repo_root}/../../../deploy/helm/nvca-operator/nvca-operator"; do chart_label="$(basename "$(dirname "${chart_dir}")")/$(basename "${chart_dir}")" assert_invalid "${chart_dir}" "${chart_label} direct bundle input" "${test_dir}/invalid-direct.yaml" diff --git a/tools/ci/github-release b/tools/ci/github-release index 5c0e1b6005..1a3b65e1fc 100755 --- a/tools/ci/github-release +++ b/tools/ci/github-release @@ -935,8 +935,15 @@ def helm_registry_login(registry, api_key): raise SystemExit(f"Helm registry login failed for {host}") -def package_release_chart(chart_dir, version, output_dir): - """Build chart dependencies and package an isolated copy at the tag version.""" +def package_release_chart(chart_dir, version, output_dir, app_version=None): + """Build chart dependencies and package an isolated copy at the tag version. + + `app_version` stamps Chart.yaml's appVersion into the package. A chart that + follows a leader deploys the release its version came from, and the chart + ships an empty image tag so appVersion is what the templates resolve. Left + at whatever is committed, the published chart would point at whichever + release last edited the file. + """ staged_chart = output_dir / "source" / chart_dir.name staged_chart.parent.mkdir(parents=True) shutil.copytree(chart_dir, staged_chart) @@ -951,7 +958,10 @@ def package_release_chart(chart_dir, version, output_dir): package_dir = output_dir / "package" package_dir.mkdir() - run(["helm", "package", str(staged_chart), "--version", version, "--destination", str(package_dir)]) + package_cmd = ["helm", "package", str(staged_chart), "--version", version] + if app_version: + package_cmd += ["--app-version", app_version] + run(package_cmd + ["--destination", str(package_dir)]) packages = list(package_dir.glob("*.tgz")) if len(packages) != 1: raise SystemExit(f"expected one packaged Helm chart, found {len(packages)}") @@ -1017,7 +1027,11 @@ def publish_release_chart(root, service, version, dry_run): registry, api_key = helm_registry_settings() with tempfile.TemporaryDirectory(prefix="nvcf-release-chart-") as tmp: package_root = Path(tmp) - local_package = package_release_chart(chart_dir, version, package_root) + # A follower's version is its leader's, so it is also the operator + # version the chart deploys. Any other chart keeps the appVersion its + # own bump flow maintains. + app_version = version if service.get("version_follows") else None + local_package = package_release_chart(chart_dir, version, package_root, app_version) helm_registry_login(registry, api_key) status, output, remote_packages = pull_release_chart( registry, chart_name, version, package_root / "published" @@ -1229,14 +1243,21 @@ def comment_release_on_pull_requests(root, service, tag, version, since_tag): return commented -def publish_tag_for_version(root, service, version, dry_run, draft, reason): +def publish_tag_for_version(root, service, version, dry_run, draft, reason, at_commit=None): + """Create and publish a release tag. + + `at_commit` pins the tag to a specific commit instead of HEAD. A follower tag + must land on the commit its leader tag already points at: the default branch + can advance between the two, and tagging HEAD would put them on different + trees for the same release. + """ tag = tag_for_version(service, version, root) - head = run(["git", "rev-parse", "--verify", "HEAD^{commit}"], cwd=root, capture=True).strip() + head = at_commit or run(["git", "rev-parse", "--verify", "HEAD^{commit}"], cwd=root, capture=True).strip() current = run(["git", "rev-parse", "-q", "--verify", f"refs/tags/{tag}"], cwd=root, capture=True, check=False).strip() if current: existing_sha = tag_sha(root, tag) if existing_sha == head: - print(f"[github-release] {service['id']}: {tag} already points at HEAD; skipping") + print(f"[github-release] {service['id']}: {tag} already points at {head[:9]}; skipping") return raise SystemExit(f"{service['id']}: {tag} already exists at {existing_sha}, not {head}") for prefix in tag_prefixes(service, root): @@ -1250,12 +1271,12 @@ def publish_tag_for_version(root, service, version, dry_run, draft, reason): print(f"[github-release] {service['id']}: legacy anchor {existing} points at HEAD; creating current tag {tag}") notes = f"Release {tag}\n\nPackage version: {version}\nSource: {reason}" if dry_run: - print(f"[github-release] {service['id']}: would create {tag} at HEAD") + print(f"[github-release] {service['id']}: would create {tag} at {head[:9]}") print(notes) return # Resolved before tagging so HEAD's own tag cannot bound the range. since_tag = ancestor_service_tag(root, service) - run(["git", "tag", tag, "HEAD"], cwd=root) + run(["git", "tag", tag, head], cwd=root) run(["git", "push", "origin", f"refs/tags/{tag}"], cwd=root) created = create_release(tag, tag, notes, draft, dry_run=False) # Only for a release this run actually created, so a re-run does not comment @@ -1312,22 +1333,101 @@ def next_patch_version(version): return f"{major}.{minor}.{patch + 1}" -def releases_a_version(subject): - """Whether a commit subject releases a version under RELEASE_RULES. +# Release levels, ordered. `None` is "releases nothing"; comparing by rank is how +# levels from two owned paths are aggregated. +RELEASE_LEVEL_RANK = {None: 0, "patch": 1, "minor": 2, "major": 3} + + +def release_level(subject): + """The release level of a commit subject under RELEASE_RULES. - Derived from RELEASE_RULES so the fan-out applies exactly the rules - semantic-release is configured with: `feat`, `fix`, and `perf` release, the - other declared types do not, and a `!` breaking marker always releases. A - subject that is not a Conventional Commit releases nothing, the same as it - would inside a service directory. + Returns None, "patch", "minor" or "major". Derived from RELEASE_RULES so it + applies exactly the rules semantic-release is configured with: `feat` is a + minor, `fix` and `perf` are patches, the other declared types release + nothing, and a `!` breaking marker is always a major. A subject that is not a + Conventional Commit releases nothing, the same as it would inside a service + directory. """ match = COMMIT_SUBJECT_PATTERN.match(subject) if not match: - return False + return None if match.group("breaking"): - return True - releasing = {rule["type"] for rule in RELEASE_RULES if rule["release"]} - return match.group("type").lower() in releasing + return "major" + by_type = {rule["type"]: rule["release"] for rule in RELEASE_RULES} + return by_type.get(match.group("type").lower()) or None + + +def higher_release_level(left, right): + return left if RELEASE_LEVEL_RANK[left] >= RELEASE_LEVEL_RANK[right] else right + + +def bump_version(version, level): + """Apply a release level to a stable X.Y.Z version.""" + if not level: + return None + major, minor, patch = (int(part) for part in version.split(".")) + if level == "major": + return f"{major + 1}.0.0" + if level == "minor": + return f"{major}.{minor + 1}.0" + return f"{major}.{minor}.{patch + 1}" + + +def releases_a_version(subject): + """Whether a commit subject releases a version under RELEASE_RULES. + + Expressed through release_level so the rules live in exactly one place. + """ + return release_level(subject) is not None + + +def owned_release_paths(service): + """Extra paths this service cuts releases for, beyond its own directory. + + semantic-release is scoped to the service directory by + semantic-release-monorepo, so a path outside it is invisible to the version + calculation. Declaring it here is what lets one release cover both. + """ + return list(service.get("owns_paths") or []) + + +def owned_paths_release_level(root, service, since_tag, end="HEAD"): + """Highest release level from commits touching the service's extra owned paths. + + An entry marked `packaged` applies a patch floor: bytes that enter a + published artifact have to ship, even when the commit subject would release + nothing on its own (`chore`, `docs`, or a subject that is not a Conventional + Commit). Paths that only carry tooling or tests around the artifact get no + floor, so touching them alone does not cut a product release. + + Commits touching both this path and the service directory are not + deduplicated because levels aggregate by maximum, which is idempotent. + """ + level = None + reasons = [] + for entry in owned_release_paths(service): + path = entry.get("path") + if not path: + raise SystemExit(f"{service['id']}: owns_paths entry is missing a path") + raw = run( + ["git", "log", "--format=%h %s", f"{since_tag}..{end}", "--", path], + cwd=root, + capture=True, + ) + for line in raw.splitlines(): + commit = line.strip() + if not commit: + continue + sha, _, subject = commit.partition(" ") + commit_level = release_level(subject) + if commit_level is None and entry.get("packaged"): + commit_level = "patch" + if commit_level is None: + continue + if RELEASE_LEVEL_RANK[commit_level] > RELEASE_LEVEL_RANK[level]: + level = commit_level + reasons.append(f"{sha} {commit_level}: {subject}") + return level, reasons def release_worthy_framework_commits_since(root, framework_paths, since_tag): @@ -1432,6 +1532,99 @@ def publish_framework_dependency_release(root, service, components, dry_run, dra return True +def multi_path_release_version(root, service, semantic_version): + """The version to cut once the service's extra owned paths are accounted for. + + semantic-release is scoped to the service directory, so it never sees the + owned paths. This compares the level it computed against the level those + paths demand and returns whichever is higher. + + Returns (version, source): + ("3.13.0", "semantic-release") semantic-release already covers it, let it publish + ("3.12.2", "owned-paths") the owned paths demand a release it did not cut + (None, "") nothing to release + """ + owned_level, reasons = owned_paths_release_level(root, service, ancestor_service_tag(root, service)) + if not owned_level: + return (semantic_version, "semantic-release") if semantic_version else (None, "") + + last_tag = latest_service_tag(service, root) + if not last_tag: + # No baseline to bump from. semantic-release owns the first release. + return (semantic_version, "semantic-release") if semantic_version else (None, "") + current = version_from_tag(service, last_tag, root) + if not re.fullmatch(STABLE_SEMVER_PATTERN, current): + return (semantic_version, "semantic-release") if semantic_version else (None, "") + + owned_version = bump_version(current, owned_level) + if not semantic_version: + print( + f"[github-release] {service['id']}: owned paths require a {owned_level} " + f"release that semantic-release did not cut; {len(reasons)} commit(s)" + ) + for reason in reasons[:MAX_QUOTED_FRAMEWORK_COMMITS]: + print(f"[github-release] {reason}") + return owned_version, "owned-paths" + + # Both want a release. Compare by resulting version rather than by level: + # semantic-release reports a version, not a level, and the two must not + # disagree about the baseline. + if semverish_sort_key(owned_version) > semverish_sort_key(semantic_version): + print( + f"[github-release] {service['id']}: owned paths require {owned_version}, " + f"above the {semantic_version} semantic-release computed" + ) + return owned_version, "owned-paths" + return semantic_version, "semantic-release" + + +def publish_follower_release(root, service, metadata, dry_run, draft): + """Tag a service at the version and commit its leader already released. + + A follower computes nothing. It reads the leader's newest tag, takes that + version, and creates its own tag on the very commit the leader tag points + at. Tagging HEAD instead would let the default branch advance between the + two and put one release on two different trees. + """ + leader_id = service["version_follows"] + leader = find_service(metadata, leader_id) + # latest_service_tag orders prereleases above the stable release they lead + # to, so it can hand back an rc the follower must refuse, leaving the stable + # tag unfollowed. Select from the stable, reachable set instead, the same + # set semantic-release resolves a baseline from. + stable = stable_reachable_versions(root, leader) + leader_tag = ( + sorted(stable, key=lambda item: semverish_sort_key(item[0]))[-1][1] if stable else "" + ) + if not leader_tag: + print( + f"[github-release] {service['id']}: {leader_id} has no release tag to follow; " + "nothing to do" + ) + return + version = version_from_tag(leader, leader_tag, root) + if not re.fullmatch(STABLE_SEMVER_PATTERN, version): + print( + f"[github-release] {service['id']}: {leader_tag} is not a stable X.Y.Z; " + "not following a prerelease" + ) + return + commit = tag_sha(root, leader_tag) + print( + f"[github-release] {service['id']}: following {leader_id} {version} " + f"at {commit[:9]}" + ) + publish_tag_for_version( + root, + service, + version, + dry_run, + draft, + f"follows {leader_id} release {leader_tag}", + at_commit=commit, + ) + + def resolve_release_outcome(exit_code, output): """Classify a semantic-release run: it released, it released nothing, or it is unreadable.""" if exit_code != 0: @@ -1802,9 +1995,17 @@ def auto_release(args): # hide that. stale_checkouts = [] + # Followers are held back until every leader has run. Their version comes + # from the leader's tag, so processing them in declaration order would make + # the result depend on where they sit in the metadata file. + followers = [] + for service in metadata.get("services", []): if not should_process_auto_service(service, service_filter, branch, default_branch, root): continue + if service.get("version_follows"): + followers.append(service) + continue service_dir = root / service["path"] if not service_dir.is_dir(): print(f"[github-release] {service['id']}: {service['path']} is not present in this checkout; skipping") @@ -1857,7 +2058,63 @@ def auto_release(args): reason = f"semantic-release dry-run exited {code} without a trustworthy release decision" print(f"[github-release] WARNING: {service['id']}: {reason}; continuing (dry-run preview)") dry_run_failures.append((service["id"], reason)) + if owned_release_paths(service) and outcome in ("released", "no-release"): + # Preview what publishing would actually do. Without this the + # dry run reports semantic-release's answer alone, which is + # not the answer a service owning extra paths would get. + previewed = parse_next_version(output) if outcome == "released" else "" + version, source = multi_path_release_version(root, service, previewed) + if not version: + print(f"[github-release] {service['id']}: would release nothing") + else: + print( + f"[github-release] {service['id']}: would release {version} " + f"(decided by {source})" + ) else: + if owned_release_paths(service): + # semantic-release publishes as soon as it computes a + # version, so a service that also owns paths outside its + # directory has to be previewed first. Otherwise a chart-only + # change that demands a larger bump arrives after the smaller + # one is already tagged. + preview_cmd = ["npx", "semantic-release", "--dry-run", "--no-ci"] + preview_code, preview_output = stream(preview_cmd, cwd=service_dir) + preview = resolve_release_outcome(preview_code, preview_output) + if preview == "stale-checkout": + print( + f"[github-release] {service['id']}: preview saw a checkout behind " + "main; not releasing" + ) + stale_checkouts.append(service["id"]) + continue + if preview == "unknown": + # An unreadable preview is not a "nothing to release". + # Treating it as one would either skip the publish run or + # cut an owned-path tag without a trustworthy baseline. + raise SystemExit( + f"[github-release] ERROR: {service['id']}: semantic-release preview " + f"exited {preview_code} without a trustworthy release decision; " + "not releasing from an unreadable preview" + ) + previewed = parse_next_version(preview_output) if preview == "released" else "" + version, source = multi_path_release_version(root, service, previewed) + if source == "owned-paths": + # semantic-release would under-release. Cut the tag here + # instead, the same way the framework fan-out does. + publish_tag_for_version( + root, + service, + version, + dry_run=False, + draft=draft, + reason=f"owned release paths under {service['path']}", + ) + continue + if not version: + print(f"[github-release] {service['id']}: no release-worthy commits") + continue + # Streamed rather than run() so the result is readable: a # dependency fan-out must only fire when semantic-release itself # released nothing. @@ -1878,6 +2135,13 @@ def auto_release(args): finally: print("::endgroup::") + for service in followers: + print(f"::group::github-release {service['id']} (follower)") + try: + publish_follower_release(root, service, metadata, dry_run=dry_run, draft=draft) + finally: + print("::endgroup::") + if dry_run and dry_run_failures: print( f"[github-release] dry-run preview: {len(dry_run_failures)} service(s) " diff --git a/tools/ci/github-release-subprojects.json b/tools/ci/github-release-subprojects.json index 19a647bf27..d885cd286a 100644 --- a/tools/ci/github-release-subprojects.json +++ b/tools/ci/github-release-subprojects.json @@ -209,7 +209,13 @@ "path": "src/compute-plane-services/nvca", "service_name": "nvca", "legacy_tag_prefix": "nvca-v", - "initial_version": "3.3.0" + "initial_version": "3.3.0", + "owns_paths": [ + { + "path": "deploy/helm/nvca-operator/nvca-operator", + "packaged": true + } + ] }, { "id": "container-cache", @@ -352,7 +358,6 @@ "service_name": "helm-nvca-operator", "legacy_tag_prefix": "helm-nvca-operator-v", "deploys": [ - "nvca", { "service": "byoo-otel-collector", "values_paths": [ @@ -362,7 +367,8 @@ "selfManaged.otelCollector.imageTag" ] } - ] + ], + "version_follows": "nvca" }, { "id": "cassandra-migrations", diff --git a/tools/ci/test-github-release.py b/tools/ci/test-github-release.py index 29e845221f..e5a3d6cefc 100644 --- a/tools/ci/test-github-release.py +++ b/tools/ci/test-github-release.py @@ -32,6 +32,13 @@ def git(root, *args): subprocess.run(["git", *args], cwd=root, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) +def git_out(root, *args): + """git, returning stdout. The plain helper above discards it.""" + return subprocess.run( + ["git", *args], cwd=root, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ).stdout + + class SubprocessShim: """Stands in for the module's `subprocess`, intercepting only `gh` calls. @@ -2357,5 +2364,312 @@ def test_success_comment_wording_matches_the_kind_of_thing_it_is_posted_on(self) ) +class MultiPathReleaseTest(unittest.TestCase): + """One release version covering a service directory and a path outside it.""" + + setUp = GithubReleaseTest.setUp + init_repo = GithubReleaseTest.init_repo + seed_nvca_service = GithubReleaseTest.seed_nvca_service + commit_all = GithubReleaseTest.commit_all + + CHART_PATH = "deploy/helm/nvca-operator/nvca-operator" + + def nvca_metadata(self, owns_paths=True): + service = { + "id": "nvca", + "path": "src/compute-plane-services/nvca", + "service_name": "nvca", + } + if owns_paths: + service["owns_paths"] = [{"path": self.CHART_PATH, "packaged": True}] + return service + + def seed_chart(self, root): + chart_dir = root / self.CHART_PATH + chart_dir.mkdir(parents=True, exist_ok=True) + (chart_dir / "Chart.yaml").write_text("name: helm-nvca-operator\n") + + def init_multi_path_repo(self, root, remote=None): + self.init_repo(root) + self.seed_nvca_service(root) + self.seed_chart(root) + self.commit_all(root, "seed") + git(root, "tag", "src/compute-plane-services/nvca/v3.12.1") + if remote is not None: + subprocess.run( + ["git", "init", "--bare", "--initial-branch=main", str(remote)], + check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + ) + git(root, "remote", "add", "origin", str(remote)) + git(root, "push", "origin", "HEAD") + self.github_release.create_release = lambda tag, title, notes, draft, dry_run: None + + def touch(self, root, relative, message): + target = root / relative + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(target.read_text() + "x\n" if target.exists() else "x\n") + self.commit_all(root, message) + + def test_release_level_follows_the_configured_rules(self): + level = self.github_release.release_level + self.assertEqual(level("feat(nvca): add a thing"), "minor") + self.assertEqual(level("fix(nvca): correct a thing"), "patch") + self.assertEqual(level("perf(nvca): speed a thing"), "patch") + self.assertIsNone(level("chore(nvca): tidy")) + self.assertIsNone(level("docs: explain")) + self.assertEqual(level("fix(nvca)!: break a thing"), "major") + self.assertEqual(level("feat!: break a thing"), "major") + # Not a Conventional Commit, so it releases nothing, the same as it + # would inside a service directory. + self.assertIsNone(level("Merge branch 'main' into topic")) + + def test_releases_a_version_still_agrees_with_release_level(self): + for subject in ("feat: a", "fix: b", "chore: c", "docs!: d", "not conventional"): + self.assertEqual( + self.github_release.releases_a_version(subject), + self.github_release.release_level(subject) is not None, + subject, + ) + + def test_bump_version_applies_the_level(self): + bump = self.github_release.bump_version + self.assertEqual(bump("3.12.1", "patch"), "3.12.2") + self.assertEqual(bump("3.12.1", "minor"), "3.13.0") + self.assertEqual(bump("3.12.1", "major"), "4.0.0") + self.assertIsNone(bump("3.12.1", None)) + + def test_packaged_path_applies_a_patch_floor(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + self.init_multi_path_repo(root) + # A chore releases nothing on its own, but it changed bytes that + # enter the published artifact, so it must still ship. + self.touch(root, f"{self.CHART_PATH}/values.yaml", "chore(chart): retune a default") + level, reasons = self.github_release.owned_paths_release_level( + root, self.nvca_metadata(), "src/compute-plane-services/nvca/v3.12.1" + ) + self.assertEqual(level, "patch") + self.assertEqual(len(reasons), 1) + + def test_unpackaged_path_gets_no_floor(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + self.init_multi_path_repo(root) + service = self.nvca_metadata() + service["owns_paths"] = [{"path": self.CHART_PATH, "packaged": False}] + self.touch(root, f"{self.CHART_PATH}/values.yaml", "chore(chart): retune a default") + level, _ = self.github_release.owned_paths_release_level( + root, service, "src/compute-plane-services/nvca/v3.12.1" + ) + self.assertIsNone(level) + + def test_owned_path_feat_outranks_a_service_fix(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + self.init_multi_path_repo(root) + self.touch(root, f"{self.CHART_PATH}/values.yaml", "feat(chart): expose a setting") + version, source = self.github_release.multi_path_release_version( + root, self.nvca_metadata(), "3.12.2" + ) + # semantic-release saw only a patch in the service directory; the + # chart's feat is the higher level and wins. + self.assertEqual(version, "3.13.0") + self.assertEqual(source, "owned-paths") + + def test_service_level_wins_when_it_is_higher(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + self.init_multi_path_repo(root) + self.touch(root, f"{self.CHART_PATH}/values.yaml", "fix(chart): correct a default") + version, source = self.github_release.multi_path_release_version( + root, self.nvca_metadata(), "3.13.0" + ) + self.assertEqual(version, "3.13.0") + self.assertEqual(source, "semantic-release") + + def test_no_owned_paths_leaves_semantic_release_alone(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) + self.init_multi_path_repo(root) + self.touch(root, f"{self.CHART_PATH}/values.yaml", "feat(chart): expose a setting") + version, source = self.github_release.multi_path_release_version( + root, self.nvca_metadata(owns_paths=False), "3.12.2" + ) + self.assertEqual(version, "3.12.2") + self.assertEqual(source, "semantic-release") + + +class FollowerReleaseTest(unittest.TestCase): + """A follower tag lands on the leader's commit, not on HEAD.""" + + setUp = GithubReleaseTest.setUp + init_repo = GithubReleaseTest.init_repo + seed_nvca_service = GithubReleaseTest.seed_nvca_service + commit_all = GithubReleaseTest.commit_all + + CHART_PATH = MultiPathReleaseTest.CHART_PATH + nvca_metadata = MultiPathReleaseTest.nvca_metadata + seed_chart = MultiPathReleaseTest.seed_chart + init_multi_path_repo = MultiPathReleaseTest.init_multi_path_repo + touch = MultiPathReleaseTest.touch + + def follower_metadata(self): + return { + "id": "nvca-operator", + "path": "deploy/helm/nvca-operator", + "service_name": "helm-nvca-operator", + "version_follows": "nvca", + } + + def metadata(self): + return {"services": [self.nvca_metadata(), self.follower_metadata()]} + + def test_follower_tags_the_leaders_commit_after_main_advances(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "repo" + root.mkdir() + self.init_multi_path_repo(root, remote=Path(tmp) / "remote.git") + self.touch(root, "src/compute-plane-services/nvca/a.go", "fix(nvca): correct a thing") + git(root, "tag", "src/compute-plane-services/nvca/v3.12.2") + leader_commit = git_out(root, "rev-parse", "HEAD").strip() + + # main moves on before the follower is created. Tagging HEAD here + # would put one release on two different trees. + self.touch(root, "unrelated.txt", "docs: something else entirely") + self.assertNotEqual(git_out(root, "rev-parse", "HEAD").strip(), leader_commit) + + self.github_release.publish_follower_release( + root, self.follower_metadata(), self.metadata(), dry_run=False, draft=False + ) + + tag = "deploy/helm/nvca-operator/v3.12.2" + self.assertEqual(git_out(root, "rev-parse", f"{tag}^{{commit}}").strip(), leader_commit) + + def test_follower_resolves_an_annotated_leader_tag_to_its_commit(self): + """An annotated tag is its own object, not the commit it points at. + + semantic-release creates annotated tags, so resolving one with + rev-parse rather than peeling it would hand the follower a tag object. + The follower tag would then point at that object instead of the commit, + which peeling in the assertion would hide. Compare the direct target. + """ + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "repo" + root.mkdir() + self.init_multi_path_repo(root, remote=Path(tmp) / "remote.git") + self.touch(root, "src/compute-plane-services/nvca/a.go", "fix(nvca): correct a thing") + leader_commit = git_out(root, "rev-parse", "HEAD").strip() + git(root, "tag", "-a", "src/compute-plane-services/nvca/v3.12.2", "-m", "release 3.12.2") + leader_tag_object = git_out( + root, "rev-parse", "src/compute-plane-services/nvca/v3.12.2" + ).strip() + self.assertNotEqual(leader_tag_object, leader_commit, "tag should be annotated") + + self.github_release.publish_follower_release( + root, self.follower_metadata(), self.metadata(), dry_run=False, draft=False + ) + + follower = "deploy/helm/nvca-operator/v3.12.2" + self.assertEqual(git_out(root, "rev-parse", follower).strip(), leader_commit) + + def test_follower_is_idempotent(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "repo" + root.mkdir() + self.init_multi_path_repo(root, remote=Path(tmp) / "remote.git") + git(root, "tag", "src/compute-plane-services/nvca/v3.12.2") + for _ in range(2): + self.github_release.publish_follower_release( + root, self.follower_metadata(), self.metadata(), dry_run=False, draft=False + ) + tags = git_out(root, "tag", "-l", "deploy/helm/nvca-operator/v*").split() + self.assertEqual(tags, ["deploy/helm/nvca-operator/v3.12.2"]) + + def test_follower_refuses_a_conflicting_existing_tag(self): + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "repo" + root.mkdir() + self.init_multi_path_repo(root, remote=Path(tmp) / "remote.git") + git(root, "tag", "src/compute-plane-services/nvca/v3.12.2") + # Someone already created the follower tag somewhere else. + self.touch(root, "unrelated.txt", "docs: elsewhere") + git(root, "tag", "deploy/helm/nvca-operator/v3.12.2") + with self.assertRaises(SystemExit): + self.github_release.publish_follower_release( + root, self.follower_metadata(), self.metadata(), dry_run=False, draft=False + ) + + def test_follower_follows_the_stable_tag_under_a_newer_prerelease(self): + """A prerelease must not hide the stable release the follower owes a tag.""" + with tempfile.TemporaryDirectory() as tmp: + root = Path(tmp) / "repo" + root.mkdir() + self.init_multi_path_repo(root, remote=Path(tmp) / "remote.git") + self.touch(root, "src/compute-plane-services/nvca/a.go", "fix(nvca): correct a thing") + git(root, "tag", "src/compute-plane-services/nvca/v3.12.2") + stable_commit = git_out(root, "rev-parse", "HEAD").strip() + # A release candidate cut afterwards sorts above the stable release. + self.touch(root, "src/compute-plane-services/nvca/b.go", "feat(nvca): start the next line") + git(root, "tag", "src/compute-plane-services/nvca/v3.13.0-rc.1") + + self.github_release.publish_follower_release( + root, self.follower_metadata(), self.metadata(), dry_run=False, draft=False + ) + + tag = "deploy/helm/nvca-operator/v3.12.2" + self.assertEqual( + git_out(root, "tag", "-l", "deploy/helm/nvca-operator/v*").split(), [tag] + ) + self.assertEqual(git_out(root, "rev-parse", f"{tag}^{{commit}}").strip(), stable_commit) + + +class PackagedAppVersionTest(unittest.TestCase): + """A follower chart publishes the operator version its release carries.""" + + setUp = GithubReleaseTest.setUp + + def chart(self, tmp, app_version="3.10.0"): + chart_dir = Path(tmp) / "nvca-operator" + chart_dir.mkdir() + (chart_dir / "Chart.yaml").write_text( + "apiVersion: v2\n" + "name: helm-nvca-operator\n" + "version: 0.0.0\n" + f'appVersion: "{app_version}"\n' + ) + (chart_dir / "values.yaml").write_text("image:\n tag: \"\"\n") + return chart_dir + + def packaged_app_version(self, package): + out = subprocess.run( + ["helm", "show", "chart", str(package)], + check=True, stdout=subprocess.PIPE, text=True, + ).stdout + for line in out.splitlines(): + if line.startswith("appVersion:"): + return line.split(":", 1)[1].strip().strip('"') + return "" + + def test_follower_package_carries_the_release_version(self): + with tempfile.TemporaryDirectory() as tmp: + out = Path(tmp) / "out" + out.mkdir() + package = self.github_release.package_release_chart( + self.chart(tmp), "3.13.0", out, app_version="3.13.0" + ) + # Committed appVersion was 3.10.0. Publishing it unchanged would ship + # a chart that resolves the operator image to a superseded release. + self.assertEqual(self.packaged_app_version(package), "3.13.0") + + def test_other_charts_keep_their_committed_app_version(self): + with tempfile.TemporaryDirectory() as tmp: + out = Path(tmp) / "out" + out.mkdir() + package = self.github_release.package_release_chart( + self.chart(tmp), "1.28.4", out + ) + self.assertEqual(self.packaged_app_version(package), "3.10.0") + + if __name__ == "__main__": unittest.main()