diff --git a/deploy/helm/nvca-operator/Makefile b/deploy/helm/nvca-operator/Makefile index aaf56522d..ccef5f14a 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-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-image-pull-secret-defaults +.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-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-image-pull-secret-defaults test-first-class-byoo-values install: ifndef values @@ -116,6 +116,9 @@ test-self-managed-nvca-image-reference: test-image-pull-secret-defaults: @bash ./tests/image_pull_secret_defaults_test.sh +test-first-class-byoo-values: + @bash ./tests/first_class_byoo_values_test.sh + uninstall: @echo "Deleting $(release) from namespace $(namespace)..." helm uninstall $(release) --namespace $(namespace) diff --git a/deploy/helm/nvca-operator/nvca-operator/README.md b/deploy/helm/nvca-operator/nvca-operator/README.md index 9389a3aec..c0ea68729 100644 --- a/deploy/helm/nvca-operator/nvca-operator/README.md +++ b/deploy/helm/nvca-operator/nvca-operator/README.md @@ -43,7 +43,12 @@ used in Kubernetes Clusters to run NVCF Workloads. | `nvcaHelmRepositoryPrefix` | Enables Helm repository restrictions to specific org/teams | `""` | | `enableGXCache` | Enables GXCache Support in NVCA | `true` | | `ddcsIPAllowList` | provides comma separated CIDR ranges to allowList | `""` | -| `agentConfig.mergeConfig` | Merge fields into the generated NVCA config. Must be a string. | `""` | +| `byoo.resources` | Resource requests and limits for the BYOO OTel collector container. | See `values.yaml` | +| `byoo.logChunking.maxPayloadBytes` | Maximum BYOO log chunk payload size in bytes. | `262144` | +| `byoo.logChunking.dryRun` | Record chunking metrics without modifying log payloads. | `false` | +| `byoo.otelCollector` | Structured BYOO OTel collector rendering overrides. | See `values.yaml` | +| `byoo.additionalResourceOverhead` | Cluster capacity reserved for BYOO and related DaemonSet overhead. | See `values.yaml` | +| `agentConfig.mergeConfig` | Merge fields into the generated NVCA config. Deprecated for BYOO settings; use `byoo` 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. | `""` | @@ -80,7 +85,7 @@ used in Kubernetes Clusters to run NVCF Workloads. | `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` | Default LLM request-router worker address rendered as STARGATE_ADDRESS for LLM workers | `""` | +| `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 diff --git a/deploy/helm/nvca-operator/nvca-operator/templates/_helpers.tpl b/deploy/helm/nvca-operator/nvca-operator/templates/_helpers.tpl index 6b5b4f7ed..d2daf412e 100644 --- a/deploy/helm/nvca-operator/nvca-operator/templates/_helpers.tpl +++ b/deploy/helm/nvca-operator/nvca-operator/templates/_helpers.tpl @@ -120,6 +120,58 @@ Reject transport trust settings that explicitly disable QUIC verification. {{- end -}} {{- end -}} +{{/* +Render the effective chart-owned agent configuration. Top-level byoo 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 -}} +{{- $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 -}} +{{- $config := dict -}} +{{- if $agent }} +{{- $_ := set $config "agent" $agent -}} +{{- end -}} +{{- $agentConfig := .Values.agentConfig | default dict -}} +{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} +{{- if $mergeConfigData }} +{{- $config = mergeOverwrite $config ($mergeConfigData | fromYaml | default dict) -}} +{{- end -}} +{{- $config | toYaml -}} +{{- end -}} + +{{/* +Return true when legacy agentConfig.mergeConfig configures BYOO fields. The +ConfigMap annotation lets the operator emit a source-aware migration warning. +*/}} +{{- define "nvcaop.usesLegacyBYOOConfig" -}} +{{- $agentConfig := .Values.agentConfig | default dict -}} +{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} +{{- $legacyBYOO := false -}} +{{- if $mergeConfigData }} +{{- $config := $mergeConfigData | fromYaml | default dict -}} +{{- $agent := $config.agent | default dict -}} +{{- range $key, $_ := $agent }} +{{- if or (hasPrefix "byoo" (lower $key)) (eq $key "additionalResourceOverhead") }} +{{- $legacyBYOO = true -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- $legacyBYOO -}} +{{- end -}} + {{/* ImagePullSecret for images. */}} diff --git a/deploy/helm/nvca-operator/nvca-operator/templates/agent-config-merge-cm.yaml b/deploy/helm/nvca-operator/nvca-operator/templates/agent-config-merge-cm.yaml index 9173150fc..b2f19e2ca 100644 --- a/deploy/helm/nvca-operator/nvca-operator/templates/agent-config-merge-cm.yaml +++ b/deploy/helm/nvca-operator/nvca-operator/templates/agent-config-merge-cm.yaml @@ -1,4 +1,5 @@ -{{- if (.Values.agentConfig).mergeConfig }} +{{- $effectiveConfig := include "nvcaop.effectiveAgentConfig" . | fromYaml }} +{{- if $effectiveConfig }} # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -21,6 +22,11 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "nvcaop.labels" . | nindent 4 }} + {{- if eq (include "nvcaop.usesLegacyBYOOConfig" .) "true" }} + annotations: + nvcf.nvidia.com/legacy-byoo-config: "true" + {{- end }} data: - config.yaml: {{- .Values.agentConfig.mergeConfig | toYaml | indent 2 }} + config.yaml: | +{{ include "nvcaop.effectiveAgentConfig" . | nindent 4 }} {{- end }} diff --git a/deploy/helm/nvca-operator/nvca-operator/templates/rbac_allowed_extra_types.yaml b/deploy/helm/nvca-operator/nvca-operator/templates/rbac_allowed_extra_types.yaml index 4a62405d1..1e170c279 100644 --- a/deploy/helm/nvca-operator/nvca-operator/templates/rbac_allowed_extra_types.yaml +++ b/deploy/helm/nvca-operator/nvca-operator/templates/rbac_allowed_extra_types.yaml @@ -1,4 +1,5 @@ -{{- if (.Values.agentConfig).mergeConfig }} +{{- $effectiveConfig := include "nvcaop.effectiveAgentConfig" . | fromYaml }} +{{- if $effectiveConfig }} # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -14,7 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -{{- $mc := .Values.agentConfig.mergeConfig | fromYaml }} +{{- $mc := $effectiveConfig }} {{- if and $mc.cluster ($mc.cluster.validationPolicy).allowedExtraKubernetesTypes }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole diff --git a/deploy/helm/nvca-operator/nvca-operator/values.schema.json b/deploy/helm/nvca-operator/nvca-operator/values.schema.json index 05c451fe4..2e4db8a44 100644 --- a/deploy/helm/nvca-operator/nvca-operator/values.schema.json +++ b/deploy/helm/nvca-operator/nvca-operator/values.schema.json @@ -201,11 +201,59 @@ "properties": { "mergeConfig": { "type": "string", - "description": "Merge fields into the generated NVCA config. Must be a 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" + } + } + } + }, "operatorConfig": { "type": "object", "properties": { diff --git a/deploy/helm/nvca-operator/nvca-operator/values.yaml b/deploy/helm/nvca-operator/nvca-operator/values.yaml index bb6609312..0c95dda76 100644 --- a/deploy/helm/nvca-operator/nvca-operator/values.yaml +++ b/deploy/helm/nvca-operator/nvca-operator/values.yaml @@ -96,7 +96,35 @@ nvcaHelmRepositoryPrefix: "" 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 +## @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. +byoo: + resources: + limits: + cpu: 1000m + memory: 4Gi + requests: + cpu: 1000m + memory: 4Gi + logChunking: + maxPayloadBytes: 262144 + dryRun: false + otelCollector: + exporterHelper: + sendingQueue: + batch: + sizer: bytes + maxSize: 1000000 + additionalResourceOverhead: + cpu: "8" + ephemeral-storage: 5Gi + memory: 10Gi ## @param agentConfig.mergeConfig Merge fields into the generated NVCA config. Must be a string. +## @description agentConfig.mergeConfig is deprecated for BYOO settings. Use the top-level byoo values instead. agentConfig: mergeConfig: |- cluster: @@ -108,7 +136,7 @@ agentConfig: # agent: # logLevel: debug # - # BYOO metric subset example: + # Legacy BYOO example (deprecated): # # mergeConfig: | # agent: @@ -178,7 +206,7 @@ resources: ## @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 Default LLM request-router worker address rendered as STARGATE_ADDRESS for LLM workers +## @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 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 new file mode 100644 index 000000000..b2cf61773 --- /dev/null +++ b/deploy/helm/nvca-operator/tests/first_class_byoo_values_test.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +repo_root="$(cd "$(dirname "$0")/.." && pwd)" +tmp_dir="$(mktemp -d)" + +cleanup() { + rm -rf "${tmp_dir}" +} +trap cleanup EXIT + +default_manifest="${tmp_dir}/default-manifest.yaml" +legacy_manifest="${tmp_dir}/legacy-manifest.yaml" +legacy_values="${tmp_dir}/legacy-values.yaml" + +render() { + local manifest="$1" + shift + + helm template nvca-operator "${repo_root}/nvca-operator" \ + --namespace nvca-operator \ + --values "${repo_root}/nvca-operator/values.yaml" \ + --values "${repo_root}/values.release-sbom.yaml" \ + --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}" +} + +agent_config_value() { + local manifest="$1" + local expression="$2" + + yq -er "select(.kind == \"ConfigMap\" and .metadata.name == \"agent-config-merge\") | .data.\"config.yaml\" | from_yaml | ${expression}" "${manifest}" +} + +assert_equal() { + local expected="$1" + local actual="$2" + local message="$3" + + if [[ "${actual}" != "${expected}" ]]; then + echo "${message}: expected ${expected}, got ${actual}" >&2 + exit 1 + fi +} + +render "${default_manifest}" +assert_equal "1000m" "$(agent_config_value "${default_manifest}" '.agent.BYOOResources.limits.cpu')" "unexpected BYOO CPU limit" +assert_equal "4Gi" "$(agent_config_value "${default_manifest}" '.agent.BYOOResources.requests.memory')" "unexpected BYOO memory request" +assert_equal "262144" "$(agent_config_value "${default_manifest}" '.agent.byooLogChunking.maxPayloadBytes')" "unexpected BYOO log chunk payload size" +assert_equal "1000000" "$(agent_config_value "${default_manifest}" '.agent.byooOtelCollector.exporterHelper.sendingQueue.batch.maxSize')" "unexpected BYOO exporter batch limit" +assert_equal "8" "$(agent_config_value "${default_manifest}" '.agent.additionalResourceOverhead.cpu')" "unexpected BYOO capacity reservation" + +yq eval ' + .agentConfig.mergeConfig = "agent:\n BYOOResources:\n limits:\n cpu: 1500m\n byooLogChunking:\n maxPayloadBytes: 131072\ncluster:\n validationPolicy:\n name: Unrestricted\n allowedExtraKubernetesTypes:\n - group: nvidia.com\n kind: DynamoGraphDeployment\n resource: dynamographdeployments\n version: v1alpha1" | + .agentConfig.mergeConfig style="literal" +' "${repo_root}/nvca-operator/values.yaml" > "${legacy_values}" + +helm template nvca-operator "${repo_root}/nvca-operator" \ + --namespace nvca-operator \ + --values "${legacy_values}" \ + --values "${repo_root}/values.release-sbom.yaml" \ + --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 \ + > "${legacy_manifest}" + +assert_equal "1500m" "$(agent_config_value "${legacy_manifest}" '.agent.BYOOResources.limits.cpu')" "legacy BYOO resources did not override the chart default" +assert_equal "131072" "$(agent_config_value "${legacy_manifest}" '.agent.byooLogChunking.maxPayloadBytes')" "legacy BYOO chunking did not override the chart default" +assert_equal "dynamographdeployments" "$(agent_config_value "${legacy_manifest}" '.cluster.validationPolicy.allowedExtraKubernetesTypes[0].resource')" "legacy validation policy was dropped" +assert_equal "true" "$(yq -er 'select(.kind == "ConfigMap" and .metadata.name == "agent-config-merge") | .metadata.annotations."nvcf.nvidia.com/legacy-byoo-config"' "${legacy_manifest}")" "legacy BYOO config was not annotated" diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/README.md b/src/compute-plane-services/nvca/deployments/nvca-operator/README.md index 20acd0894..0e9c2c8f2 100644 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/README.md +++ b/src/compute-plane-services/nvca/deployments/nvca-operator/README.md @@ -43,7 +43,12 @@ used in Kubernetes Clusters to run NVCF Workloads. | `nvcaHelmRepositoryPrefix` | Enables Helm repository restrictions to specific org/teams | `""` | | `enableGXCache` | Enables GXCache Support in NVCA | `true` | | `ddcsIPAllowList` | provides comma separated CIDR ranges to allowList | `""` | -| `agentConfig.mergeConfig` | Merge fields into the generated NVCA config. Must be a string. | `""` | +| `byoo.resources` | Resource requests and limits for the BYOO OTel collector container. | See `values.yaml` | +| `byoo.logChunking.maxPayloadBytes` | Maximum BYOO log chunk payload size in bytes. | `262144` | +| `byoo.logChunking.dryRun` | Record chunking metrics without modifying log payloads. | `false` | +| `byoo.otelCollector` | Structured BYOO OTel collector rendering overrides. | See `values.yaml` | +| `byoo.additionalResourceOverhead` | Cluster capacity reserved for BYOO and related DaemonSet overhead. | See `values.yaml` | +| `agentConfig.mergeConfig` | Merge fields into the generated NVCA config. Deprecated for BYOO settings; use `byoo` 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. | `""` | 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 index 6b5b4f7ed..d2daf412e 100644 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/templates/_helpers.tpl +++ b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/_helpers.tpl @@ -120,6 +120,58 @@ Reject transport trust settings that explicitly disable QUIC verification. {{- end -}} {{- end -}} +{{/* +Render the effective chart-owned agent configuration. Top-level byoo 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 -}} +{{- $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 -}} +{{- $config := dict -}} +{{- if $agent }} +{{- $_ := set $config "agent" $agent -}} +{{- end -}} +{{- $agentConfig := .Values.agentConfig | default dict -}} +{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} +{{- if $mergeConfigData }} +{{- $config = mergeOverwrite $config ($mergeConfigData | fromYaml | default dict) -}} +{{- end -}} +{{- $config | toYaml -}} +{{- end -}} + +{{/* +Return true when legacy agentConfig.mergeConfig configures BYOO fields. The +ConfigMap annotation lets the operator emit a source-aware migration warning. +*/}} +{{- define "nvcaop.usesLegacyBYOOConfig" -}} +{{- $agentConfig := .Values.agentConfig | default dict -}} +{{- $mergeConfigData := $agentConfig.mergeConfig | default "" -}} +{{- $legacyBYOO := false -}} +{{- if $mergeConfigData }} +{{- $config := $mergeConfigData | fromYaml | default dict -}} +{{- $agent := $config.agent | default dict -}} +{{- range $key, $_ := $agent }} +{{- if or (hasPrefix "byoo" (lower $key)) (eq $key "additionalResourceOverhead") }} +{{- $legacyBYOO = true -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- $legacyBYOO -}} +{{- end -}} + {{/* ImagePullSecret for images. */}} 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 index 9173150fc..b2f19e2ca 100644 --- 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 @@ -1,4 +1,5 @@ -{{- if (.Values.agentConfig).mergeConfig }} +{{- $effectiveConfig := include "nvcaop.effectiveAgentConfig" . | fromYaml }} +{{- if $effectiveConfig }} # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -21,6 +22,11 @@ metadata: namespace: {{ .Release.Namespace }} labels: {{- include "nvcaop.labels" . | nindent 4 }} + {{- if eq (include "nvcaop.usesLegacyBYOOConfig" .) "true" }} + annotations: + nvcf.nvidia.com/legacy-byoo-config: "true" + {{- end }} data: - config.yaml: {{- .Values.agentConfig.mergeConfig | toYaml | indent 2 }} + config.yaml: | +{{ include "nvcaop.effectiveAgentConfig" . | nindent 4 }} {{- 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 index bdb63091c..00e159c94 100644 --- 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 @@ -5,7 +5,7 @@ # 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 +# 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, 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 new file mode 100644 index 000000000..6662875aa --- /dev/null +++ b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/poddisruptionbudget.yaml @@ -0,0 +1,39 @@ +# 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 .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/rbac_allowed_extra_types.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/templates/rbac_allowed_extra_types.yaml index 4a62405d1..1e170c279 100644 --- 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 @@ -1,4 +1,5 @@ -{{- if (.Values.agentConfig).mergeConfig }} +{{- $effectiveConfig := include "nvcaop.effectiveAgentConfig" . | fromYaml }} +{{- if $effectiveConfig }} # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # @@ -14,7 +15,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -{{- $mc := .Values.agentConfig.mergeConfig | fromYaml }} +{{- $mc := $effectiveConfig }} {{- if and $mc.cluster ($mc.cluster.validationPolicy).allowedExtraKubernetesTypes }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole 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 index 4ac4f2910..cb8e024fd 100644 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/values.schema.json +++ b/src/compute-plane-services/nvca/deployments/nvca-operator/values.schema.json @@ -201,11 +201,59 @@ "properties": { "mergeConfig": { "type": "string", - "description": "Merge fields into the generated NVCA config. Must be a 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" + } + } + } + }, "operatorConfig": { "type": "object", "properties": { diff --git a/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml b/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml index 89da88093..87e8e8d45 100644 --- a/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml +++ b/src/compute-plane-services/nvca/deployments/nvca-operator/values.yaml @@ -70,6 +70,13 @@ serviceAccount: ## @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 (e.g. 1 or "50%"). + minAvailable: "" + maxUnavailable: "" ## @param systemNamespace Namespace in which NVCFBackend objects are created. systemNamespace: nvca-operator @@ -107,7 +114,36 @@ 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 +## @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. +byoo: + resources: + limits: + cpu: 1000m + memory: 4Gi + requests: + cpu: 1000m + memory: 4Gi + logChunking: + maxPayloadBytes: 262144 + dryRun: false + otelCollector: + exporterHelper: + sendingQueue: + batch: + sizer: bytes + maxSize: 1000000 + additionalResourceOverhead: + cpu: "8" + ephemeral-storage: 5Gi + memory: 10Gi + ## @param agentConfig.mergeConfig Merge fields into the generated NVCA config. Must be a string. +## @description agentConfig.mergeConfig is deprecated for BYOO settings. Use the top-level byoo values instead. agentConfig: mergeConfig: "" # Example: @@ -116,7 +152,7 @@ agentConfig: # agent: # logLevel: debug # - # BYOO metric subset example: + # Legacy BYOO example (deprecated): # # mergeConfig: | # agent: diff --git a/src/compute-plane-services/nvca/pkg/operator/reconcile/backendk8scache.go b/src/compute-plane-services/nvca/pkg/operator/reconcile/backendk8scache.go index 6c0fce5d7..d790e5796 100644 --- a/src/compute-plane-services/nvca/pkg/operator/reconcile/backendk8scache.go +++ b/src/compute-plane-services/nvca/pkg/operator/reconcile/backendk8scache.go @@ -25,6 +25,7 @@ import ( "net/http" "reflect" "sort" + "sync" "sync/atomic" "time" @@ -176,6 +177,10 @@ type BackendK8sCache struct { // When true, the reconciliation loop will skip cleanup of NVCFBackend resources // and let the shutdown handler manage the cleanup instead. gracefulShutdown atomic.Bool + + legacyBYOOConfigWarningMu sync.Mutex + legacyBYOOConfigWarningResourceVersion string + legacyBYOOConfigWarningSeen bool } // BackendK8sCacheBuilder builds Backendk8sCache and start related K8s diff --git a/src/compute-plane-services/nvca/pkg/operator/reconcile/byoo_deprecation_test.go b/src/compute-plane-services/nvca/pkg/operator/reconcile/byoo_deprecation_test.go new file mode 100644 index 000000000..2360655ba --- /dev/null +++ b/src/compute-plane-services/nvca/pkg/operator/reconcile/byoo_deprecation_test.go @@ -0,0 +1,47 @@ +/* +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. +*/ + +package operator + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestShouldWarnForLegacyBYOOConfig(t *testing.T) { + cache := &BackendK8sCache{} + configMap := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{ + Namespace: "nvca-operator", + Name: agentConfigMergeConfigMapName, + ResourceVersion: "1", + Annotations: map[string]string{ + legacyBYOOConfigAnnotation: "true", + }, + }} + + assert.True(t, cache.shouldWarnForLegacyBYOOConfig(configMap)) + assert.False(t, cache.shouldWarnForLegacyBYOOConfig(configMap)) + + configMap.ResourceVersion = "2" + assert.True(t, cache.shouldWarnForLegacyBYOOConfig(configMap)) + + configMap.Annotations = nil + assert.False(t, cache.shouldWarnForLegacyBYOOConfig(configMap)) +} diff --git a/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_reconcile.go b/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_reconcile.go index a99f3437c..81159310c 100644 --- a/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_reconcile.go +++ b/src/compute-plane-services/nvca/pkg/operator/reconcile/nvcaagent_reconcile.go @@ -127,6 +127,7 @@ const ( agentConfigMergeConfigMapName = "agent-config-merge" nvcaOperatorConfigMapName = "nvca-operator-config" agentConfigVolumeName = "agent-config" + legacyBYOOConfigAnnotation = "nvcf.nvidia.com/legacy-byoo-config" // ReVal config. ReValCacheVolumeName = "reval-rendered-helmcharts" @@ -1477,9 +1478,31 @@ func (bc *BackendK8sCache) getRawAgentConfigToMerge(ctx context.Context) (nvcaco return nvcaconfig.Config{}, false, nvcaoperatorerrors.FatalError(fmt.Errorf("invalid %s: %w", agentConfigMergeConfigMapName, err)) } + if bc.shouldWarnForLegacyBYOOConfig(cm) { + log.Warnf( + "ConfigMap %s/%s contains deprecated agentConfig.mergeConfig BYOO settings; migrate to top-level chart byoo values before the next minor release", + cm.Namespace, + cm.Name, + ) + } return cfg, true, nil } +func (bc *BackendK8sCache) shouldWarnForLegacyBYOOConfig(cm *corev1.ConfigMap) bool { + if cm.Annotations[legacyBYOOConfigAnnotation] != "true" { + return false + } + + bc.legacyBYOOConfigWarningMu.Lock() + defer bc.legacyBYOOConfigWarningMu.Unlock() + if bc.legacyBYOOConfigWarningSeen && bc.legacyBYOOConfigWarningResourceVersion == cm.ResourceVersion { + return false + } + bc.legacyBYOOConfigWarningResourceVersion = cm.ResourceVersion + bc.legacyBYOOConfigWarningSeen = true + return true +} + func (bc *BackendK8sCache) getImageRegistryServerFromRepo(nb *nvidiaiov1.NVCFBackend) string { repo := nb.Spec.NVCAImageConfig.Repository if repo == "" {