From f79be89e4e38f21c4229018d970922261636fd01 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Sun, 15 Feb 2026 08:36:45 -0500 Subject: [PATCH] docs: Add comprehensive deployment and variables reference Add detailed documentation to enable autonomous deployment by other AI agents. New files: - DEPLOYMENT.md: Complete step-by-step deployment guide - Prerequisites checklist - Required vs optional variables clearly marked - All configuration variables documented with examples - Troubleshooting section - Quick deploy example with all values - Configuration summary table - VARIABLES.md: Complete variables reference - All variables listed with file, line, type, description - Required variables clearly marked - Optional variables with defaults - Variables grouped by use case - Quick copy templates - Environment variables reference Updated README.md: - Add links to new documentation files - Clear pointer to DEPLOYMENT.md for deployment These docs enable another Claude Code instance to: 1. Identify all required configuration variables 2. Know where to find/set each variable 3. Understand what values are valid 4. Deploy successfully without human intervention Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude Co-Authored-By: Happy --- DEPLOYMENT.md | 436 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 8 + VARIABLES.md | 305 +++++++++++++++++++++++++++++++++++ 3 files changed, 749 insertions(+) create mode 100644 DEPLOYMENT.md create mode 100644 VARIABLES.md diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000..8549c75 --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,436 @@ +# Deployment Guide + +This guide provides step-by-step instructions for deploying the Antigravity Dev Container to Kubernetes. + +## Prerequisites + +- Kubernetes cluster with Gateway API support +- `kubectl` configured to access your cluster +- ReadWriteMany storage class available (e.g., `ceph-filesystem`, `nfs-client`, `efs-sc`) +- Sealed Secrets controller installed (for secret encryption) +- GitHub Container Registry access (images are public) + +## Required Configuration Variables + +Before deploying, you need to provide the following configuration: + +### 1. Storage Configuration + +**Variable:** `storageClassName` +**Location:** `k8s/statefulset.yaml` (line ~117) +**Description:** The ReadWriteMany storage class name in your cluster +**Example values:** +- `ceph-filesystem` (Rook-Ceph) +- `nfs-client` (NFS) +- `efs-sc` (AWS EFS) +- `azurefile` (Azure Files) +- `filestore` (GCP Filestore) + +**How to find your storage class:** +```bash +kubectl get storageclass +``` + +Look for a storage class that supports `ReadWriteMany` access mode. + +### 2. GitHub Repository (Required) + +**Variable:** `github-repo` +**Location:** `k8s/configmap.yaml` (line ~9) +**Description:** The GitHub repository URL to clone on container startup +**Format:** `https://github.com/username/repository` +**Example:** `https://github.com/cpfarhood/my-project` + +### 3. GitHub Token (Optional, for private repos) + +**Variable:** `github-token` +**Location:** `k8s/secrets-example.yaml` (sealed secret) +**Description:** GitHub Personal Access Token for cloning private repositories +**Format:** `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` +**Required:** Only if cloning a private repository + +**How to create a GitHub token:** +1. Go to https://github.com/settings/tokens +2. Click "Generate new token (classic)" +3. Select scopes: `repo` (for private repos) +4. Generate and copy the token + +### 4. VNC Password (Optional) + +**Variable:** `vnc-password` +**Location:** `k8s/secrets-example.yaml` (sealed secret) +**Description:** Password for accessing the VNC web interface +**Format:** Any string (recommend 12+ characters) +**Required:** Optional, but recommended for security + +### 5. Gateway Configuration (Required for external access) + +**Variables:** +- `parentRefs.name` - Your Gateway resource name +- `parentRefs.namespace` - Namespace where Gateway is deployed +- `hostnames` - Domain name for accessing the container + +**Location:** `k8s/httproute.yaml` +**Example:** +```yaml +parentRefs: +- name: cilium-gateway # Your Gateway name + namespace: kube-system # Your Gateway namespace +hostnames: +- "devcontainer.example.com" # Your domain +``` + +### 6. Namespace (Optional) + +**Variable:** `namespace` +**Location:** `k8s/kustomization.yaml` (line ~5) +**Description:** Kubernetes namespace to deploy into +**Default:** `default` +**Example:** `devcontainer`, `development`, `team-workspaces` + +### 7. Container Image (Optional) + +**Variable:** `image` +**Location:** `k8s/statefulset.yaml` (line ~32) +**Description:** Docker image to use +**Default:** `ghcr.io/cpfarhood/devcontainer:latest` +**Format:** `registry/repository:tag` + +### 8. Resource Limits (Optional) + +**Variables:** +- `resources.requests.memory` (default: `2Gi`) +- `resources.requests.cpu` (default: `1000m`) +- `resources.limits.memory` (default: `8Gi`) +- `resources.limits.cpu` (default: `4000m`) + +**Location:** `k8s/statefulset.yaml` (lines ~98-103) + +### 9. Happy Coder Configuration (Optional) + +**Variables:** +- `happy-server-url` - Custom Happy server URL +- `happy-webapp-url` - Custom Happy webapp URL + +**Location:** `k8s/configmap.yaml` (lines ~12-13, commented out) +**Default:** Uses Happy's default servers +**When to set:** Only if using a self-hosted Happy instance + +## Deployment Steps + +### Step 1: Clone the Repository + +```bash +git clone https://github.com/cpfarhood/devcontainer.git +cd devcontainer +``` + +### Step 2: Configure Storage Class + +Edit `k8s/statefulset.yaml` and find the `volumeClaimTemplates` section (around line 117): + +```bash +# Find your storage class +kubectl get storageclass + +# Edit the file +vi k8s/statefulset.yaml +``` + +Change `storageClassName` to match your cluster: +```yaml +volumeClaimTemplates: +- metadata: + name: userhome + spec: + accessModes: [ "ReadWriteMany" ] + storageClassName: "ceph-filesystem" # ← Change this + resources: + requests: + storage: 10Gi +``` + +### Step 3: Configure GitHub Repository + +Edit `k8s/configmap.yaml`: + +```bash +vi k8s/configmap.yaml +``` + +Set your repository URL: +```yaml +data: + github-repo: "https://github.com/yourusername/yourrepo" +``` + +### Step 4: Configure Gateway (HTTPRoute) + +Edit `k8s/httproute.yaml`: + +```bash +# Find your Gateway +kubectl get gateway -A + +# Edit the file +vi k8s/httproute.yaml +``` + +Update with your Gateway details: +```yaml +spec: + parentRefs: + - name: your-gateway-name # ← Change this + namespace: your-gateway-namespace # ← Change this + hostnames: + - "devcontainer.yourdomain.com" # ← Change this +``` + +### Step 5: Create Secrets + +Create the secrets for GitHub token and VNC password: + +```bash +# Create the secret +kubectl create secret generic antigravity-secrets \ + --from-literal=github-token='ghp_your_token_here' \ + --from-literal=vnc-password='your_vnc_password' \ + --dry-run=client -o yaml | \ + kubeseal --format=yaml > k8s/sealedsecrets.yaml + +# Verify the sealed secret was created +cat k8s/sealedsecrets.yaml +``` + +**If you don't have Sealed Secrets controller:** + +Option 1: Install Sealed Secrets +```bash +kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml +``` + +Option 2: Use plain secrets (not recommended for production) +```bash +kubectl create secret generic antigravity-secrets \ + --from-literal=github-token='ghp_your_token_here' \ + --from-literal=vnc-password='your_vnc_password' +``` + +### Step 6: Review Configuration (Optional) + +Review and adjust optional settings: + +**Namespace:** +```bash +vi k8s/kustomization.yaml +# Change line 5: namespace: default +``` + +**Resource limits:** +```bash +vi k8s/statefulset.yaml +# Adjust lines 98-103 for your needs +``` + +### Step 7: Deploy to Kubernetes + +```bash +# Deploy everything +kubectl apply -k k8s/ + +# Or if you changed the namespace +kubectl apply -k k8s/ -n your-namespace +``` + +### Step 8: Verify Deployment + +```bash +# Check StatefulSet +kubectl get statefulset antigravity + +# Check Pod +kubectl get pods -l app=antigravity + +# Check PVC +kubectl get pvc -l app=antigravity + +# Check HTTPRoute +kubectl get httproute antigravity + +# View logs +kubectl logs antigravity-0 +``` + +### Step 9: Access the Container + +**Option A: Via HTTPRoute (external access)** +```bash +# Open in browser +open https://devcontainer.yourdomain.com +``` + +**Option B: Via Port Forward (local access)** +```bash +# Port forward to localhost +kubectl port-forward statefulset/antigravity 5800:5800 + +# Open in browser +open http://localhost:5800 +``` + +## Configuration Summary + +Here's a quick checklist of all variables you need to set: + +### Required Variables + +| Variable | File | Line | Example Value | +|----------|------|------|---------------| +| `storageClassName` | `k8s/statefulset.yaml` | ~117 | `ceph-filesystem` | +| `github-repo` | `k8s/configmap.yaml` | ~9 | `https://github.com/user/repo` | +| `parentRefs.name` | `k8s/httproute.yaml` | ~8 | `cilium-gateway` | +| `parentRefs.namespace` | `k8s/httproute.yaml` | ~9 | `kube-system` | +| `hostnames` | `k8s/httproute.yaml` | ~10 | `devcontainer.example.com` | + +### Optional Variables + +| Variable | File | Line | Default | When to Change | +|----------|------|------|---------|----------------| +| `namespace` | `k8s/kustomization.yaml` | ~5 | `default` | If deploying to different namespace | +| `github-token` | Sealed secret | N/A | None | For private repos | +| `vnc-password` | Sealed secret | N/A | None | For VNC security | +| `image` | `k8s/statefulset.yaml` | ~32 | `ghcr.io/cpfarhood/devcontainer:latest` | For specific version or custom build | +| `resources.*` | `k8s/statefulset.yaml` | ~98-103 | 2Gi/8Gi RAM, 1/4 CPU | Based on workload needs | +| `happy-server-url` | `k8s/configmap.yaml` | ~12 | Default Happy server | For self-hosted Happy | +| `happy-webapp-url` | `k8s/configmap.yaml` | ~13 | Default Happy webapp | For self-hosted Happy | + +## Troubleshooting + +### Pod not starting + +**Check events:** +```bash +kubectl describe pod antigravity-0 +``` + +**Common issues:** +- Storage class doesn't support ReadWriteMany +- PVC not binding (check storage class exists) +- Image pull errors (check image name) + +### Repository not cloning + +**Check logs:** +```bash +kubectl logs antigravity-0 | grep -A 10 "Repository Initialization" +``` + +**Common issues:** +- Invalid GitHub URL +- Private repo without token +- Token doesn't have correct permissions + +### HTTPRoute not working + +**Check HTTPRoute:** +```bash +kubectl describe httproute antigravity +``` + +**Common issues:** +- Gateway name/namespace incorrect +- Domain not pointing to Gateway +- TLS certificate not issued + +### VNC not accessible + +**Check service:** +```bash +kubectl get svc antigravity +kubectl describe svc antigravity +``` + +**Port forward test:** +```bash +kubectl port-forward antigravity-0 5800:5800 +# Try accessing http://localhost:5800 +``` + +## Quick Deploy Example + +Complete deployment with all values filled in: + +```bash +# 1. Set your values +STORAGE_CLASS="ceph-filesystem" +GITHUB_REPO="https://github.com/myuser/myproject" +GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +VNC_PASSWORD="my-secure-password-123" +GATEWAY_NAME="cilium-gateway" +GATEWAY_NAMESPACE="kube-system" +DOMAIN="devcontainer.example.com" + +# 2. Update storage class +sed -i "s/storageClassName: .*/storageClassName: \"$STORAGE_CLASS\"/" k8s/statefulset.yaml + +# 3. Update GitHub repo +sed -i "s|github-repo: .*|github-repo: \"$GITHUB_REPO\"|" k8s/configmap.yaml + +# 4. Update Gateway +sed -i "s/- name: gateway/- name: $GATEWAY_NAME/" k8s/httproute.yaml +sed -i "s/namespace: gateway-system/namespace: $GATEWAY_NAMESPACE/" k8s/httproute.yaml +sed -i "s/antigravity.example.com/$DOMAIN/" k8s/httproute.yaml + +# 5. Create sealed secret +kubectl create secret generic antigravity-secrets \ + --from-literal=github-token="$GITHUB_TOKEN" \ + --from-literal=vnc-password="$VNC_PASSWORD" \ + --dry-run=client -o yaml | \ + kubeseal --format=yaml > k8s/sealedsecrets.yaml + +# 6. Deploy +kubectl apply -k k8s/ + +# 7. Watch deployment +kubectl get pods -l app=antigravity -w +``` + +## Updates and Maintenance + +### Updating the Image + +The image is automatically built and pushed to ghcr.io on every commit to main. + +**To use latest:** +```bash +kubectl set image statefulset/antigravity \ + antigravity=ghcr.io/cpfarhood/devcontainer:latest +``` + +**To use specific version:** +```bash +kubectl set image statefulset/antigravity \ + antigravity=ghcr.io/cpfarhood/devcontainer:v1.0.0 +``` + +### Changing Repository + +Edit the ConfigMap and restart: +```bash +kubectl edit configmap antigravity-config +# Change github-repo value +kubectl rollout restart statefulset/antigravity +``` + +### Scaling + +```bash +# Scale to multiple instances (each gets own home PVC) +kubectl scale statefulset antigravity --replicas=3 +``` + +## Support + +For issues or questions: +- GitHub Issues: https://github.com/cpfarhood/devcontainer/issues +- Documentation: https://github.com/cpfarhood/devcontainer diff --git a/README.md b/README.md index cee1c87..37c5cfe 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,16 @@ A containerized development environment with GUI access, featuring: - Workspace mounted at `/workspace` - Repository cloned on first startup +## Documentation + +- **[DEPLOYMENT.md](DEPLOYMENT.md)** - Complete deployment guide with step-by-step instructions +- **[VARIABLES.md](VARIABLES.md)** - Reference for all configuration variables +- **[README.md](README.md)** - This file (overview and quick start) + ## Quick Start +**👉 For detailed deployment instructions, see [DEPLOYMENT.md](DEPLOYMENT.md)** + ### 1. Get the Image The image is automatically built and published to GitHub Container Registry on every push to main. diff --git a/VARIABLES.md b/VARIABLES.md new file mode 100644 index 0000000..554d4b6 --- /dev/null +++ b/VARIABLES.md @@ -0,0 +1,305 @@ +# Configuration Variables Reference + +Quick reference for all configurable variables in this project. + +## Required Variables + +These MUST be configured before deployment: + +### Storage Class Name +- **Variable:** `storageClassName` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~117 +- **Type:** String +- **Description:** ReadWriteMany storage class available in your cluster +- **Example:** `ceph-filesystem`, `nfs-client`, `efs-sc` +- **How to find:** `kubectl get storageclass` + +### GitHub Repository URL +- **Variable:** `github-repo` +- **File:** `k8s/configmap.yaml` +- **Line:** ~9 +- **Type:** String (URL) +- **Description:** Repository to clone on container startup +- **Format:** `https://github.com/username/repository` +- **Example:** `https://github.com/cpfarhood/my-project` + +### Gateway Name +- **Variable:** `parentRefs[0].name` +- **File:** `k8s/httproute.yaml` +- **Line:** ~8 +- **Type:** String +- **Description:** Name of your Gateway resource +- **How to find:** `kubectl get gateway -A` + +### Gateway Namespace +- **Variable:** `parentRefs[0].namespace` +- **File:** `k8s/httproute.yaml` +- **Line:** ~9 +- **Type:** String +- **Description:** Namespace where Gateway is deployed +- **How to find:** `kubectl get gateway -A` + +### Domain Hostname +- **Variable:** `hostnames[0]` +- **File:** `k8s/httproute.yaml` +- **Line:** ~11 +- **Type:** String (FQDN) +- **Description:** Domain name for accessing the container +- **Example:** `devcontainer.example.com` + +## Optional Variables + +### GitHub Token +- **Variable:** `github-token` +- **File:** Sealed Secret +- **Type:** String (GitHub PAT) +- **Description:** Personal Access Token for private repos +- **Required:** Only for private repositories +- **Format:** `ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx` +- **Scopes:** `repo` + +### VNC Password +- **Variable:** `vnc-password` +- **File:** Sealed Secret +- **Type:** String +- **Description:** Password for VNC web interface +- **Required:** Recommended for security +- **Format:** Any string (12+ characters recommended) + +### Namespace +- **Variable:** `namespace` +- **File:** `k8s/kustomization.yaml` +- **Line:** ~5 +- **Type:** String +- **Description:** Kubernetes namespace for deployment +- **Default:** `default` + +### Container Image +- **Variable:** `image` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~32 +- **Type:** String (image reference) +- **Description:** Docker image to deploy +- **Default:** `ghcr.io/cpfarhood/devcontainer:latest` +- **Format:** `registry/repository:tag` + +### Memory Request +- **Variable:** `resources.requests.memory` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~99 +- **Type:** String (quantity) +- **Description:** Minimum memory to reserve +- **Default:** `2Gi` +- **Format:** `Gi` or `Mi` + +### Memory Limit +- **Variable:** `resources.limits.memory` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~102 +- **Type:** String (quantity) +- **Description:** Maximum memory allowed +- **Default:** `8Gi` +- **Format:** `Gi` or `Mi` + +### CPU Request +- **Variable:** `resources.requests.cpu` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~100 +- **Type:** String (quantity) +- **Description:** Minimum CPU to reserve +- **Default:** `1000m` (1 core) +- **Format:** `m` (millicores) or `` (cores) + +### CPU Limit +- **Variable:** `resources.limits.cpu` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~103 +- **Type:** String (quantity) +- **Description:** Maximum CPU allowed +- **Default:** `4000m` (4 cores) +- **Format:** `m` (millicores) or `` (cores) + +### Storage Size +- **Variable:** `storage` (under volumeClaimTemplates) +- **File:** `k8s/statefulset.yaml` +- **Line:** ~120 +- **Type:** String (quantity) +- **Description:** Size of home directory PVC +- **Default:** `10Gi` +- **Format:** `Gi` or `Ti` + +### Happy Server URL +- **Variable:** `happy-server-url` +- **File:** `k8s/configmap.yaml` +- **Line:** ~12 (commented) +- **Type:** String (URL) +- **Description:** Custom Happy Coder server +- **Default:** `https://api.cluster-fluster.com` +- **When to set:** Self-hosted Happy instance only + +### Happy Webapp URL +- **Variable:** `happy-webapp-url` +- **File:** `k8s/configmap.yaml` +- **Line:** ~13 (commented) +- **Type:** String (URL) +- **Description:** Custom Happy Coder webapp +- **Default:** `https://app.happy.engineering` +- **When to set:** Self-hosted Happy instance only + +### Display Width +- **Variable:** `DISPLAY_WIDTH` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~56 +- **Type:** String (number) +- **Description:** VNC display width in pixels +- **Default:** `1920` + +### Display Height +- **Variable:** `DISPLAY_HEIGHT` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~58 +- **Type:** String (number) +- **Description:** VNC display height in pixels +- **Default:** `1080` + +### User ID +- **Variable:** `USER_ID` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~51 +- **Type:** String (number) +- **Description:** UID for claude user +- **Default:** `1000` + +### Group ID +- **Variable:** `GROUP_ID` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~53 +- **Type:** String (number) +- **Description:** GID for claude user +- **Default:** `1000` + +### StatefulSet Replicas +- **Variable:** `replicas` +- **File:** `k8s/statefulset.yaml` +- **Line:** ~21 +- **Type:** Integer +- **Description:** Number of container instances +- **Default:** `1` +- **Note:** Each replica gets own home PVC + +## Environment Variables (Runtime) + +These are set at runtime, not in configuration files: + +### GITHUB_REPO +- **Type:** String (URL) +- **Description:** Repository URL (from ConfigMap) +- **Required:** Yes +- **Source:** ConfigMap `antigravity.github-repo` + +### GITHUB_TOKEN +- **Type:** String +- **Description:** GitHub PAT (from Secret) +- **Required:** No (only for private repos) +- **Source:** Secret `antigravity.github-token` + +### VNC_PASSWORD +- **Type:** String +- **Description:** VNC password (from Secret) +- **Required:** No +- **Source:** Secret `antigravity.vnc-password` + +### HAPPY_SERVER_URL +- **Type:** String (URL) +- **Description:** Happy server URL (from ConfigMap) +- **Required:** No +- **Source:** ConfigMap `antigravity.happy-server-url` + +### HAPPY_WEBAPP_URL +- **Type:** String (URL) +- **Description:** Happy webapp URL (from ConfigMap) +- **Required:** No +- **Source:** ConfigMap `antigravity.happy-webapp-url` + +### HAPPY_HOME_DIR +- **Type:** String (path) +- **Description:** Happy data directory +- **Required:** No +- **Default:** `/home/claude/.happy` +- **Source:** Hardcoded in StatefulSet + +### HAPPY_EXPERIMENTAL +- **Type:** String (boolean) +- **Description:** Enable Happy experimental features +- **Required:** No +- **Default:** `true` +- **Source:** Hardcoded in StatefulSet + +## Variable Groups by Use Case + +### Minimal Deployment +Only these variables are required for basic deployment: +1. `storageClassName` +2. `github-repo` +3. `parentRefs.name` +4. `parentRefs.namespace` +5. `hostnames` + +### Private Repository Deployment +Add these for private repos: +1. All minimal deployment variables +2. `github-token` (sealed secret) + +### Production Deployment +Recommended for production: +1. All private repository variables +2. `vnc-password` (sealed secret) +3. `resources.requests.*` (adjusted for workload) +4. `resources.limits.*` (adjusted for workload) +5. `namespace` (dedicated namespace) + +### Multi-User Deployment +For multiple users: +1. All production deployment variables +2. `replicas` (set to number of users) +3. Larger `storage` size for home PVCs + +## Quick Copy Templates + +### Minimal Required Variables +```yaml +# k8s/statefulset.yaml +storageClassName: "CHANGE_ME" # Line ~117 + +# k8s/configmap.yaml +github-repo: "CHANGE_ME" # Line ~9 + +# k8s/httproute.yaml +parentRefs: +- name: CHANGE_ME # Line ~8 + namespace: CHANGE_ME # Line ~9 +hostnames: +- "CHANGE_ME" # Line ~11 +``` + +### With Secrets +```bash +kubectl create secret generic antigravity-secrets \ + --from-literal=github-token='CHANGE_ME' \ + --from-literal=vnc-password='CHANGE_ME' \ + --dry-run=client -o yaml | \ + kubeseal --format=yaml > k8s/sealedsecrets.yaml +``` + +### With Resource Adjustments +```yaml +# k8s/statefulset.yaml (lines ~98-103) +resources: + requests: + memory: "CHANGE_ME" # e.g., 4Gi + cpu: "CHANGE_ME" # e.g., 2000m + limits: + memory: "CHANGE_ME" # e.g., 16Gi + cpu: "CHANGE_ME" # e.g., 8000m +```