Commit 7662de1c authored by mohammedabdulwahhab's avatar mohammedabdulwahhab Committed by GitHub
Browse files

feat: Add POST deployments endpoint to api-store service in Dynamo cloud (#582)


Co-authored-by: default avatarHannah Zhang <hannahz@nvidia.com>
Co-authored-by: default avatarJulien Mancuso <jmancuso@nvidia.com>
Co-authored-by: default avatarmabdulwahhab <mabdulwahhab@nvidia.com>
parent 0292feb5
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
import os
import uuid
from datetime import datetime
from typing import Optional
from fastapi import APIRouter, HTTPException
from ..models.schemas import (
CreateDeploymentSchema,
DeploymentFullSchema,
ResourceSchema,
create_default_cluster,
create_default_user,
)
from .k8s import create_dynamo_deployment
router = APIRouter(prefix="/api/v2/deployments", tags=["deployments"])
def sanitize_deployment_name(name: Optional[str], dynamo_nim: str) -> str:
"""
Resolve a name for the DynamoDeployment that will work safely in k8s
Args:
name: Optional custom name
dynamo_nim: Bento name and version (format: name:version)
Returns:
A unique deployment name that is at most 63 characters
"""
if name:
# If name is provided, truncate it to 55 chars to leave room for UUID
base_name = name[:55]
else:
# Generate base name from dynamoNim
dynamo_nim_parts = dynamo_nim.split(":")
if len(dynamo_nim_parts) != 2:
raise ValueError("Invalid dynamoNim format, expected 'name:version'")
base_name = f"dep-{dynamo_nim_parts[0]}-{dynamo_nim_parts[1]}"
# Truncate to 55 chars to leave room for UUID
base_name = base_name[:55]
# Add UUID and ensure total length is <= 63
return f"{base_name}-{uuid.uuid4().hex[:7]}"
@router.post("", response_model=DeploymentFullSchema)
async def create_deployment(deployment: CreateDeploymentSchema):
"""
Create a new deployment.
Args:
deployment: The deployment configuration following CreateDeploymentSchema
Returns:
DeploymentFullSchema: The created deployment details
"""
try:
# Get ownership info for labels
ownership = {"organization_id": "default-org", "user_id": "default-user"}
# Get the k8s namespace from environment variable
kube_namespace = os.getenv("DEFAULT_KUBE_NAMESPACE", "dynamo")
# Generate deployment name
deployment_name = sanitize_deployment_name(deployment.name, deployment.bento)
# Create the deployment using helper function
created_crd = create_dynamo_deployment(
name=deployment_name,
namespace=kube_namespace,
dynamo_nim=deployment.bento,
labels={
"ngc-organization": ownership["organization_id"],
"ngc-user": ownership["user_id"],
},
)
# Create response schema
resource = ResourceSchema(
uid=created_crd["metadata"]["uid"],
name=created_crd["metadata"]["name"],
created_at=datetime.utcnow(),
updated_at=datetime.utcnow(),
resource_type="deployment",
labels=[],
)
# Use helper functions for default resources
creator = create_default_user()
cluster = create_default_cluster(creator)
deployment_schema = DeploymentFullSchema(
**resource.dict(),
status="running",
kube_namespace=kube_namespace,
creator=creator,
cluster=cluster,
latest_revision=None,
manifest=None,
urls=[f"https://{created_crd['metadata']['name']}.dynamo.example.com"],
)
return deployment_schema
except Exception as e:
print("Error creating deployment:")
print(e)
raise HTTPException(status_code=500, detail=str(e))
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
from typing import Any, Dict
from kubernetes import client, config
def create_custom_resource(
group: str, version: str, namespace: str, plural: str, body: Dict[str, Any]
) -> Dict[str, Any]:
"""
Create a custom resource in Kubernetes.
Args:
group: API group
version: API version
namespace: Target namespace
plural: Resource plural name
body: Resource definition
Returns:
Created resource
"""
try:
config.load_incluster_config()
except config.config_exception.ConfigException:
config.load_kube_config()
api = client.CustomObjectsApi()
return api.create_namespaced_custom_object(
group=group, version=version, namespace=namespace, plural=plural, body=body
)
def create_dynamo_deployment(
name: str, namespace: str, dynamo_nim: str, labels: Dict[str, str]
) -> Dict[str, Any]:
"""
Create a DynamoDeployment custom resource.
Args:
name: Deployment name
namespace: Target namespace
dynamo_nim: Bento name and version (format: name:version)
labels: Resource labels
Returns:
Created deployment
"""
body = {
"apiVersion": "nvidia.com/v1alpha1",
"kind": "DynamoDeployment",
"metadata": {"name": name, "namespace": namespace, "labels": labels},
"spec": {"dynamoNim": dynamo_nim, "services": {}},
}
return create_custom_resource(
group="nvidia.com",
version="v1alpha1",
namespace=namespace,
plural="dynamodeployments",
body=body,
)
......@@ -20,6 +20,7 @@ import sys
import uvicorn
from fastapi import FastAPI
from .api.deployments import router as deployments_router
from .api.dynamo import router as dynamo_router # type: ignore
from .api.health_check import router as health_check_router
from .api.storage import create_db_and_tables_async
......@@ -66,6 +67,7 @@ async def run_app():
app.include_router(health_check_router)
app.include_router(dynamo_router)
app.include_router(deployments_router)
port = int(os.getenv("SERVICE_PORT", "8000"))
await initialize_database()
......
# SPDX-FileCopyrightText: Copyright (c) 2022 Atalaya Tech. Inc
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
from datetime import datetime
from typing import Dict, List, Optional
from pydantic import BaseModel, Field
class BaseSchema(BaseModel):
uid: str
created_at: datetime
updated_at: Optional[datetime] = None
deleted_at: Optional[datetime] = None
class ResourceSchema(BaseSchema):
name: str
resource_type: str
labels: List[Dict[str, str]]
class UserSchema(BaseModel):
name: str
email: str
first_name: str
last_name: str
class ClusterSchema(ResourceSchema):
description: str
organization_name: str
creator: UserSchema
is_first: bool = False
class DeploymentConfigSchema(BaseModel):
access_authorization: bool = False
envs: Optional[List[Dict[str, str]]] = None
labels: Optional[List[Dict[str, str]]] = None
secrets: Optional[List[str]] = None
services: Dict[str, Dict] = Field(default_factory=dict)
class UpdateDeploymentSchema(DeploymentConfigSchema):
bento: str
class CreateDeploymentSchema(UpdateDeploymentSchema):
name: Optional[str] = None
dev: bool = False
class DeploymentSchema(ResourceSchema):
status: str
kube_namespace: str
creator: UserSchema
cluster: ClusterSchema
latest_revision: Optional[Dict] = None
manifest: Optional[Dict] = None
class DeploymentFullSchema(DeploymentSchema):
urls: List[str] = Field(default_factory=list)
def create_default_user() -> UserSchema:
"""Create a default user schema for testing/demo purposes."""
return UserSchema(
name="default-user",
email="default@example.com",
first_name="Default",
last_name="User",
)
def create_default_cluster(creator: UserSchema) -> ClusterSchema:
"""Create a default cluster schema for testing/demo purposes."""
return ClusterSchema(
uid="default-cluster",
name="default",
created_at=datetime.utcnow(),
updated_at=datetime.utcnow(),
resource_type="cluster",
labels=[],
description="Default cluster",
organization_name="default-org",
creator=creator,
is_first=True,
)
......@@ -34,6 +34,7 @@ dependencies = [
"boto3==1.37.1",
"botocore==1.37.1",
"sqlmodel==0.0.22",
"kubernetes"
]
[project.optional-dependencies]
......
# Deploy CompoundAI API server and Operator
# Deploy Dynamo Cloud
### Manually install etcd and nats
## Deploy Dynamo Cloud Platform
Pre-requisite: make sure your terminal is set in the `deploy/dynamo/helm/` directory.
1. [Optional] Create a new kubernetes namespace and set it as your default
```bash
export KUBE_NS=cai # change this to whatever you want!
kubectl create namespace $KUBE_NS
kubectl config set-context --current --namespace=$KUBE_NS
cd deploy/dynamo/helm
export KUBE_NS=hello-world # change this to whatever you want!
```
2. Install bitnami/etcd:
1. [One-time Action] Create a new kubernetes namespace and set it as your default. Create image pull secrets if needed.
```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
```
3. Install nats:
```bash
helm repo add nats https://nats-io.github.io/k8s/helm/charts/
helm repo update
```
4. Install etcd and nats to your kubernetes namespace:
kubectl create namespace $KUBE_NS
kubectl config set-context --current --namespace=$KUBE_NS
```bash
helm install etcd bitnami/etcd -n $KUBE_NS -f etcd.yaml
helm install my-nats nats/nats --version 1.2.9 -f nats.yaml -n $KUBE_NS
# [Optional] if needed, create image pull secrets
kubectl create secret docker-registry docker-imagepullsecret \
--docker-server=<your-registry> \
--docker-username=<your-username> \
--docker-password=<your-password> \
--namespace=$KUBE_NS
```
5. Deploy the helm charts:
2. Deploy the helm chart using the deploy script:
```bash
export NGC_TOKEN=$NGC_API_TOKEN
export NAMESPACE=$KUBE_NS
export CI_COMMIT_SHA=6083324a0a5f310dcec38c6863f043cd9070ffcc
export CI_COMMIT_SHA=<TAG> # Use the same tag you used when building the images
export CI_REGISTRY_IMAGE=<CONTAINER_REGISTRY>/<ORGANIZATION> # Use the same registry/org you used when building the images
export RELEASE_NAME=$KUBE_NS
./deploy.sh
```
\ No newline at end of file
6. Make an example cluster POST:
```bash
./post-cluster.sh
```
As a bonus, the CompoundAI Deployments UI is also deployed alongside, so you can access it at https://${NAMESPACE}.dev.aire.nvidia.com/
\ No newline at end of file
......@@ -17,50 +17,87 @@
set -euo pipefail
# Use system helm
HELM_CMD=$(which helm)
# Set default values only if not already set
export NAMESPACE="${NAMESPACE:=cai-system}" # Default namespace
export NGC_TOKEN="${NGC_TOKEN:=<your-ngc-token>}" # Default NGC token
export CI_REGISTRY_IMAGE="${CI_REGISTRY_IMAGE:=<your-registry>/<your-org>}" # Default registry/org
export CI_COMMIT_SHA="${CI_COMMIT_SHA:=250e2e0f93f7af3d83a4a0ff992e56956f7651f2}" # Default commit SHA
export RELEASE_NAME="${RELEASE_NAME:=dynamo-platform}" # Default commit SHA
export RELEASE_NAME="${RELEASE_NAME:=dynamo-platform}" # Default release name
export DYNAMO_INGRESS_SUFFIX="${DYNAMO_INGRESS_SUFFIX:=}"
# Check if required variables are set
if [ "$NGC_TOKEN" = "<your-ngc-token>" ]; then
echo "Error: Please set your NGC_TOKEN in the script or via environment variable"
exit 1
fi
if [ "$CI_REGISTRY_IMAGE" = "<your-registry>/<your-org>" ]; then
echo "Error: Please set your CI_REGISTRY_IMAGE in the script or via environment variable"
exit 1
fi
# Function to retry commands
retry_command() {
local -r cmd="$1"
local -r max_attempts=${2:-3}
local -r delay=${3:-5}
local attempt=1
until eval "$cmd"; do
if ((attempt >= max_attempts)); then
echo "Command '$cmd' failed after $attempt attempts"
return 1
fi
echo "Command '$cmd' failed, attempt $attempt of $max_attempts. Retrying in ${delay}s..."
((attempt++))
sleep "$delay"
done
}
# Update the helm repo and build the dependencies
retry_command "$HELM_CMD repo add nats https://nats-io.github.io/k8s/helm/charts/" 5 5 && \
retry_command "$HELM_CMD repo add bitnami https://charts.bitnami.com/bitnami" 5 5 && \
retry_command "$HELM_CMD repo add minio https://charts.min.io/" 5 5 && \
retry_command "$HELM_CMD repo update" 5 5
cd platform
cd components/operator
helm dependency update
retry_command "$HELM_CMD dependency update" 5 5
cd ../..
cd components/api-server
helm dependency update
cd components/api-store
retry_command "$HELM_CMD dependency update" 5 5
cd ../..
helm dep update
helm repo update
retry_command "$HELM_CMD dep update" 7 5
cd ..
# Generate the values file
echo "Generating values file with:"
echo "NAMESPACE: $NAMESPACE"
echo "CI_COMMIT_SHA: $CI_COMMIT_SHA"
echo "CI_REGISTRY_IMAGE: $CI_REGISTRY_IMAGE"
echo "NGC_TOKEN: [HIDDEN]"
echo "RELEASE_NAME: $RELEASE_NAME"
echo "generated file contents:"
envsubst '${NAMESPACE} ${NGC_TOKEN} ${CI_COMMIT_SHA} ${RELEASE_NAME} ${DYNAMO_INGRESS_SUFFIX}' < dynamo-platform-values.yaml
envsubst '${NAMESPACE} ${NGC_TOKEN} ${CI_COMMIT_SHA} ${RELEASE_NAME} ${DYNAMO_INGRESS_SUFFIX} ${CI_REGISTRY_IMAGE}' < dynamo-platform-values.yaml
envsubst '${NAMESPACE} ${NGC_TOKEN} ${CI_COMMIT_SHA} ${RELEASE_NAME} ${DYNAMO_INGRESS_SUFFIX}' < dynamo-platform-values.yaml > generated-values.yaml
envsubst '${NAMESPACE} ${NGC_TOKEN} ${CI_COMMIT_SHA} ${RELEASE_NAME} ${DYNAMO_INGRESS_SUFFIX} ${CI_REGISTRY_IMAGE}' < dynamo-platform-values.yaml > generated-values.yaml
echo ""
echo "Generated values file saved as generated-values.yaml"
Build dependencies before installation
echo "Building helm dependencies..."
cd platform
retry_command "$HELM_CMD dep build" 5 5
cd ..
# Install/upgrade the helm chart
echo "Installing/upgrading helm chart..."
helm upgrade --install $RELEASE_NAME platform/ \
$HELM_CMD upgrade --install $RELEASE_NAME platform/ \
-f generated-values.yaml \
--create-namespace \
--namespace ${NAMESPACE}
......
......@@ -28,7 +28,7 @@ dynamo-operator:
controllerManager:
manager:
image:
repository: gitlab-master.nvidia.com:5005/aire/microservices/compoundai/dynamo-operator
repository: ${CI_REGISTRY_IMAGE}/dynamo-operator
tag: ${CI_COMMIT_SHA}
args:
- --health-probe-bind-address=:8081
......@@ -37,12 +37,10 @@ dynamo-operator:
dynamo:
dynamoIngressSuffix: ${DYNAMO_INGRESS_SUFFIX}
yatai:
# todo: only use dns name
endpoint: http://dynamo-server
endpoint: http://dynamo-store
clusterName: default
yataiSystem:
# can we just infer this, or change scheme to use default until overridden
namespace: ""
internalImages:
......@@ -63,10 +61,7 @@ dynamo-operator:
server: 'nvcr.io/nvidian/nim-llm-dev'
inClusterServer: ''
username: '$oauthtoken'
# Password will use global.NGC_API_KEY by default
# password: ""
passwordExistingSecretName: 'nvcrimagepullsecret'
# passwordExistingSecretKey: ''
secure: true
bentoRepositoryName: yatai-bentos
......@@ -80,31 +75,32 @@ dynamo-operator:
cacheRepo: ''
snapshotMode: ''
dynamo-api-server:
dynamo-api-store:
enabled: true
namespaceRestriction:
enabled: true
targetNamespace: ${NAMESPACE}
istio:
host:
${NAMESPACE}.dev.aire.nvidia.com
host: ${NAMESPACE}.dev.aire.nvidia.com
dynamo:
# need to be set to deployment-management
env:
resource_scope: "user"
image:
repository: gitlab-master.nvidia.com:5005/aire/microservices/compoundai/dynamo-api-server
# temporarily force to use old commit for api-server
tag: fccbb8777fbd2ac11dad4871c8a8ba6884525e07
#tag: ${CI_COMMIT_SHA}
pullPolicy: IfNotPresent
storeImage:
repository: gitlab-master.nvidia.com:5005/aire/microservices/compoundai/dynamo-api-store
repository: ${CI_REGISTRY_IMAGE}/dynamo-api-store
tag: ${CI_COMMIT_SHA}
pullPolicy: IfNotPresent
imagePullSecrets:
- name: nvcrimagepullsecret
- name: docker-imagepullsecret
- name: gitlab-imagepull
ingress:
enabled: true
className: nginx
hosts:
- host: ${NAMESPACE}.dev.aire.nvidia.com
paths:
- path: /
pathType: Prefix
postgresql:
primary:
persistence:
......@@ -126,6 +122,8 @@ etcd:
enabled: true
storageClass: "local-path"
size: 1Gi
preUpgrade:
enabled: false
nats:
enabled: true
......
......@@ -26,12 +26,12 @@ dependencies:
version: 0.1.1
repository: file://components/operator
condition: dynamo-operator.enabled
- name: dynamo-api-server
version: 0.1.1
repository: "file://components/api-server"
condition: dynamo-api-server.enabled
- name: dynamo-api-store
version: 0.1.0
repository: file://components/api-store
condition: dynamo-api-store.enabled
- name: nats
version: 1.2.9
version: 1.3.2
repository: https://nats-io.github.io/k8s/helm/charts/
condition: nats.enabled
- name: etcd
......
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
apiVersion: v2
name: dynamo-api-server
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.1
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.1.0"
# These are the dependencies needed by the API server to run
dependencies:
- name: postgresql
version: "16.0.4" # This is an example; update with the latest version if needed
repository: "https://charts.bitnami.com/bitnami"
# https://github.com/bitnami/charts/tree/main/bitnami/minio
- name: minio
version: 13.3.1
repository: oci://registry-1.docker.io/bitnamicharts
\ No newline at end of file
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
1. Get the application URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "helm.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "helm.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "helm.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "helm.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT
{{- end }}
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "helm.fullname" . }}
labels:
{{- include "helm.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "helm.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "helm.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "helm.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
initContainers:
- name: wait-for-postgres
image: busybox
command: [ 'sh', '-c', 'until nc -z {{ .Release.Name }}-postgresql 5432; do echo "PostgreSQL is unavailable. Sleeping for 5 seconds"; sleep 5; done;' ]
containers:
- name: "api-server"
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: {{ .Values.dynamo.apiServer.port }}
protocol: TCP
livenessProbe:
{{- toYaml .Values.livenessProbe | nindent 12 }}
readinessProbe:
{{- toYaml .Values.readinessProbe | nindent 12 }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.volumeMounts }}
volumeMounts:
{{- toYaml . | nindent 12 }}
{{- end }}
env:
- name: DB_HOST
value: "{{ .Release.Name }}-postgresql"
- name: DB_USER
value: {{ .Values.postgresql.auth.username | quote }}
- name: DB_NAME
value: {{ .Values.postgresql.auth.database | quote }}
- name: DB_PORT
value: "5432"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: "{{ .Release.Name }}-postgresql"
key: password
- name: API_DATABASE_PORT
value: "8001"
- name: API_BACKEND_URL
value: "http://dynamo-store"
- name: RESOURCE_SCOPE
value: {{ .Values.dynamo.env.resource_scope | quote }}
- name: DEFAULT_KUBE_NAMESPACE
value: {{ .Release.Namespace }}
envFrom:
- secretRef:
name: dynamo-deployment-env
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
{{- with .Values.volumes }}
volumes:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dynamo-ui
spec:
progressDeadlineSeconds: 600
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: dynamo-ui
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
app: dynamo-ui
spec:
containers:
- image: nvcr.io/nvidian/nim-llm-dev/compoundai-ui:0.0.11
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /health
port: 80
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 10
name: dynamo-ui
ports:
- containerPort: 80
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /health
port: 80
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
memory: 2Gi
requests:
memory: 1Gi
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
imagePullSecrets:
- name: nvcrimagepullsecret
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
# SPDX-FileCopyrightText: Copyright (c) 2025 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 (not .Values.objectStore.existingSecret) }}
---
apiVersion: v1
kind: Secret
metadata:
name: {{ include "api-server.objectStore.secretName" . }}
namespace: {{ include "common.names.namespace" . | quote }}
type: Opaque
data:
accessKey: {{ .Values.objectStore.accessKey | b64enc | quote }}
accessSecret: {{ .Values.objectStore.accessSecret | b64enc | quote }}
endpoint: {{ .Values.objectStore.endpoint | b64enc | quote }}
{{- end }}
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
apiVersion: v1
kind: Service
metadata:
name: "dynamo-store"
labels:
{{- include "helm.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.dynamo.apiStore.port }}
protocol: TCP
name: http
selector:
app: dynamo-api-store
# SPDX-FileCopyrightText: Copyright (c) 2025 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.
apiVersion: v1
kind: Service
metadata:
name: dynamo-ui
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: dynamo-ui
sessionAffinity: None
type: ClusterIP
......@@ -12,18 +12,16 @@
# 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: Pod
metadata:
name: "{{ include "helm.fullname" . }}-test-connection"
labels:
{{- include "helm.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "helm.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
apiVersion: v2
name: dynamo-api-store
description: A Helm chart for the Dynamo API Store component
type: application
version: 0.1.0
appVersion: "1.0.0"
dependencies:
- name: postgresql
version: "16.6.2" # This is an example; update with the latest version if needed
repository: "https://charts.bitnami.com/bitnami"
- name: minio
version: "16.0.2"
repository: "https://charts.bitnami.com/bitnami"
\ No newline at end of file
......@@ -62,6 +62,7 @@ Selector labels
{{- define "helm.selectorLabels" -}}
app.kubernetes.io/name: {{ include "helm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app: {{ include "helm.name" . }}
{{- end }}
{{/*
......@@ -74,49 +75,3 @@ Create the name of the service account to use
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
ObjectStore Secret Name
*/}}
{{- define "api-server.objectStore.secretName" -}}
{{- if .Values.objectStore.existingSecret -}}
{{- print .Values.objectStore.existingSecret -}}
{{- else -}}
{{- printf "%s-%s" (include "common.names.fullname" .) "external-objstore" -}}
{{- end -}}
{{- end -}}
{{/*
ObjectStore Access Key
*/}}
{{- define "api-server.objectStore.accessKey" -}}
{{- if .Values.objectStore.existingSecret -}}
{{- print .Values.objectStore.existingSecretAccessKey -}}
{{- else -}}
{{- print "accessKey" -}}
{{- end -}}
{{- end -}}
{{/*
ObjectStore Access Secret
*/}}
{{- define "api-server.objectStore.accessSecret" -}}
{{- if .Values.objectStore.existingSecret -}}
{{- print .Values.objectStore.existingSecretAccessSecret -}}
{{- else -}}
{{- print "accessSecret" -}}
{{- end -}}
{{- end -}}
{{/*
Object Store Endpoint
*/}}
{{- define "api-server.objectStore.endpoint" -}}
{{- if .Values.objectStore.existingSecret -}}
{{- print .Values.objectStore.existingSecretEndpoint -}}
{{- else -}}
{{- print "endpoint" -}}
{{- end -}}
{{- end -}}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment