Skip to content

feat(DIARCHERS-1674): add GKE cluster provisioning REST API (v0) - #637

Open
Jiachen0715 wants to merge 2 commits into
masterfrom
feat/DIARCHERS-1674-gke-provisioning-api
Open

feat(DIARCHERS-1674): add GKE cluster provisioning REST API (v0)#637
Jiachen0715 wants to merge 2 commits into
masterfrom
feat/DIARCHERS-1674-gke-provisioning-api

Conversation

@Jiachen0715

Copy link
Copy Markdown
Contributor

Background

The dhaas project has migrated from InfraBox to GitHub Actions (PR #4934), but 5 integration test jobs still depend on InfraBox's native services: [{kind: GKECluster}] mechanism — the platform's GCP operator dynamically creates/destroys ephemeral GKE clusters for each job. GHA has no equivalent, and the interim workaround (reimplementing gcloud in shell, ~130 lines) has three problems:

  • Requires exposing a GCP Service Account key as a GitHub secret
  • Borrows an inappropriate default compute SA
  • No audit, no rate limiting, no project isolation

Decision: Since InfraBox remains a long-term platform service, wrap a thin GKE provisioning REST API on the InfraBox backend. GHA (and other external CI) only calls the API and never touches GCP credentials directly.

Approach

The API does NOT reimplement gcloud — it is a thin orchestration layer that manipulates GKECluster CRs in Kubernetes and reads the kubeconfig Secret produced by the operator. All actual cluster provisioning logic is fully reused from the existing GCP operator (src/services/gcp, unchanged).

GHA runner ──① Call API (user JWT) ──▶ InfraBox API ──② Manage GKECluster CR ──▶ K8s API
                                                                                    │
                                                                                    ▼
                                                                            GCP operator (unchanged)
                                                                                    │
                                                                                    ▼
                                                                                GCP GKE

This PR is v0: prove end-to-end feasibility on test-new using user JWT auth. v1 will add list / TTL GC / MCP-token or OIDC auth / audit / rate limiting / production deployment.

API Endpoints

All endpoints under /api/v1/projects/<project_id>/gke-clusters.

Method Path Purpose Response
POST / Create a cluster (async) 202 {name, status:"pending"}
GET /<name> Get cluster status 200 {name, status, message, clusterName}
GET /<name>/kubeconfig Fetch kubeconfig (only when ready) 200 YAML / 409 not ready
DELETE /<name> Delete cluster (async via operator finalizer) 202 {name, status:"deleting"}

Authentication & Authorization

  • v0 auth: user JWT (existing global OPA + g.token mechanism)
  • Permission: all endpoints require token.type=user AND the user must be a project collaborator
  • Cross-project guard: GET/DELETE/kubeconfig all compare the CR's infrabox.net/project-id label with the URL's project_id; mismatch → 403
  • v1 will switch to: MCP token (short term) or GHA OIDC (long term)

Files Changed

File Status Notes
src/api/handlers/projects/gke_clusters.py New 4-endpoint handler
src/api/handlers/projects/__init__.py Modified Register new handler
src/openpolicyagent/policies/projects_gke_clusters.rego New 4 OPA allow rules (default-deny → explicit allow)
deploy/infrabox/templates/api/deployment.yaml Modified API pod runs under the infrabox ServiceAccount + injects INFRABOX_KUBERNETES_MASTER_HOST/PORT

Key Design Decisions

  1. No kubernetes python client dependency — the handler uses requests to talk to the K8s API directly, matching the exact pattern of scheduler.py (reads /var/run/secrets/kubernetes.io/serviceaccount/token + ca.crt). This avoids adding a new binary dependency.
  2. CR namespace via env var INFRABOX_GENERAL_WORKER_NAMESPACE — identical to what scheduler uses, guaranteeing the operator watches and picks up the CR.
  3. RBAC reuses the infrabox ServiceAccount (cluster-admin, same as scheduler) — simplified for v0; v1 can tighten to gkeclusters CRUD + secrets read only.
  4. CR label triad + audit stub:
    • service.infrabox.net/secret-name (operator uses this to name the output Secret)
    • infrabox.net/created-by: api (distinguishes from scheduler-created CRs, useful for GC scanning)
    • infrabox.net/project-id (ownership check)
    • infrabox.net/created-by-user (audit stub)

Known Limitations (explicitly out-of-scope for v0)

  • No GET /gke-clusters (list)
  • No TTL / operator GC (leaked clusters must be swept manually with kubectl get gkeclusters -l infrabox.net/created-by=api)
  • No audit log persistence
  • No rate limiting
  • Only user JWT auth supported (dhaas team will need MCP token / OIDC for production use)
  • Deployed to test-new only, not production

Testing

Planned end-to-end validation after deploying to test-new (using the mcp-e2e project + a user JWT):

B=https://test-new.infrabox.datahub.only.sap
# 1) Create
NAME=$(curl -sf -X POST "$B/api/v1/projects/$PID/gke-clusters" \
  -H "Authorization: bearer $JWT" -H "Content-Type: application/json" \
  -d '{"zone":"us-east1-b","numNodes":1,"machineType":"n1-standard-1","preemptible":true}' | jq -r .name)
# 2) Poll until ready (3-8 min)
until [ "$(curl -sf "$B/api/v1/projects/$PID/gke-clusters/$NAME" -H "Authorization: bearer $JWT" | jq -r .status)" = "ready" ]; do sleep 20; done
# 3) Fetch kubeconfig, actually use it with kubectl
curl -sf "$B/api/v1/projects/$PID/gke-clusters/$NAME/kubeconfig" -H "Authorization: bearer $JWT" > /tmp/kubeconfig
KUBECONFIG=/tmp/kubeconfig kubectl get nodes
# 4) Delete
curl -sf -X DELETE "$B/api/v1/projects/$PID/gke-clusters/$NAME" -H "Authorization: bearer $JWT"

Passing kubectl get nodes against the API-provisioned cluster proves the end-to-end feasibility target.

Related

  • JIRA: DIARCHERS-1674
  • Related migration: dhaas PR #4934 (GHA migration) — will switch to this API after v1

Jiachen Fan and others added 2 commits August 21, 2026 15:02
Introduces a thin REST API to let external CI consumers (e.g. GitHub Actions)
dynamically provision ephemeral GKE clusters via InfraBox, without exposing
GCP credentials.

The API is a thin orchestration layer on top of the existing GCP operator:
POST/GET/DELETE against GKECluster CRs in the worker namespace, plus a
GET /kubeconfig endpoint that reads the operator-generated Secret.

Endpoints (v0):
  POST   /api/v1/projects/<pid>/gke-clusters
  GET    /api/v1/projects/<pid>/gke-clusters/<name>
  GET    /api/v1/projects/<pid>/gke-clusters/<name>/kubeconfig
  DELETE /api/v1/projects/<pid>/gke-clusters/<name>

Changes:
- src/api/handlers/projects/gke_clusters.py: new handler (uses requests +
  the pod's SA token/CA, same pattern as scheduler.py to avoid adding the
  kubernetes python client dep)
- src/api/handlers/projects/__init__.py: register new handler
- src/openpolicyagent/policies/projects_gke_clusters.rego: OPA allow rules
  for the 4 endpoints (default deny -> explicit allow)
- deploy/infrabox/templates/api/deployment.yaml: run the API pod under the
  existing 'infrabox' ServiceAccount (cluster-admin, same as scheduler),
  and inject INFRABOX_KUBERNETES_MASTER_HOST/PORT so the handler can reach
  the in-cluster K8s API server
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.

1 participant