Skip to content

ADR 010 — Ownership de l'exposition : plateforme vs application (2026-05-30)

Contexte

Issue #60. Jusqu'ici, l'exposition publique (GatewayClass, Gateway, HTTPRoute, Certificate) vivait dans l'overlay Kustomize k8s/overlays/gateway-api/. Or le pipeline applicatif ne déploie que kubectl apply -k k8s/base/. Conséquence : ces ressources n'étaient jamais déployées par la CI. Elles étaient appliquées à la main (depuis #28), donc chaque nouveau cluster éphémère naissait sans exposition, et api.devopsyouss.com restait down jusqu'à intervention manuelle.

Faire pointer le deploy CI (least-privilege, edit scopé au ns fastapi) sur l'overlay gateway-api n'est pas viable : - GatewayClass est cluster-scoped → le user CI ne peut pas la créer (même classe qu'INC-045 sur le Namespace). - Gateway, HTTPRoute (gateway.networking.k8s.io) et Certificate (cert-manager.io) sont des CRDs non couvertes par le ClusterRole edit → 403 (même classe qu'INC-047/048).

Lui donner ces droits impliquerait un ClusterRoleBinding cluster-wide sur une GatewayClass, ce qui casse le least-privilege.


Décision 1 — Le découpage suit le modèle role-oriented de Gateway API

Gateway API sépare explicitement les responsabilités infra et application. On adopte ce découpage :

Ressource Propriétaire Posée par
GatewayClass (cluster-scoped) Cluster operator (infra) bootstrap Ansible
Gateway (ELB, listeners, TLS) Cluster operator (infra) bootstrap Ansible
Certificate (lié au listener) Cluster operator (infra) bootstrap Ansible
HTTPRoute (routage app) Développeur d'app deploy CI (k8s/base)

Le Gateway est un point d'entrée stable, provisionné une fois par cycle de cluster. Le HTTPRoute évolue au rythme des releases (canary via backendRefs pondérés, nouveaux paths, header matching pour blueprints) et doit donc rester pilotable par la CI sans re-bootstrap.

Prolongement direct d'INC-045 : les ressources cluster-scoped et de plateforme sont ownées par le bootstrap cluster-admin, pas par le deploy least-privilege.


Décision 2 — Le HTTPRoute conserve son propre binding RBAC explicite

Puisque le HTTPRoute repart dans k8s/base/ (déployé par la CI), le user CI doit pouvoir agir sur les httproutes. Comme pour les CRDs ESO (INC-048), ce CRD n'est pas couvert par edit : le bootstrap pose un Role + RoleBinding fastapi-deploy-httproute (ns fastapi) bindant directement l'ARN du user CI.

On réapplique le pattern validé en #59 : binding RBAC explicite sur l'ARN, jamais d'agrégation (sans effet sur les users mappés via AWS Access Policy).


Décision 3 — Manifests versionnés, appliqués par le bootstrap via src:

Les manifests d'exposition restent des fichiers YAML (pas de YAML inline dans le playbook) : - k8s/platform/gateway.yaml (GatewayClass + Gateway) - k8s/platform/certificate.yaml

Le bootstrap les applique via kubernetes.core.k8s avec src:. L'overlay k8s/overlays/gateway-api/ est supprimé (les manifests d'exposition partent dans k8s/platform/, le HTTPRoute dans k8s/base/).


Décision 4 — Ordre de teardown pour éviter l'ELB orphelin

Le teardown supprime le Gateway avant l'uninstall d'Envoy : c'est le contrôleur Envoy, encore vivant, qui supprime l'ELB associé. Une attente de libération ELB + ENIs précède la suite du teardown. Sans cet ordre, l'ELB orphelin bloque terraform destroy (DependencyViolation sur les subnets, cf INC-016).


Conséquences

  • api.devopsyouss.com est exposé automatiquement à chaque montée d'infra, sans intervention manuelle (hors mise à jour du CNAME, suivie par le futur item ExternalDNS).
  • Le HTTPRoute est exclu de l'overlay local-kind ($patch: delete), kind n'ayant pas les CRDs Gateway API.
  • Point ouvert (quota TLS) : le Certificate utilise letsencrypt-prod. Le cluster étant éphémère, le secret fastapi-tls meurt à chaque aws-stop, donc cert-manager ré-émet à chaque montée → quota Let's Encrypt prod (5 certificats identiques/semaine). À mitiger plus tard (persister fastapi-tls, ou valider le flux en letsencrypt-staging avant de basculer prod).

Date : 2026-05-30 Sprint : 3 Issue : #60