# 8.1 — `ServiceAdapter` CRD, informer, RBAC (GREEN) Phase: 8 — ServiceAdapter CRD rollout Stage: RED Depends on: Phase 6 (cutover) — done, confirmed live in-cluster Design contract: [API_ROUTING_HYBRID_DESIGN.md](../API_ROUTING_HYBRID_DESIGN.md) §1. **G2 note — read before objecting.** G2 says "the gateway holds no Kubernetes credentials, config comes from git not a CRD." This task deliberately supersedes G2 for this one narrow purpose; the acknowledged-supersession rationale is written out in API_ROUTING_HYBRID_DESIGN.md's Context section (top of that doc) and in `INDEX.md`'s G2 line. Do not treat this as a task-file error — the supersession is intentional and pre-approved, scoped to exactly the read-only Role this task adds. - [ ] `apis/gateway/v1/serviceadapter_types.go` defines `ServiceAdapter` matching the design doc's example CRs (§1, §6): `spec.serviceName`, `spec.upstream.{url,timeoutSeconds}`, `spec.auth.{required,capability}`, `spec.retryable`, `spec.resources[].name`, `spec.resources[].methods[].{verb,upstreamPath,requestSchema,responseSchema,auth}` - [ ] `controller-gen` generates the CRD YAML from those types into `k8s/crd-serviceadapter.yaml`, added to `k8s/kustomization.yaml`'s `resources:` - [ ] CRD is namespace-scoped, group `gateway.riotpiao.com/v1`, kind `ServiceAdapter` — not cluster-scoped - [ ] `k8s/rbac.yaml` gains a namespace-scoped `Role`/`RoleBinding` (`get`, `list`, `watch` only, no `status`/`finalizers` verbs) for the existing `api-gateway` ServiceAccount, exactly as specced in §1 - [ ] `internal/serviceadapter/registry.go`: `client-go` `SharedInformer` on `ServiceAdapter` in namespace `api`, feeding an in-memory map keyed by `spec.serviceName` - [ ] Add/Update/Delete informer callbacks mutate the map directly; no gateway restart required to pick up a CR change - [ ] A `ServiceAdapter` CR with a malformed `requestSchema`/`responseSchema` (per 8.3's DSL) logs an error and is skipped — it does not crash the informer or block other adapters from loading - [ ] `go build ./...` succeeds with the new `apis/` package and `internal/serviceadapter/registry.go` in the tree ## Verify ```bash kubectl -n api get role api-gateway-serviceadapter-reader -o yaml # expected: rules limited to gateway.riotpiao.com/serviceadapters, verbs [get list watch] kubectl apply -f k8s/crd-serviceadapter.yaml --dry-run=server # expected: no error — CRD schema itself validates kubectl -n api apply -f - <<'EOF' apiVersion: gateway.riotpiao.com/v1 kind: ServiceAdapter metadata: { name: smoke-test } spec: serviceName: smoke-test upstream: { url: http://example.invalid, timeoutSeconds: 5 } auth: { required: false } resources: [] EOF kubectl -n api logs deploy/api-gateway --since=10s | grep -i "smoke-test" # expected: informer logs an Add event for smoke-test within resync/watch latency, no restart kubectl -n api delete serviceadapter smoke-test ```