diff --git a/deploy/helm/vanity-gateway/.gitignore b/deploy/helm/vanity-gateway/.gitignore new file mode 100644 index 000000000..2c20004ed --- /dev/null +++ b/deploy/helm/vanity-gateway/.gitignore @@ -0,0 +1,2 @@ +bin/ +packaged-charts/ diff --git a/deploy/helm/vanity-gateway/AGENTS.md b/deploy/helm/vanity-gateway/AGENTS.md new file mode 100644 index 000000000..93f8050b3 --- /dev/null +++ b/deploy/helm/vanity-gateway/AGENTS.md @@ -0,0 +1,49 @@ +# AGENTS.md - vanity-gateway helm chart + +Native Helm chart subtree. Shared chart rules live in `deploy/helm/AGENTS.md`. + +## Chart Facts + +- Subproject id: `vanity-gateway-helm` +- Chart name: `helm-nvcf-vanity-gateway` +- Chart directory: `helm-nvcf-vanity-gateway` +- CI values: `tools/ci/helm-validate-values/vanity-gateway.yaml` +- Release service name: `helm-nvcf-vanity-gateway` +- Initial release version: not yet assigned. See "Versioning" below. + +## Provenance + +This chart was recovered from the published OCI artifact +`helm-nvcf-vanity-gateway:0.1.0-nvcf-10204.1`. No source tree for it existed in +this repo or in any known upstream project, so the imported files are the +unpacked contents of that artifact plus the sibling scaffolding in this +directory. `.helmignore` is not carried in a packaged chart and was added here +to match the other chart subtrees. + +## Versioning + +The imported `Chart.yaml` still carries the published version +`0.1.0-nvcf-10204.1` and appVersion `1.25.0-nvcf-10204.0`. Neither is a form the +repo release tooling accepts, so no release lane is registered for this chart +and no tag exists. Renumber `version` to a plain `X.Y.Z` before wiring a +release lane. + +## Validate + +```bash +helm lint helm-nvcf-vanity-gateway -f ../../../tools/ci/helm-validate-values/vanity-gateway.yaml +helm template vanity-gateway helm-nvcf-vanity-gateway -f ../../../tools/ci/helm-validate-values/vanity-gateway.yaml +``` + +The chart renders with defaults alone, but `vanityGateway.image.registry` is +empty by default and yields an unqualified image reference, so a values +override is used for validation. + +`values.schema.json` sets `additionalProperties: false` on `vanityGateway` and +on most of its sub-objects. Adding a value key requires a matching schema +change or the render fails. + +This chart pairs with the service image source at +`src/invocation-plane-services/vanity-gateway`, whose service name is +`nvcf-ai-api-gateway-service`. Route configuration for the gateway in front of +it lives in `deploy/helm/gateway-routes`. diff --git a/deploy/helm/vanity-gateway/CLAUDE.md b/deploy/helm/vanity-gateway/CLAUDE.md new file mode 100644 index 000000000..43c994c2d --- /dev/null +++ b/deploy/helm/vanity-gateway/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/deploy/helm/vanity-gateway/Makefile b/deploy/helm/vanity-gateway/Makefile new file mode 100644 index 000000000..4305f3f78 --- /dev/null +++ b/deploy/helm/vanity-gateway/Makefile @@ -0,0 +1,109 @@ +# 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. + +# Variables +release ?= vanity-gateway +namespace ?= nvcf +helm_dir ?= ./helm-nvcf-vanity-gateway +values := $(helm_dir)/values.yaml + +# OPTIONAL for deploy target: Path to an additional Helm values file. +# Example: make deploy values=my-values.yaml additional_values=override.yaml +additional_values ?= + +# Values used only for local lint/template/validate rendering. The default +# values.yaml leaves vanityGateway.image.registry empty, so rendering with +# defaults alone produces an unqualified image reference. Point at the CI +# validation values by default. Matches the documented helm commands in +# AGENTS.md. Deployment targets keep using $(values). +validation_values ?= ../../../tools/ci/helm-validate-values/vanity-gateway.yaml + +# OCI Registry details +OCI_REGISTRY_HOST ?= nvcr.io +OCI_REGISTRY_NAMESPACE ?= 0651155215864979/ncp-dev + +# Automatically determine chart name and version from Chart.yaml +# IMPORTANT: For this setup, CHART_NAME is expected to include any desired OCI prefix (e.g., "helm-yourchart") +# as defined in helm/Chart.yaml's 'name' field. +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 + +install: +ifndef values + $(error "values" variable is not set. Please specify with 'make deploy values=') +endif + @echo "Deploying $(release) to namespace $(namespace) using values file '$(values)'..." + @echo "Additional values file: '$(if $(additional_values),$(additional_values),N/A)'" + helm install $(release) $(helm_dir) \ + --namespace $(namespace) \ + --values $(values) \ + $(if $(additional_values),--values $(additional_values),) \ + --atomic \ + --create-namespace \ + --wait \ + --wait-for-jobs \ + --timeout 20m + +uninstall: + @echo "Deleting $(release) from namespace $(namespace)..." + helm uninstall $(release) --namespace $(namespace) + +status: + @echo "Checking status of $(release) in namespace $(namespace)..." + helm status $(release) --namespace $(namespace) + +lint: + @echo "Linting chart $(helm_dir)..." + helm lint $(helm_dir) \ + $(if $(validation_values),--values $(validation_values),) \ + $(if $(additional_values),--values $(additional_values),) + +template: + @echo "Templating chart $(helm_dir)..." + @mkdir -p bin + helm template $(release) $(helm_dir) \ + --namespace $(namespace) \ + $(if $(validation_values),--values $(validation_values),) \ + $(if $(additional_values),--values $(additional_values),) \ + > bin/manifest.yaml + @echo "Rendered manifest to bin/manifest.yaml" + +validate: template + @echo "Validating manifest with kubeconform..." + @kubeconform -strict -summary -output pretty -kubernetes-version 1.31.5 bin/manifest.yaml + +# Publish Chart +# NOTE: this is manual until the CI pipeline is updated to push the chart to the NVCR OCI registry +clean: + rm -rf ./packaged-charts + +package: clean lint + @echo "[package] Packaging Helm chart $(CHART_NAME) version $(CHART_VERSION)..." + @mkdir -p ./packaged-charts + @helm package $(helm_dir) -d ./packaged-charts/ + @echo "[package] Packaged chart to ./packaged-charts/$(CHART_NAME)-$(CHART_VERSION).tgz" + +push-oci: + @echo "[push-oci] Pushing chart $(CHART_NAME) version $(CHART_VERSION) to oci://$(OCI_REGISTRY_HOST)/$(OCI_REGISTRY_NAMESPACE)/$(CHART_NAME):$(CHART_VERSION)" + @echo "[push-oci] Note: You must be logged into oci://$(OCI_REGISTRY_HOST) for the push to succeed." + @if [ ! -f ./packaged-charts/$(CHART_NAME)-$(CHART_VERSION).tgz ]; then \ + echo "[push-oci] Error: Packaged chart ./packaged-charts/$(CHART_NAME)-$(CHART_VERSION).tgz not found. Run 'make package' first."; \ + exit 1; \ + fi + @helm push ./packaged-charts/$(CHART_NAME)-$(CHART_VERSION).tgz oci://$(OCI_REGISTRY_HOST)/$(OCI_REGISTRY_NAMESPACE) + @echo "[push-oci] Successfully pushed chart to OCI registry." + @echo "[push-oci] Preserved packaged chart at ./packaged-charts/$(CHART_NAME)-$(CHART_VERSION).tgz (run 'make clean' to remove)." diff --git a/deploy/helm/vanity-gateway/README.md b/deploy/helm/vanity-gateway/README.md new file mode 100644 index 000000000..1cd85bf50 --- /dev/null +++ b/deploy/helm/vanity-gateway/README.md @@ -0,0 +1,126 @@ +# NVCF Vanity Gateway Helm Chart + +This directory contains the Helm chart for deploying the NVCF Vanity Gateway on +Kubernetes. The Vanity Gateway maps OpenAI-compatible and vanity URL routes onto +NVCF function invocations. + +## Overview + +The chart renders a Deployment, a Service, a ServiceAccount, two ConfigMaps (one +for environment configuration, one for the route mapping file), and an optional +ServiceMonitor. + +The route mapping is supplied through `vanityGateway.mappingConfig`, which is +serialized into `config.yaml` and mounted at +`/etc/vanity-gateway/config/config.yaml`. The defaults ship an empty mapping, so +routes must be provided through an override values file. + +The default values leave `vanityGateway.image.registry` empty. Set the registry, +and the tag if you do not want the chart `appVersion`, in your override file. + +```yaml +vanityGateway: + image: + registry: + repository: nvcf-ai-api-gateway-service + tag: +``` + +## Prerequisites + +- Kubernetes cluster +- Helm 3.x +- `kubectl` +- A reachable NVCF invocation endpoint, set through + `vanityGateway.config.nvcfApiEndpoint` +- The Prometheus Operator CRDs, only if `vanityGateway.serviceMonitor.enabled` + is `true` + +## Getting Started + +Install the chart with the default values plus your own overrides: + +```bash +helm install vanity-gateway helm-nvcf-vanity-gateway \ + --namespace nvcf \ + --create-namespace \ + --values helm-nvcf-vanity-gateway/values.yaml \ + --values path/to/values.yaml \ + --wait \ + --timeout 10m +``` + +Upgrade an existing release: + +```bash +helm upgrade vanity-gateway helm-nvcf-vanity-gateway \ + --namespace nvcf \ + --values helm-nvcf-vanity-gateway/values.yaml \ + --values path/to/values.yaml \ + --wait \ + --timeout 10m +``` + +Uninstall the release: + +```bash +helm uninstall vanity-gateway --namespace nvcf +``` + +## Configuration + +The default chart configuration lives in +`helm-nvcf-vanity-gateway/values.yaml`, and `values.schema.json` constrains it. +The schema sets `additionalProperties: false` on `vanityGateway` and most of its +sub-objects, so an unrecognized key fails the render rather than being ignored. + +Important settings to review before deployment: + +- `vanityGateway.image.*` for the container image +- `vanityGateway.imagePullSecrets` for private registry access +- `vanityGateway.replicaCount`, resource requests, and limits for your + environment +- `vanityGateway.config.nvcfApiEndpoint` for the invocation endpoint +- `vanityGateway.config.otelExporterOtlpEndpoint` for trace export, empty by + default +- `vanityGateway.mappingConfig.v2config` for the OpenAI and vanity route tables +- `vanityGateway.serviceMonitor.enabled` for Prometheus Operator scraping + +### Ports + +The container listens on 10081 for traffic and 10083 for admin and metrics. The +Service exposes those as `httpPort` (8080 by default) and `adminPort` (10083 by +default). Metrics are scraped from `/metrics` on the admin port, and the health +probes use `/health` on the traffic port. + +### Shutdown + +`vanityGateway.shutdown` controls draining. `preStopSleepSeconds` (70 by +default) is how long the preStop hook sleeps before the container is signaled, +which lets in-flight and newly routed requests settle. +`terminationGracePeriodSeconds` (330 by default) must stay comfortably above it, +or the pod is killed mid-drain. + +## Route mapping + +`vanityGateway.mappingConfig.v2config` has two sections: + +- `openai`: per-endpoint model routes, keyed by endpoint (`chatCompletions`, + `completions`, `embeddings`, `responses`, and the image endpoints). Each route + requires `modelName` and `functionID`, and supports shadow-traffic fields such + as `shadowModelName`, `shadowPercentage`, and + `shadowCancelOnClientDisconnect`. +- `vanity`: host-based routes, each requiring a `host` and a `paths` map. Each + path requires `path` and `functionID`. + +Both sections are empty by default. `vanityGateway.config.shadowMaxConcurrent` +bounds concurrent shadow requests across all routes. + +## Notes + +- The chart version and appVersion carry the versions of the published artifact + this chart was recovered from. They are not in a form the repo release tooling + accepts, and no release lane is registered. See `AGENTS.md`. +- If you publish or mirror the required images into another registry, set the + image registry, repository, tag, and pull secret values explicitly in your + override file. diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/.helmignore b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/Chart.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/Chart.yaml new file mode 100644 index 000000000..d4925f50e --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +appVersion: 1.32.1 +description: Helm chart for the NVCF Vanity Gateway +name: helm-nvcf-vanity-gateway +type: application +version: 0.1.0-nvcf-10204.1 diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/_helpers.tpl b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/_helpers.tpl new file mode 100644 index 000000000..ae6a5f716 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/_helpers.tpl @@ -0,0 +1,52 @@ +{{/* +SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 +*/}} + +{{- define "vanity-gateway.name" -}} +{{- default .Chart.Name .Values.vanityGateway.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- define "vanity-gateway.fullname" -}} +{{- if .Values.vanityGateway.fullnameOverride -}} +{{- .Values.vanityGateway.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.vanityGateway.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "vanity-gateway.labels" -}} +helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" | quote }} +{{ include "vanity-gateway.selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{- define "vanity-gateway.selectorLabels" -}} +app.kubernetes.io/name: {{ include "vanity-gateway.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{- define "vanity-gateway.serviceAccountName" -}} +{{- if .Values.vanityGateway.serviceAccount.create -}} +{{- default (include "vanity-gateway.fullname" .) .Values.vanityGateway.serviceAccount.name -}} +{{- else -}} +{{- default "default" .Values.vanityGateway.serviceAccount.name -}} +{{- end -}} +{{- end -}} + +{{- define "vanity-gateway.image" -}} +{{- $registry := trimSuffix "/" .Values.vanityGateway.image.registry -}} +{{- $repository := required "A valid image repository (.Values.vanityGateway.image.repository) is required!" .Values.vanityGateway.image.repository -}} +{{- $tag := default .Chart.AppVersion .Values.vanityGateway.image.tag -}} +{{- if $registry -}} +{{- printf "%s/%s:%s" $registry $repository $tag -}} +{{- else -}} +{{- printf "%s:%s" $repository $tag -}} +{{- end -}} +{{- end -}} diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/configmap.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/configmap.yaml new file mode 100644 index 000000000..e14195a72 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/configmap.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 +# +# 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: {{ include "vanity-gateway.fullname" . }}-env + labels: + {{- include "vanity-gateway.labels" . | nindent 4 }} +data: + MAPPING_PATH: {{ .Values.vanityGateway.config.mappingPath | quote }} + NVCF_API_ENDPOINT: {{ .Values.vanityGateway.config.nvcfApiEndpoint | quote }} + OTEL_EXPORTER_OTLP_ENDPOINT: {{ .Values.vanityGateway.config.otelExporterOtlpEndpoint | quote }} + SECRETS_PATH: {{ .Values.vanityGateway.config.secretsPath | quote }} + PRIVATE_MODEL_NAME_REGEX_PATTERN: {{ .Values.vanityGateway.config.privateModelNameRegexPattern | quote }} + AWS_REGION: {{ .Values.vanityGateway.config.awsRegion | quote }} + SHADOW_MAX_CONCURRENT: {{ .Values.vanityGateway.config.shadowMaxConcurrent | quote }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "vanity-gateway.fullname" . }}-config + labels: + {{- include "vanity-gateway.labels" . | nindent 4 }} +data: + config.yaml: | + {{- toYaml .Values.vanityGateway.mappingConfig | nindent 4 }} diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/deployment.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/deployment.yaml new file mode 100644 index 000000000..1dc8696a8 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/deployment.yaml @@ -0,0 +1,114 @@ +# 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 "vanity-gateway.fullname" . }} + labels: + {{- include "vanity-gateway.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.vanityGateway.replicaCount }} + selector: + matchLabels: + {{- include "vanity-gateway.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "vanity-gateway.selectorLabels" . | nindent 8 }} + {{- with .Values.vanityGateway.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vanityGateway.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "vanity-gateway.serviceAccountName" . }} + {{- with .Values.vanityGateway.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vanityGateway.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vanityGateway.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vanityGateway.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.vanityGateway.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.vanityGateway.shutdown.terminationGracePeriodSeconds }} + containers: + - name: vanity-gateway + image: {{ include "vanity-gateway.image" . }} + imagePullPolicy: {{ .Values.vanityGateway.image.pullPolicy }} + envFrom: + - configMapRef: + name: {{ include "vanity-gateway.fullname" . }}-env + env: + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + ports: + - name: http + containerPort: 10081 + protocol: TCP + - name: admin + containerPort: 10083 + protocol: TCP + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 5 + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 5 + failureThreshold: 3 + lifecycle: + preStop: + exec: + command: + - /usr/bin/sleep + - {{ .Values.vanityGateway.shutdown.preStopSleepSeconds | quote }} + resources: + {{- toYaml .Values.vanityGateway.resources | nindent 12 }} + {{- with .Values.vanityGateway.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: mapping-config + mountPath: /etc/vanity-gateway/config + readOnly: true + volumes: + - name: mapping-config + configMap: + name: {{ include "vanity-gateway.fullname" . }}-config diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/service.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/service.yaml new file mode 100644 index 000000000..c9f63fb11 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/service.yaml @@ -0,0 +1,34 @@ +# 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: Service +metadata: + name: {{ include "vanity-gateway.fullname" . }} + labels: + {{- include "vanity-gateway.labels" . | nindent 4 }} +spec: + type: {{ .Values.vanityGateway.service.type }} + selector: + {{- include "vanity-gateway.selectorLabels" . | nindent 4 }} + ports: + - name: http + port: {{ .Values.vanityGateway.service.httpPort }} + targetPort: http + protocol: TCP + - name: admin + port: {{ .Values.vanityGateway.service.adminPort }} + targetPort: admin + protocol: TCP diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/serviceaccount.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/serviceaccount.yaml new file mode 100644 index 000000000..621082925 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/serviceaccount.yaml @@ -0,0 +1,27 @@ +# 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 .Values.vanityGateway.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "vanity-gateway.serviceAccountName" . }} + labels: + {{- include "vanity-gateway.labels" . | nindent 4 }} + {{- with .Values.vanityGateway.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/servicemonitor.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/servicemonitor.yaml new file mode 100644 index 000000000..0df6f4e82 --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/templates/servicemonitor.yaml @@ -0,0 +1,37 @@ +# 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 .Values.vanityGateway.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "vanity-gateway.fullname" . }} + labels: + {{- include "vanity-gateway.labels" . | nindent 4 }} + {{- with .Values.vanityGateway.serviceMonitor.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + {{- include "vanity-gateway.selectorLabels" . | nindent 6 }} + endpoints: + - port: admin + path: /metrics + scheme: http + interval: {{ .Values.vanityGateway.serviceMonitor.interval | quote }} + attachMetadata: + node: true +{{- end }} diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/values.schema.json b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/values.schema.json new file mode 100644 index 000000000..90340b28c --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/values.schema.json @@ -0,0 +1,347 @@ +{ + "$schema": "https://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "vanityGateway": { + "type": "object", + "properties": { + "replicaCount": { + "type": "integer", + "minimum": 0 + }, + "image": { + "type": "object", + "properties": { + "registry": { + "type": "string" + }, + "repository": { + "type": "string", + "minLength": 1 + }, + "tag": { + "type": "string" + }, + "pullPolicy": { + "type": "string" + } + }, + "additionalProperties": false + }, + "imagePullSecrets": { + "type": "array" + }, + "nameOverride": { + "type": "string" + }, + "fullnameOverride": { + "type": "string" + }, + "serviceAccount": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "annotations": { + "type": "object" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false + }, + "podAnnotations": { + "type": "object" + }, + "podLabels": { + "type": "object" + }, + "podSecurityContext": { + "type": "object" + }, + "securityContext": { + "type": "object" + }, + "shutdown": { + "type": "object", + "properties": { + "terminationGracePeriodSeconds": { + "type": "integer", + "minimum": 0 + }, + "preStopSleepSeconds": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "terminationGracePeriodSeconds", + "preStopSleepSeconds" + ], + "additionalProperties": false + }, + "service": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "httpPort": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + }, + "adminPort": { + "type": "integer", + "minimum": 1, + "maximum": 65535 + } + }, + "additionalProperties": false + }, + "config": { + "type": "object", + "properties": { + "mappingPath": { + "$ref": "#/definitions/nonEmptyString" + }, + "nvcfApiEndpoint": { + "$ref": "#/definitions/nonEmptyString" + }, + "otelExporterOtlpEndpoint": { + "type": "string" + }, + "secretsPath": { + "type": "string" + }, + "privateModelNameRegexPattern": { + "type": "string" + }, + "awsRegion": { + "type": "string" + }, + "shadowMaxConcurrent": { + "type": "integer", + "minimum": 1 + } + }, + "additionalProperties": false + }, + "mappingConfig": { + "type": "object", + "properties": { + "v2config": { + "type": "object", + "properties": { + "openai": { + "$ref": "#/definitions/openAIRoutes" + }, + "vanity": { + "$ref": "#/definitions/vanityRoutes" + } + }, + "required": [ + "openai", + "vanity" + ], + "additionalProperties": true + } + }, + "required": [ + "v2config" + ], + "additionalProperties": true + }, + "resources": { + "type": "object" + }, + "nodeSelector": { + "type": "object" + }, + "tolerations": { + "type": "array" + }, + "affinity": { + "type": "object" + }, + "serviceMonitor": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "interval": { + "$ref": "#/definitions/nonEmptyString" + }, + "labels": { + "type": "object" + } + }, + "additionalProperties": false + } + }, + "required": [ + "replicaCount", + "image", + "service", + "config", + "mappingConfig" + ], + "additionalProperties": false + } + }, + "definitions": { + "nonEmptyString": { + "type": "string", + "minLength": 1 + }, + "openAIRoutes": { + "type": "object", + "properties": { + "host": { + "$ref": "#/definitions/nonEmptyString" + }, + "chatCompletions": { + "$ref": "#/definitions/openAIEndpointRoutes" + }, + "completions": { + "$ref": "#/definitions/openAIEndpointRoutes" + }, + "embeddings": { + "$ref": "#/definitions/openAIEndpointRoutes" + }, + "responses": { + "$ref": "#/definitions/openAIEndpointRoutes" + }, + "imageGenerations": { + "$ref": "#/definitions/openAIEndpointRoutes" + }, + "imageEdits": { + "$ref": "#/definitions/openAIEndpointRoutes" + }, + "imageVariations": { + "$ref": "#/definitions/openAIEndpointRoutes" + } + }, + "additionalProperties": { + "$ref": "#/definitions/openAIEndpointRoutes" + } + }, + "openAIEndpointRoutes": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/openAIModelRoute" + } + }, + "openAIModelRoute": { + "type": "object", + "properties": { + "modelName": { + "$ref": "#/definitions/nonEmptyString" + }, + "functionID": { + "$ref": "#/definitions/nonEmptyString" + }, + "functionVersionID": { + "type": "string" + }, + "outgoingPathOverride": { + "type": "string" + }, + "usePexec": { + "type": "boolean" + }, + "eol": { + "type": "string" + }, + "offlineMessage": { + "type": "string" + }, + "tooManyRequestsMessage": { + "type": "string" + }, + "shadowModelName": { + "type": "string" + }, + "shadowModelNames": { + "type": "array", + "items": { + "$ref": "#/definitions/nonEmptyString" + } + }, + "shadowPercentage": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "shadowCancelOnClientDisconnect": { + "type": "boolean" + } + }, + "required": [ + "modelName", + "functionID" + ], + "additionalProperties": true + }, + "vanityRoutes": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/vanityRoute" + } + }, + "vanityRoute": { + "type": "object", + "properties": { + "host": { + "$ref": "#/definitions/nonEmptyString" + }, + "paths": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/vanityPathRoute" + } + } + }, + "required": [ + "host", + "paths" + ], + "additionalProperties": true + }, + "vanityPathRoute": { + "type": "object", + "properties": { + "path": { + "$ref": "#/definitions/nonEmptyString" + }, + "functionID": { + "$ref": "#/definitions/nonEmptyString" + }, + "functionVersionID": { + "type": "string" + }, + "outgoingPathOverride": { + "type": "string" + }, + "usePexec": { + "type": "boolean" + }, + "eol": { + "type": "string" + }, + "offlineMessage": { + "type": "string" + } + }, + "required": [ + "path", + "functionID" + ], + "additionalProperties": true + } + }, + "additionalProperties": true +} diff --git a/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/values.yaml b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/values.yaml new file mode 100644 index 000000000..d9af9863a --- /dev/null +++ b/deploy/helm/vanity-gateway/helm-nvcf-vanity-gateway/values.yaml @@ -0,0 +1,77 @@ +# 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. + +vanityGateway: + replicaCount: 2 + + image: + registry: "" + repository: nvcf-ai-api-gateway-service + tag: "" + pullPolicy: IfNotPresent + + imagePullSecrets: [] + nameOverride: "" + fullnameOverride: vanity-gateway + + serviceAccount: + create: true + annotations: {} + name: "" + + podAnnotations: {} + podLabels: {} + podSecurityContext: {} + securityContext: {} + + shutdown: + terminationGracePeriodSeconds: 330 + preStopSleepSeconds: 70 + + service: + type: ClusterIP + httpPort: 8080 + adminPort: 10083 + + config: + mappingPath: /etc/vanity-gateway/config/config.yaml + nvcfApiEndpoint: http://invocation.nvcf.svc.cluster.local:8080 + otelExporterOtlpEndpoint: "" + secretsPath: "" + privateModelNameRegexPattern: "" + awsRegion: "" + shadowMaxConcurrent: 256 + + mappingConfig: + v2config: + openai: {} + vanity: {} + + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + + nodeSelector: {} + tolerations: [] + affinity: {} + + serviceMonitor: + enabled: false + interval: 60s + labels: {} diff --git a/deploy/helm/vanity-gateway/values.local.yaml b/deploy/helm/vanity-gateway/values.local.yaml new file mode 100644 index 000000000..9e811cc6e --- /dev/null +++ b/deploy/helm/vanity-gateway/values.local.yaml @@ -0,0 +1,35 @@ +# 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. + +vanityGateway: + replicaCount: 1 + + # values.schema.json requires a non-empty image.repository, so it cannot be + # blanked out here. Set the registry to wherever the image is available. + image: + registry: "" + repository: nvcf-ai-api-gateway-service + + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 250m + memory: 256Mi + + # Local clusters do not usually run the Prometheus Operator CRDs. + serviceMonitor: + enabled: false diff --git a/tools/ci/helm-validate-values/vanity-gateway.yaml b/tools/ci/helm-validate-values/vanity-gateway.yaml new file mode 100644 index 000000000..ab0cc7432 --- /dev/null +++ b/tools/ci/helm-validate-values/vanity-gateway.yaml @@ -0,0 +1,7 @@ +# CI-only values for helm lint/template validation. +vanityGateway: + image: + registry: example.com + repository: foo/bar/baz + serviceMonitor: + enabled: true