Skip to content

Latest commit

 

History

History
392 lines (278 loc) · 8.38 KB

File metadata and controls

392 lines (278 loc) · 8.38 KB

Deployment Guide

Three ways to run devctl depending on your situation:

Option When to use Requires
A — Full Kubernetes Primary setup, closest to production Docker, Terraform, kubectl
B — Docker Compose Test API against an existing cluster Docker, existing kubeconfig
C — Local dev Backend hot-reload during development Go 1.23+, existing cluster

Prerequisites

Install these before anything else:

# Verify each is available
terraform version   # need 1.5+
kubectl version     # need 1.28+
docker version      # need 20+
go version          # need 1.23+  (Option C only)
Tool Install
Terraform brew install terraform
kubectl brew install kubectl
Docker Desktop https://docs.docker.com/get-docker/
Go brew install go
kind Installed automatically by Terraform

Option A — Full Kubernetes (Recommended)

This is the full setup. Terraform provisions a Kind cluster, ingress-nginx is installed via Helm, and the devctl backend runs as a pod inside the cluster.

Step 1 — Clone the repo

git clone https://github.com/abhaychaurasiya/dev-env-platform.git
cd dev-env-platform

Step 2 — Bootstrap the cluster

./scripts/setup.sh

This runs 5 steps automatically:

==> [1/5] Checking prerequisites...
==> [2/5] Provisioning Kind cluster via Terraform...
==> [3/5] Waiting for ingress-nginx to be ready...
==> [4/5] Deploying devctl backend...
==> [5/5] Setting up port-forward for local access...

What it does under the hood:

  • terraform init && terraform apply — creates a Kind cluster with ports 80/443 mapped to localhost
  • Installs ingress-nginx via Helm chart v4.9.1 into the cluster
  • Applies k8s-manifests/backend/ — RBAC, ServiceAccount, Deployment, Service
  • Applies k8s-manifests/system/ — the TTL cleanup CronJob

Expected output at the end:

==> Setup complete!
    Next steps:
    1. Install CLI:  ./scripts/install-devctl.sh
    2. Spin up env:  devctl up --template=mern

Step 3 — Install the CLI

./scripts/install-devctl.sh

devctl version
# devctl v0.1.0

This builds the Go binary and copies it to /usr/local/bin/devctl.

Step 4 — Forward the backend port

In a separate terminal, keep this running:

kubectl port-forward -n devctl-system svc/devctl-backend 8080:8080

You can verify it's working:

curl http://localhost:8080/healthz
# {"status":"ok"}

Step 5 — Create your first environment

devctl up --template=mern

The CLI will print the local URL when ready. Add it to /etc/hosts so your browser can resolve it:

echo "127.0.0.1  $(whoami)-mern.dev.local" | sudo tee -a /etc/hosts
open http://$(whoami)-mern.dev.local

Step 6 — Check status

devctl status

# Environment: dev-abhay
# Phase:       Ready
# URL:         http://abhay-mern.dev.local
# Expires:     2024-01-15T11:00:00Z (in 28m)
#
# Services:
#   ✓ mongodb             Running
#   ✓ express             Running
#   ✓ react               Running

Watch live (refreshes every 5s):

devctl status --watch

Step 7 — Tear down

Destroy just one environment:

devctl down

Destroy everything — all environments, system resources, and the Kind cluster:

./scripts/teardown.sh

Option B — Docker Compose

Runs the backend in a container using your existing ~/.kube/config. The backend talks to whatever cluster your kubeconfig points to.

Step 1 — Start the backend

docker compose up -d

The backend mounts ~/.kube/config read-only. If your kubeconfig is in a non-standard location, set KUBECONFIG before running:

KUBECONFIG=/path/to/config docker compose up -d

Step 2 — Verify it's healthy

curl http://localhost:8080/healthz
# {"status":"ok"}

curl http://localhost:8080/api/v1/templates
# [{"name":"go-api",...},{"name":"mern",...},{"name":"react",...}]

Step 3 — Use the CLI against it

export DEVCTL_BACKEND_URL=http://localhost:8080
devctl up --template=react
devctl status
devctl down

Or run the CLI as a one-shot container (no local Go install needed):

docker compose --profile cli run cli up --template=react
docker compose --profile cli run cli status
docker compose --profile cli run cli down

Step 4 — Stop

docker compose down

Option C — Local Development (Hot-reload)

Run the backend directly on your machine. Best when you're actively changing backend code.

Step 1 — Start the backend

./scripts/dev.sh

This runs go mod tidy && go run . in backend/. The backend uses your current kubeconfig automatically.

devctl backend listening on :8080

Step 2 — Use the CLI

In another terminal:

export DEVCTL_BACKEND_URL=http://localhost:8080
devctl up --template=go-api

Step 3 — Run tests

# Backend unit tests (no cluster needed)
cd backend && go test -race ./...

# CLI unit tests
cd cli && go test -race ./...

# Full E2E integration test (requires running cluster + backend on :8080)
DEVCTL_BACKEND_URL=http://localhost:8080 ./scripts/integration-test.sh

Authentication (Optional)

By default the backend accepts all requests. To enable token auth:

Backend — set the token secret in the Deployment:

# k8s-manifests/backend/deployment.yaml
env:
  - name: DEVCTL_TOKEN
    valueFrom:
      secretKeyRef:
        name: devctl-token
        key: token
kubectl create secret generic devctl-token \
  --from-literal=token=mysecrettoken \
  -n devctl-system

CLI — pass the token:

devctl up --template=mern --token=mysecrettoken
# or
export DEVCTL_TOKEN=mysecrettoken
devctl up --template=mern

Customising TTL

Environments auto-delete after their TTL expires. A CronJob checks every 5 minutes.

devctl up --template=mern                  # default: 30 minutes
devctl up --template=mern --timeout=1h     # 1 hour
devctl up --template=mern --timeout=4h     # 4 hours

# Manual teardown before TTL
devctl down

Multi-user Setup

Each user gets an isolated namespace. Multiple developers can use the same cluster simultaneously:

# Alice
devctl up --template=mern --username=alice
# → namespace: dev-alice, URL: http://alice-mern.dev.local

# Bob
devctl up --template=react --username=bob
# → namespace: dev-bob, URL: http://bob-react.dev.local

Each namespace has its own ResourceQuota preventing one user from exhausting cluster resources.


Troubleshooting

Backend pod not starting:

kubectl get pods -n devctl-system
kubectl describe pod -n devctl-system -l app=devctl-backend
kubectl logs -n devctl-system -l app=devctl-backend

Environment stuck in Provisioning:

# Check pod events in the user namespace
kubectl get pods -n dev-<username>
kubectl describe pod -n dev-<username> <pod-name>

# Or use the CLI
devctl status --name=<username>-<template>

ingress-nginx not ready after terraform apply:

kubectl get pods -n ingress-nginx
kubectl wait --namespace ingress-nginx \
  --for=condition=ready pod \
  --selector=app.kubernetes.io/component=controller \
  --timeout=120s

Port-forward dropped:

# Restart port-forward
kubectl port-forward -n devctl-system svc/devctl-backend 8080:8080

Terraform state issues:

cd terraform
terraform refresh
terraform plan   # review before applying

Full reset:

./scripts/teardown.sh    # deletes everything
./scripts/setup.sh       # start fresh

CI/CD Pipeline

CI — runs on every push and PR

lint-test-cli        go vet + golangci-lint + go test -race  (cli/)
lint-test-backend    go vet + golangci-lint + go test -race  (backend/)
validate-manifests   kubeval on all k8s-manifests/*.yaml
build-cli            cross-compile: linux/amd64, darwin/arm64
build-backend-image  docker build (no push in CI)

Release — runs on git tag v*

git tag v1.0.0
git push origin v1.0.0

This triggers:

  • GoReleaser — builds CLI binaries for Linux/macOS/Windows (amd64 + arm64), creates GitHub Release with archives and checksums
  • Docker — builds and pushes ghcr.io/abhaychaurasiya/devctl-backend with semver tags + SHA digest
# Pull the released backend image
docker pull ghcr.io/abhaychaurasiya/devctl-backend:v1.0.0