Skip to content

Add Helm chart for spur-cloud deployment#62

Open
yansun1996 wants to merge 1 commit into
ROCm:mainfrom
yansun1996:helm-chart
Open

Add Helm chart for spur-cloud deployment#62
yansun1996 wants to merge 1 commit into
ROCm:mainfrom
yansun1996:helm-chart

Conversation

@yansun1996

Copy link
Copy Markdown
Member

Closes #61

Summary

  • New Helm chart at deploy/helm/spur-cloud/ wrapping the existing K8s manifests.
  • API and frontend Deployments + Services, ServiceAccount + ClusterRole/Binding, Ingress, optional in-cluster Postgres, session namespace.
  • Full spur-cloud.toml rendered into a Secret (removes the existing ConfigMap leak of jwt_secret and the CHANGEME placeholder).
  • secrets.existingSecret escape hatch for ExternalSecrets / sealed-secrets workflows.
  • checksum/secret annotation on the API pod so config changes trigger a rollout.
  • Image refs default to ghcr.io/rocm/spur-cloud-{api,frontend} (override via values); pull-policy and pull-secrets are wired through.
  • Standard app.kubernetes.io/* labels and release-prefixed names so multiple instances can coexist.
  • Session namespace carries helm.sh/resource-policy: keep so helm uninstall does not delete running user pods.
  • Postgres service is clusterIP: None (proper headless service for the StatefulSet), PGDATA set to a subdir to avoid the lost+found first-boot gotcha, plus a pg_isready readiness probe.

Verification

  • helm lint deploy/helm/spur-cloud --set secrets.jwtSecret=test --set secrets.dbPassword=test — clean (only the "icon is recommended" info).
  • helm template ... renders with auth providers on/off; required-secret guards (jwtSecret, dbPassword, githubClientSecret, oktaClientSecret) fail loudly when missing.

How to try it

JWT=$(openssl rand -hex 32); DBPW=$(openssl rand -hex 16)
helm install spur-cloud ./deploy/helm/spur-cloud -n spur-cloud --create-namespace \
  --set secrets.jwtSecret="$JWT" \
  --set secrets.dbPassword="$DBPW" \
  --set ingress.host=gpu.example.com

External Postgres:

helm install spur-cloud ./deploy/helm/spur-cloud -n spur-cloud --create-namespace \
  --set postgres.enabled=false \
  --set database.url="postgresql://user:pass@managed-pg.example.com:5432/spur_cloud" \
  --set secrets.jwtSecret="$JWT"

Out of scope (tracked in #61 follow-ups)

  • HPA / PodDisruptionBudget / anti-affinity / TopologySpreadConstraints.
  • OCI registry publishing + tagged release workflow.
  • dependencies: on a future spur chart so --set spur.enabled=true brings up the whole stack.
  • Decision on whether to delete deploy/k8s/*.yaml or regenerate them from helm template in CI.

Test plan

  • helm lint passes
  • helm template renders with defaults + auth providers + existing-secret + external-DB modes
  • helm install on a real cluster (reviewer)
  • verify helm upgrade with a changed config.publicUrl rolls API pods (checksum annotation)

Wraps the existing deploy/k8s manifests into a configurable Helm chart at
deploy/helm/spur-cloud. Renders the full spur-cloud.toml into a Secret
(removing the CHANGEME placeholder leak), parameterizes image refs and
ingress, and adds optional in-cluster Postgres / existing-secret support
for ExternalSecrets workflows.

Co-Authored-By: Claude Opus 4 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 5, 2026 22:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a first-class Helm chart under deploy/helm/spur-cloud/ to deploy Spur Cloud’s control plane (API + frontend) with optional in-cluster Postgres, ingress, session namespace management, and RBAC, replacing the current “apply raw manifests” workflow with configurable Helm values and upgrade semantics.

Changes:

  • Introduces a chart skeleton (Chart.yaml, values.yaml, .helmignore) and end-user docs (README.md, NOTES.txt).
  • Adds templates for API + frontend Deployments/Services, Ingress routing, optional Postgres StatefulSet/headless Service, and session Namespace retention.
  • Renders spur-cloud.toml into a Secret (with optional secrets.existingSecret support) and wires it into the API pod.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
deploy/helm/spur-cloud/Chart.yaml Defines the spur-cloud Helm chart metadata.
deploy/helm/spur-cloud/values.yaml Provides configurable defaults for images, ingress, config, secrets, RBAC, and Postgres.
deploy/helm/spur-cloud/README.md Documents installation and configuration patterns (including external DB / existing secret).
deploy/helm/spur-cloud/.helmignore Standard Helm packaging ignore patterns.
deploy/helm/spur-cloud/templates/_helpers.tpl Naming/label helpers and DB URL rendering helpers.
deploy/helm/spur-cloud/templates/secret.yaml Creates the Secret containing spur-cloud.toml (+ db password material).
deploy/helm/spur-cloud/templates/api.yaml Deploys the API and mounts the rendered config Secret; includes rollout checksum.
deploy/helm/spur-cloud/templates/frontend.yaml Deploys the frontend Service + Deployment.
deploy/helm/spur-cloud/templates/ingress.yaml Routes /api to API and / to frontend.
deploy/helm/spur-cloud/templates/postgres.yaml Optional in-cluster Postgres StatefulSet + headless Service.
deploy/helm/spur-cloud/templates/rbac.yaml ServiceAccount and ClusterRole/ClusterRoleBinding.
deploy/helm/spur-cloud/templates/session-namespace.yaml Optional retained session namespace creation.
deploy/helm/spur-cloud/templates/NOTES.txt Post-install guidance and reminders.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

enabled: false
secretName: ""

# Application config rendered into the ConfigMap as spur-cloud.toml.
Comment on lines +56 to +62
{{- define "spur-cloud.secretName" -}}
{{- if .Values.secrets.existingSecret -}}
{{- .Values.secrets.existingSecret -}}
{{- else -}}
{{ include "spur-cloud.fullname" . }}-secrets
{{- end -}}
{{- end -}}
{{- if not .Values.secrets.dbPassword -}}
{{- fail "secrets.dbPassword must be set when postgres.enabled is true and secrets.existingSecret is unused" -}}
{{- end -}}
{{- printf "postgresql://%s:%s@%s:5432/%s" .Values.postgres.user .Values.secrets.dbPassword (include "spur-cloud.postgres.fullname" .) .Values.postgres.database -}}
Comment on lines +55 to +57
{{- if and .Values.postgres.enabled (not .Values.database.url) }}
db-password: {{ .Values.secrets.dbPassword | quote }}
{{- end }}
Comment on lines +1 to +4
{{- if .Values.secrets.create -}}
{{- if not .Values.secrets.jwtSecret -}}
{{- fail "secrets.jwtSecret is required (generate with: openssl rand -hex 32) — or set secrets.existingSecret and secrets.create=false" -}}
{{- end -}}
Comment on lines +12 to +15
{{- end }}
{{- if .Values.rbac.create }}
---
apiVersion: rbac.authorization.k8s.io/v1
enabled: true
replicas: 2
image:
repository: ghcr.io/rocm/spur-cloud-api

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have these images yet. This can be misleading.

name: spur-cloud
description: GPU as a Service platform built on Spur. Deploys the API, frontend, optional in-cluster Postgres, RBAC, and ingress.
type: application
version: 0.1.0

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
version: 0.1.0
version: 0.0.1-dev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Helm chart for spur-cloud deployment

3 participants