# Phase 2: Namespace-Scoped Auto-Provisioning Testing Guide ## Overview Phase 2 implements **namespace-scoped automatic TemporalWorker provisioning** (Kafka broker model). One TemporalWorker per Temporal namespace processes ALL task queues in that namespace. When multiple Queues share the same `temporal.io/namespace` label, they trigger creation of a single TemporalWorker that handles all of them. ``` Queues (labeled temporal.io/namespace: "production") ├── orders-fifo ├── payments └── notifications ↓ queue-operator creates 1 TemporalWorker (worker-production) ↓ TemporalWorker controller creates 1 Deployment ↓ Worker pod(s) connect to Temporal namespace "production" ↓ Process ALL task queues in that namespace (scale horizontally by replicas) ``` ## Implementation Changes ### 1. TemporalWorker CRD (`apis/kmsvc/v1/temporalworker_types.go`) - New Kubernetes resource type to manage namespace-scoped workers - Fields: Namespace (required), Image, Replicas, Resources, NodeSelector, Affinity, Tolerations - Status: Phase (Pending/Ready/Failed), Replicas, ReadyReplicas, Conditions - Model: 1 TemporalWorker per Temporal namespace (not per queue) ### 2. QueueReconciler Extension (`internal/operator/queue_controller.go`) - New method: `reconcileTemporalWorker()` - Logic: If Queue has `temporal.io/namespace` label, create TemporalWorker for that namespace - Idempotent: multiple queues with same namespace label create same TemporalWorker (no duplicates) ### 3. TemporalWorkerReconciler (`internal/operator/temporal_worker_controller.go`) - New controller watching TemporalWorker objects - Creates/updates Kubernetes Deployment with: - Pod spec: container image, env vars (TEMPORAL_FRONTEND_ADDRESS, TEMPORAL_TASK_QUEUE) - Replicas, resources, node selector, affinity, tolerations from TemporalWorker spec - Updates TemporalWorker status with deployment replica counts and phase ### 4. Operator Main (`cmd/queue-operator/main.go`) - Registers TemporalWorker CRD in scheme - Registers TemporalWorkerReconciler controller - Controller watches TemporalWorker objects; owns Deployment objects ## Testing Procedure ### Prerequisites - kmsvc queue-operator must be running (built and deployed) - Temporal cluster must be ready (temporal-frontend service available at `temporal-frontend.temporal.svc.cluster.local:7233`) - story-crater-backend Docker image must exist (used as default worker image) ### Step 1: Build and Deploy kmsvc Operator ```bash cd /Users/rockliang/workplace/kmsvc-manage make build # builds queue-operator binary make docker-build # builds Docker image make deploy # deploys to cluster (requires Helm chart) ``` Or manually: ```bash cd /Users/rockliang/workplace/kmsvc-manage go build -o bin/queue-operator ./cmd/queue-operator kubectl apply -f k8s/queue-operator-rbac.yaml kubectl apply -f k8s/queue-operator-deployment.yaml ``` ### Step 2: Create Queues with Temporal Namespace Labels ```bash kubectl apply -f /Users/rockliang/workplace/homelab/k8s/temporal/queues/example-queue.yaml ``` Verify Queues are Ready: ```bash kubectl get queue -n sqs -l temporal.io/namespace=production kubectl describe queue -n sqs story-crater-tasks ``` Expected: ``` NAME FIFO PHASE AGE story-crater-tasks false Ready 5s story-crater-notifications false Ready 5s ``` ### Step 3: Verify TemporalWorker CRD Auto-Created (1 per namespace) ```bash kubectl get temporalworker -n temporal kubectl describe temporalworker -n temporal worker-production ``` Expected: ``` NAME PHASE READY DESIRED AGE worker-production Pending 0 1 5s ``` Only ONE TemporalWorker for all queues in "production" namespace! ### Step 4: Verify Deployment Auto-Created ```bash kubectl get deploy -n temporal -l app.kubernetes.io/managed-by=kmsvc-temporal-operator kubectl get pods -n temporal -l app.kubernetes.io/instance=worker-production ``` Expected: ``` NAME READY UP-TO-DATE AVAILABLE AGE worker-production 1/1 1 1 10s NAME READY STATUS RESTARTS AGE worker-production-5f8b4c... 1/1 Running 0 10s ``` ### Step 5: Verify Worker Connected to Temporal Namespace Check Temporal UI for namespace "production": ```bash open https://temporal.riotpiao.com/namespaces/production/task-queues ``` Look for all task queues with worker count > 0: - `story-crater-tasks` - `story-crater-notifications` - (worker processes all of them) Or via CLI: ```bash kubectl port-forward -n temporal svc/temporal-frontend 7233 & curl http://localhost:7233/api/v1/task-queues?namespace=production ``` ### Step 6: Verify TemporalWorker Status Updated ```bash kubectl get temporalworker -n temporal kubectl describe temporalworker -n temporal worker-production ``` Expected: ``` NAME PHASE READY DESIRED AGE worker-production Ready 1 1 15s Status: Phase: Ready Ready Replicas: 1 Replicas: 1 ``` ### Step 7: Test Namespace-Level Scaling Create more queues in the same namespace: ```yaml apiVersion: kmsvc.io/v1 kind: Queue metadata: name: story-crater-llm-processing namespace: sqs labels: temporal.io/namespace: "production" # same namespace ``` Verify: No new TemporalWorker created (same worker handles all 3 queues): ```bash kubectl get temporalworker -n temporal # still just 1 worker-production kubectl get deploy -n temporal worker-production # same deployment ``` Worker auto-discovers new task queue in namespace and processes it. ### Step 8: Test Cascading Deletion Delete a Queue; worker should remain (other queues still need it): ```bash kubectl delete queue -n sqs story-crater-notifications ``` Verify: ```bash kubectl get temporalworker -n temporal # worker-production still exists kubectl get pods -n temporal worker-production # still running ``` Delete all queues in namespace: ```bash kubectl delete queue -n sqs -l temporal.io/namespace=production ``` Verify: TemporalWorker now has no owner (not cascade-deleted; manual cleanup needed): ```bash kubectl get temporalworker -n temporal # worker-production still there (manual cleanup) kubectl delete temporalworker -n temporal worker-production # cleanup manually ``` ## Debugging ### Queue stuck in Pending Check queue-operator logs: ```bash kubectl logs -n sqs deploy/kmsvc-queue-operator -f kubectl logs -n sqs deploy/kmsvc-queue-operator --tail=50 | grep -i error ``` ### TemporalWorker not created - Verify Queue has the label: `kubectl get queue -o yaml | grep temporal.io` - Check queue-operator logs for "reconcileTemporalWorker" errors ### Deployment not created - Check TemporalWorker controller logs: `kubectl logs -n sqs deploy/kmsvc-queue-operator -f` - Verify TemporalWorker exists: `kubectl get temporalworker -n temporal` - Check Deployment errors: `kubectl describe deploy -n temporal worker-story-crater-tasks` ### Worker not showing in Temporal UI - Check pod logs: `kubectl logs -n temporal deploy/worker-story-crater-tasks` - Verify env vars: `kubectl set env pod -n temporal --list | grep TEMPORAL` - Test connectivity: `kubectl exec -n temporal -- nc -zv temporal-frontend.temporal.svc.cluster.local 7233` ## Next Steps Once Phase 2 is working: 1. **Phase 3 (Future):** Implement autoscaling based on queue depth metrics 2. **Production Hardening:** - Add QueueRef validation (ensure Queue exists in sqs namespace) - Add image validation/defaults from ConfigMap - Add worker readiness probe configuration - Add graceful shutdown/drain behavior ## Files Modified/Created | File | Change | |------|--------| | `apis/kmsvc/v1/temporalworker_types.go` | NEW: CRD type definitions | | `internal/operator/queue_controller.go` | MODIFIED: Added reconcileTemporalWorker() | | `internal/operator/temporal_worker_controller.go` | NEW: TemporalWorker → Deployment reconciler | | `cmd/queue-operator/main.go` | MODIFIED: Register TemporalWorker CRD + controller | | `k8s/temporal/queues/example-queue.yaml` | NEW: Example Queue with label |