Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ jobs:
steps:
- uses: actions/checkout@v4
with:
# Collector tests render the stack's pinned, independently released chart.
fetch-depth: 0
persist-credentials: false

# Pinned and checksum-verified rather than floating latest, so a chart
Expand Down Expand Up @@ -124,7 +126,7 @@ jobs:
- name: Test NVCA default ownership
run: |
make -C deploy/helm/nvca-operator test-default-ownership
make -C deploy/stacks/nvcf-compute-plane test-nvca-default-ownership
make -C deploy/stacks/nvcf-compute-plane test-nvca-default-ownership test-collector-images

- name: Run self-managed Helmfile render tests
run: make -C deploy/stacks/self-managed test
Expand Down
6 changes: 5 additions & 1 deletion deploy/stacks/nvcf-compute-plane/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ test-nvca-entrypoints: render-local
test-nvca-default-ownership:
@tests/nvca-default-ownership.sh

.PHONY: test-collector-images
test-collector-images:
@bash tests/collector-images.sh

test-compare-golden:
@tests/test-compare-golden.sh

Expand All @@ -193,7 +197,7 @@ test-nats-reloader-alignment:
test-nats-server-source:
@tests/nats-server-source.sh

test-local: test-observability-profile test-register-cluster test-kube-context test-nvca-default-ownership test-compare-golden test-nats-reloader-alignment test-nats-server-source test-nvca-entrypoints
test-local: test-observability-profile test-register-cluster test-kube-context test-nvca-default-ownership test-collector-images test-compare-golden test-nats-reloader-alignment test-nats-server-source test-nvca-entrypoints
@tests/verify-golden.sh "$(GOLDEN_LOCAL_DIR)" "$(DIST_DIR)/out"

generate-golden: render-local
Expand Down
7 changes: 6 additions & 1 deletion deploy/stacks/nvcf-compute-plane/environments/base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ global:
# =============================================================================
nvcaOperator:
imageTag: "3.10.3"
# BYOO collector used by function and task workloads. The repository
# defaults to global.image; override it when mirroring this image separately.
byooOtelCollector:
# imageRepository: <registry>/<repository>/byoo-otel-collector
imageTag: "0.160.0-nv-0.2.8"
selfManaged:
nvcaVersion: "3.10.3"
imageCredHelper:
Expand All @@ -42,7 +47,7 @@ global:
# Self-hosted registries do not always mirror this optional image.
# Enable it only after publishing the collector in global.image.
enabled: false
imageTag: "0.157.0-nv-0.2.1"
imageTag: "0.160.0-nv-0.2.8"
# ICMS (SIS) service URL: required; set per environment.
icmsServiceURL: ""
icmsServiceHostHeaderOverride: ""
Expand Down
12 changes: 10 additions & 2 deletions deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,11 @@ helmDefaults:
{{- $defaultOTelCollectorImageRepository := printf "%s/%s/nvcf-otel-collector" .Values.global.image.registry .Values.global.image.repository }}
{{- $otelCollectorImageRepository := dig "imageRepository" $defaultOTelCollectorImageRepository $otelCollector }}

{{- $byooOtelCollector := dig "byooOtelCollector" dict $nvcaOp }}
{{- $defaultByooOtelCollectorImageRepository := printf "%s/%s/byoo-otel-collector" .Values.global.image.registry .Values.global.image.repository }}
{{- $byooOtelCollectorImageRepository := dig "imageRepository" "" $byooOtelCollector | default $defaultByooOtelCollectorImageRepository }}
{{- $byooOtelCollectorImageTag := dig "imageTag" "" $byooOtelCollector }}

{{- $featureGateValues := dig "featureGateValues" list $selfManaged }}
{{- $kaiSchedulerEnabled := dig "addons" "kaiScheduler" "enabled" false .Values }}
{{- $dynamoOperatorEnabled := dig "addons" "dynamoOperator" "enabled" false .Values }}
Expand Down Expand Up @@ -295,8 +300,12 @@ releases:
{{- $agent := dig "agent" dict $nvcaOp }}
{{- $agentTolerations := dig "tolerations" list $agent }}
{{- $workloadTolerations := dig "workload" "tolerations" list $agent }}
{{- if or $agentTolerations $workloadTolerations }}
agent:
byooOtelCollector:
imageRepository: {{ $byooOtelCollectorImageRepository | quote }}
{{- with $byooOtelCollectorImageTag }}
imageTag: {{ . | quote }}
{{- end }}
{{- with $agentTolerations }}
tolerations:
{{ toYaml . | nindent 12 }}
Expand All @@ -306,7 +315,6 @@ releases:
tolerations:
{{ toYaml . | nindent 14 }}
{{- end }}
{{- end }}
labels:
release-group: workers
wait: true
Expand Down
2 changes: 2 additions & 0 deletions deploy/stacks/nvcf-compute-plane/release-inventory.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ states:
- addons.topologyAwareScheduling.enabled=true
- plane: compute-plane
path: deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl
fullOverrides:
- global.nvcaOperator.selfManaged.otelCollector.enabled=true
sourceCharts:
helm-nvca-operator:
tagPrefix: deploy/helm/nvca-operator/v
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ spec:
- name: OTEL_COLLECTOR_IMAGE_REPO
value: "nvcr.io/0651155215864979/ncp-dev/nvcf-otel-collector"
- name: OTEL_COLLECTOR_IMAGE_TAG
value: "0.157.0-nv-0.2.1"
value: "0.160.0-nv-0.2.8"
- name: OTEL_COLLECTOR_RESOURCES_B64
value: "eyJsaW1pdHMiOnsiY3B1IjoiMTAwMG0iLCJtZW1vcnkiOiIxR2kifSwicmVxdWVzdHMiOnsiY3B1IjoiMjAwbSIsIm1lbW9yeSI6IjI1Nk1pIn19"
- name: OPERATOR_NAMESPACE
Expand Down Expand Up @@ -127,9 +127,9 @@ spec:
- --nvca-cache-mount-options
- "ro,norecovery,nouuid"
- --function-env-overrides-b64
- "eyJCWU9PX09URUxfQ09MTEVDVE9SX0NPTlRBSU5FUiI6Im52Y3IuaW8vbnZpZGlhL252Y2YtYnlvYy9ieW9vLW90ZWwtY29sbGVjdG9yOjAuMTYwLjAtbnYtMC4yLjUifQ=="
- "eyJCWU9PX09URUxfQ09MTEVDVE9SX0NPTlRBSU5FUiI6Im52Y3IuaW8vMDY1MTE1NTIxNTg2NDk3OS9uY3AtZGV2L2J5b28tb3RlbC1jb2xsZWN0b3I6MC4xNjAuMC1udi0wLjIuOCJ9"
- --task-env-overrides-b64
- "eyJCWU9PX09URUxfQ09MTEVDVE9SX0NPTlRBSU5FUiI6Im52Y3IuaW8vbnZpZGlhL252Y2YtYnlvYy9ieW9vLW90ZWwtY29sbGVjdG9yOjAuMTYwLjAtbnYtMC4yLjUifQ=="
- "eyJCWU9PX09URUxfQ09MTEVDVE9SX0NPTlRBSU5FUiI6Im52Y3IuaW8vMDY1MTE1NTIxNTg2NDk3OS9uY3AtZGV2L2J5b28tb3RlbC1jb2xsZWN0b3I6MC4xNjAuMC1udi0wLjIuOCJ9"
image: nvcr.io/0651155215864979/ncp-dev/nvca-operator:3.10.3
imagePullPolicy: IfNotPresent
securityContext:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ data:
enabled: false
imageConfig:
repository: "nvcr.io/0651155215864979/ncp-dev/nvcf-otel-collector"
tag: "0.157.0-nv-0.2.1"
tag: "0.160.0-nv-0.2.8"
agent:
natsURL: "nats://nats.nats-system.svc.cluster.local:4222"
helmReValStageOAuthTokenURL: ""
Expand Down
91 changes: 91 additions & 0 deletions deploy/stacks/nvcf-compute-plane/tests/collector-images.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

set -euo pipefail

stack_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
repo_root="$(cd "${stack_dir}/../../.." && pwd)"
work_dir="$(mktemp -d)"
trap 'rm -rf "${work_dir}"' EXIT

fail() {
echo "collector-images: $*" >&2
exit 1
}

state="${stack_dir}/helmfile.d/02-nvca.yaml.gotmpl"
base="${stack_dir}/environments/base.yaml"
chart_version="$(sed -n '/chart: nvcf\/helm-nvca-operator/{n;s/^[[:space:]]*version: //p;}' "${state}")"
chart_path="deploy/helm/nvca-operator/nvca-operator"
git -C "${repo_root}" archive "deploy/helm/nvca-operator/v${chart_version}" "${chart_path}" |
tar -x -C "${work_dir}"
chart="${work_dir}/${chart_path}"
cp "${stack_dir}/testdata/registration/ncp-local-register-values.yaml" "${work_dir}/collector-test-register-values.yaml"

byoo_tag="$(yq -er '.global.nvcaOperator.byooOtelCollector.imageTag' "${base}")"
ordinary_tag="$(yq -er '.global.nvcaOperator.selfManaged.otelCollector.imageTag' "${base}")"

render() {
local name="$1"
shift
HELMFILE_ENV=base CLUSTER_NAME=collector-test NCA_ID=collector-test OUTPUT_DIR="${work_dir}" \
helmfile --file "${state}" --environment default \
--state-values-set-string global.image.registry=nvcr.io \
--state-values-set-string global.image.repository=nvidia/nvcf \
"$@" --selector name=nvca-operator write-values \
--output-file-template "${work_dir}/${name}-values.yaml" >/dev/null
helm template nvca-operator "${chart}" --namespace nvca-operator \
--values "${work_dir}/${name}-values.yaml" >"${work_dir}/${name}.yaml"
}

check_images() {
local name="$1" byoo="$2" ordinary="$3" enabled="$4"
local manifest="${work_dir}/${name}.yaml" flag encoded actual
for flag in --function-env-overrides-b64 --task-env-overrides-b64; do
encoded="$(FLAG="${flag}" yq -r 'select(.kind == "Deployment") |
.spec.template.spec.containers[] | select(.name == "nvca-operator") |
.args | .[to_entries | map(select(.value == strenv(FLAG))) | .[0].key + 1]' "${manifest}")"
actual="$(printf '%s' "${encoded}" | base64 --decode | yq -r '.BYOO_OTEL_COLLECTOR_CONTAINER')"
test "${actual}" = "${byoo}" || fail "${name}: ${flag} selected ${actual}, expected ${byoo}"
done
yq -r 'select(.kind == "Deployment") | .spec.template.spec.containers[] |
select(.name == "nvca-operator") | .env | map({"key": .name, "value": .value}) | from_entries' \
"${manifest}" >"${work_dir}/operator-env.yaml"
actual="$(yq -r '.OTEL_COLLECTOR_IMAGE_REPO + ":" + .OTEL_COLLECTOR_IMAGE_TAG' "${work_dir}/operator-env.yaml")"
test "${actual}" = "${ordinary}" || fail "${name}: operator collector selected ${actual}"
test "$(yq -r '.OTEL_COLLECTOR_ENABLED' "${work_dir}/operator-env.yaml")" = "${enabled}" ||
fail "${name}: operator collector enablement changed"
yq -r 'select(.kind == "ConfigMap" and .metadata.name == "nvcfbackend-self-managed") |
.data."cluster-dto.yaml" | from_yaml | .otelCollector' "${manifest}" >"${work_dir}/backend-collector.yaml"
actual="$(yq -r '.imageConfig.repository + ":" + .imageConfig.tag' "${work_dir}/backend-collector.yaml")"
test "${actual}" = "${ordinary}" || fail "${name}: backend collector selected ${actual}"
test "$(yq -r '.enabled' "${work_dir}/backend-collector.yaml")" = "${enabled}" ||
fail "${name}: backend collector enablement changed"
test "$(yq -r 'select(.kind == "ConfigMap" and .metadata.name == "nvcfbackend-helm-managed") | .data | length' "${manifest}")" = 0 ||
fail "${name}: self-managed stack unexpectedly instantiated the helm-managed backend"
}

render default
check_images default "nvcr.io/nvidia/nvcf/byoo-otel-collector:${byoo_tag}" \
"nvcr.io/nvidia/nvcf/nvcf-otel-collector:${ordinary_tag}" false

inventory_override="$(yq -er '.states[] | select(.path == "deploy/stacks/nvcf-compute-plane/helmfile.d/02-nvca.yaml.gotmpl") | .fullOverrides[] | select(. == "global.nvcaOperator.selfManaged.otelCollector.enabled=true")' "${stack_dir}/release-inventory.yaml")"
render inventory --state-values-set "${inventory_override}"
check_images inventory "nvcr.io/nvidia/nvcf/byoo-otel-collector:${byoo_tag}" \
"nvcr.io/nvidia/nvcf/nvcf-otel-collector:${ordinary_tag}" true

render mirror --state-values-set-string global.image.registry=mirror.example.invalid \
--state-values-set-string global.image.repository=team/nvcf
check_images mirror "mirror.example.invalid/team/nvcf/byoo-otel-collector:${byoo_tag}" \
"mirror.example.invalid/team/nvcf/nvcf-otel-collector:${ordinary_tag}" false

render override \
--state-values-set-string global.nvcaOperator.byooOtelCollector.imageRepository=mirror.example.invalid/custom/byoo \
--state-values-set-string global.nvcaOperator.byooOtelCollector.imageTag=byoo-fixture \
--state-values-set-string global.nvcaOperator.selfManaged.otelCollector.imageRepository=mirror.example.invalid/custom/ordinary \
--state-values-set-string global.nvcaOperator.selfManaged.otelCollector.imageTag=ordinary-fixture
check_images override mirror.example.invalid/custom/byoo:byoo-fixture \
mirror.example.invalid/custom/ordinary:ordinary-fixture false

echo "collector-images: all checks passed with released chart ${chart_version}"
77 changes: 77 additions & 0 deletions tools/docs-version-sync/resolved_inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package main

import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -336,6 +337,11 @@ func collectResolvedImages(node *yaml.Node, images map[string]parsedImageReferen
return err
}
}
if key.Value == "env" {
if err := collectResolvedCollectorEnvironment(value, images); err != nil {
return err
}
}
if err := collectResolvedImages(value, images); err != nil {
return err
}
Expand All @@ -351,6 +357,33 @@ func collectResolvedImages(node *yaml.Node, images map[string]parsedImageReferen
}

func collectResolvedImageArguments(node *yaml.Node, images map[string]parsedImageReference) error {
if node.Kind == yaml.SequenceNode {
for i, argument := range node.Content {
if argument.Value != "--function-env-overrides-b64" && argument.Value != "--task-env-overrides-b64" {
continue
}
if i+1 == len(node.Content) {
return fmt.Errorf("%s has no encoded environment", argument.Value)
}
decoded, err := base64.StdEncoding.DecodeString(node.Content[i+1].Value)
if err != nil {
return fmt.Errorf("decode %s: %w", argument.Value, err)
}
var overrides map[string]string
if err := json.Unmarshal(decoded, &overrides); err != nil {
return fmt.Errorf("parse %s environment: %w", argument.Value, err)
}
if reference := overrides["BYOO_OTEL_COLLECTOR_CONTAINER"]; reference != "" {
image, err := parseResolvedImageReference(reference)
if err != nil {
return fmt.Errorf("BYOO_OTEL_COLLECTOR_CONTAINER: %w", err)
}
if existing, exists := images[image.Reference]; !exists || existing.Indirect {
images[image.Reference] = image
}
}
}
}
if node.Kind == yaml.ScalarNode {
if reference, ok := resolvedImageArgument(node.Value); ok {
image, err := parseResolvedImageReference(reference)
Expand All @@ -373,6 +406,50 @@ func collectResolvedImageArguments(node *yaml.Node, images map[string]parsedImag
return nil
}

func collectResolvedCollectorEnvironment(node *yaml.Node, images map[string]parsedImageReference) error {
if node.Kind != yaml.SequenceNode {
return nil
}
var repository, tag, enabled string
for _, entry := range node.Content {
var variable struct {
Name string `yaml:"name"`
Value string `yaml:"value"`
}
if err := entry.Decode(&variable); err != nil {
return fmt.Errorf("decode collector environment: %w", err)
}
switch variable.Name {
case "OTEL_COLLECTOR_IMAGE_REPO":
repository = variable.Value
case "OTEL_COLLECTOR_IMAGE_TAG":
tag = variable.Value
case "OTEL_COLLECTOR_ENABLED":
enabled = variable.Value
}
}
if repository == "" && tag == "" {
return nil
}
if repository == "" || tag == "" {
// Disabled collectors may omit image defaults that are only needed at runtime.
if enabled == "false" {
return nil
}
return fmt.Errorf("OTEL_COLLECTOR_IMAGE_REPO and OTEL_COLLECTOR_IMAGE_TAG must both be resolved")
}
image, err := parseResolvedImageReference(repository + ":" + tag)
if err != nil {
return fmt.Errorf("OTEL_COLLECTOR_IMAGE_REPO/TAG: %w", err)
}
// NVCA creates this optional collector later; it is not an operator container.
image.Indirect = true
if existing, exists := images[image.Reference]; !exists || existing.Indirect {
images[image.Reference] = image
}
return nil
}

func resolvedImageArgument(value string) (string, bool) {
matches := imageArgumentRe.FindStringSubmatch(value)
if matches == nil {
Expand Down
Loading
Loading