Skip to content

refactor(cluster): migrate module from server - #2495

Open
shikanime wants to merge 22 commits into
mainfrom
pr/cluster-migration
Open

shikanime wants to merge 22 commits into
mainfrom
pr/cluster-migration

Conversation

@shikanime

@shikanime shikanime commented Aug 13, 2026 •

Copy link
Copy Markdown
Member

Issues liées

Refs #2492


Quel est le comportement actuel ?

Le module cluster (clusters, usage, environnements) est servi par l'ancienne application Fastify apps/server.

Quel est le nouveau comportement ?

Migration du module cluster vers apps/server-nestjs :

  • ClusterController : routes GET /, GET /:clusterId, GET /usage/:clusterId, GET /:clusterId/environments, POST /, PUT /:clusterId, DELETE /:clusterId.
  • ClusterService : CRUD + calcul d'usage et liste des environnements rattachés.
  • ClusterModule : enregistrement dans main.module.ts.
  • Parité des contrats et codes HTTP contre apps/server/src/resources/cluster/.

État lors de la bascule

Cette PR migre le module côté server-nestjs mais ne bascule pas encore le trafic : le routage reste porté par l'ancienne application, ce qui rend le point de routage non bloquant ici mais à traiter dans la vague de bascule.

  • Routage : apps/nginx-strangler/conf.d/routing.conf ne contient aucune location /api/v1/clusters sur cette branche ; /api/v1/clusters retombe donc dans location /api/ → server-legacy. Les routes migrées ne sont pas encore servies par server-nestjs.
  • Hooks : ClusterService émet cluster.upsert / cluster.delete via EventEmitter2, et server-nestjs porte les listeners correspondants (argocd.service.ts : @OnEvent('cluster.upsert') → syncCluster, @OnEvent('cluster.delete') → cleanupCluster, résultats argocd centralisés via capturePluginResult) ; l'écouteur historique reste dans l'ancienne application (apps/server/src/utils/hook-wrapper.ts, câblé par apps/server/src/resources/cluster/business.ts). À la bascule du routage, l'émission côté server-nestjs déclenchera donc le provisionnement — aucun risque de no-op ; la vague de bascule n'a plus que le bloc location /api/v1/clusters à porter.

Cette PR introduit-elle un breaking change ?

Non.

Autres informations

@shikanime

Copy link
Copy Markdown
Member Author

Réouverte : couverture des familles restantes pour #1889 — le fermeture précédente était un tri, pas un rejet du travail.

@shikanime
shikanime restored the pr/cluster-migration branch September 17, 2026 09:51
@shikanime

Copy link
Copy Markdown
Member Author

Réouverte : famille requise pour #1889. Branche recréée depuis le SHA d'origine (l'ancienne ref avait été supprimée).

@shikanime shikanime reopened this Sep 17, 2026
@shikanime shikanime self-assigned this Sep 17, 2026
@shikanime shikanime added this to the 9.27.0 milestone Sep 17, 2026
Comment thread apps/server-nestjs/src/modules/cluster/cluster.controller.ts
Comment thread apps/server-nestjs/src/modules/cluster/cluster.controller.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster-queries.utils.ts Outdated
Comment thread apps/server-nestjs/src/modules/cluster/cluster-testing.utils.ts Outdated
@shikanime

Copy link
Copy Markdown
Member Author

🔴 Bloquant pour la bascule : emitAsync('cluster.upsert'/'cluster.delete') n'a aucun consommateur @OnEvent dans server-nestjs. Les abonnés legacy du hook cluster sont argocd, gitlab et vault — le pont reste à écrire (même pattern que #2749 pour adminRole.*). Rien de cassé tant qu'apps/server sert la route.

@shikanime

Copy link
Copy Markdown
Member Author

Revue de conformité menée contre la base de code et le vocabulaire des conventions (cpn) : couche requêtes (sélectifs *Select sous satisfies Prisma.*Select, verbes list*/get*/upsert* Prisma-only, generateClusterWhereInput), pipeline d'événements (échec plugin → 422 centralisé dans AppEventsService.throwOnPluginFailure, hook avant mutation pour que la suppression reste rejouable) et specs de matrice de permissions suivent les conventions du dépôt. Un point de parité bloquant : la forme du secret ArgoCD déplace username/password/bearerToken hors du bloc tlsClientConfig par rapport au legacy (convertConfig dans plugins/argocd/src/cluster.ts), ce qui casserait l'authentification par token/login des clusters à la bascule ; trois points importants l'accompagnent (erreur brute dans le bridge argocd au lieu d'un KO plugin, message 422 création/mise à jour replié sur une seule formulation, mutation du body typé dans updateCluster). Pas d'approbation : retour au porteur après correction.

@shikanime shikanime left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revue du head 17a9b3b0 (rebase + restauration des throws locaux par le pattern zone). Le module cluster est propre : union EventLogAction bien complétée avec 'Create Cluster' | 'Update Cluster' | 'Delete Cluster', wrapper emitClusterEventAndThrowOnFailure qui reproduit le 422 legacy (le delete n'est émis qu'après nettoyage plugins, la ligne ne disparaît qu'après OK de tous les plugins), listeners @OnEvent('cluster.upsert') / @OnEvent('cluster.delete') portés dans argocd.service.ts (contrairement à ce qu'affirme encore le corps de la PR), aucun cast as, pas de specs supprimées, imports de module minimaux. Les interactions Vault ajoutées (upsertKvData / deleteKvMetadata pour le secret cluster ArgoCD) sont idempotentes et tolèrent le 404 à la suppression — conformes au pattern existant. Rien de bloquant.

Résumé des sévérités : 0 bloquant, 1 important, 1 nit.

return capturePluginResult('argocd', () => this.cleanupProject(project))
}

@OnEvent('cluster.upsert')

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

important — Le corps de la PR (section « État lors de la bascule ») affirme encore que « server-nestjs n'a aucun @onevent('cluster.upsert') ni @onevent('cluster.delete') » et que l'émission sera un no-op à la bascule. C'est faux sur ce head : les listeners existent dans argocd.service.ts (lignes 73 et 100). Mettre à jour le corps — c'est la note qui décide si #2756 peut fusionner sans risque de no-op. Profiter pour corriger la route listée GET /:clusterId/usage → le contrat est /usage/:clusterId.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le corps de la PR a été corrigé entre-temps : la section « État lors de la bascule » documente désormais les listeners @OnEvent('cluster.upsert'/'cluster.delete') d'argocd.service.ts et conclut à l'absence de no-op. Marqué à jour.

action: 'Create Cluster',
userId,
requestId,
}, 'Echec des services à la création/mise à jour du cluster')

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit — Le legacy distingue deux messages : « Echec des services à la création du cluster » (business.ts:132) et « Echec des services à la mise à jour du cluster » (business.ts:198). Ici create et update partagent « à la création/mise à jour ». Parité stricte des messages d'erreur = un message par appel ; deux chaînes distinctes coûtent zéro ligne de plus.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Corrigé dans 8655a0f : messages séparés aux libellés legacy exacts (« …à la création du cluster » / « …à la mise à jour du cluster »).

shikanime and others added 22 commits October 9, 2026 15:45
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Ib545fd310384d7847a47cd007d36a8526a6a6964

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I603381219713d5fcda1e1e6bf9b7a7496a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
rebases cleanly.

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Id763439e79b52a6af29fdef6bf8c1d826a6a6964
Co-authored-by: Automata <automata@shikanime.studio>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
…ked admin-token PR

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Ib00d7b390b49a646f18899caf9d879fb6a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
- name CreateClusterBodySchema/UpdateClusterBodySchema in shared, derive type aliases from them, validate at controller pipe
- propagate cluster.upsert hook failure as 422 with regression test
- dedupe test factories to canonical modules, drop divider comments

Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Ia5abe56ef859df38cc46c7491b8c07096a6a6964

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I74a8bf02a144ac92603ed7b89a4c395d6a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Co-authored-by: Automata <automata@shikanime.studio>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
ClusterService injects LogService and the controller guard requires auth and user-permission providers that ClusterModule never imported; the backend crashed at boot.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I4a060cde917ff9c62ac4779f2810f05c6a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
ClusterService injects EventEmitter2; import EventsModule so the provider resolves and the backend stops crashing at boot.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
ClusterService injects LogService and EventEmitter2 and the controller guard requires auth and user-permission providers that ClusterModule never imported; the backend crashed at boot.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Reflect-based guard metadata assertions pass whether or not the guard is
enforced and add no behavioural coverage; the guard spec and the controller
delegation tests are untouched.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Id551b411ab24291da518f2599ae1edd66a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Ie239c572870d655ca697a5ad53e487496a6a6964

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I8f0a537fd3f724c1bcd283997171a9a76a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Iea5eabc6a0b5ff15d0b32365897ec56a6a6a6964
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Id86ef6af532de064301d2586c163a7326a6a6964

Co-authored-by: Automata <automata@shikanime.studio>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I5cafffa7f7e9786916555e0e69894d6b6a6a6964

Co-authored-by: Automata <automata@shikanime.studio>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I0719ab0da6e2d14f052da99641f0980c6a6a6964
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I9681a6be979533285c8ea54ae6f9b9956a6a6964

Co-authored-by: Automata <automata@shikanime.studio>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Co-authored-by: Automata <automata@shikanime.studio>

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: Ic0c77b63ed86e4d744e584c7b4ed2d346a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I425f9664368d246076e7d5891de423606a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
- generateClusterTlsClientConfig now nests tlsClientConfig under username,
  password and bearerToken, matching the legacy convertConfig shape; secrets
  stay out of the tlsClientConfig block
- split create/update failure messages to their legacy-exact wording
- stop mutating the typed update body before destructuring it

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I3327b416d19ce00fa7be415daf162af16a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
…eries.utils

Colocate syncClusterProjectLinks beside its exported stage twin
syncClusterStageLinks; fold the single-caller projectsToRemoveFrom
helper into a plain filter at its only call site.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I0ef7ec192f02eac54de38aafd129d33a6a6a6964

Signed-off-by: Shikanime Deva <22115108+shikanime@users.noreply.github.com>
Rename generateClusterWhereInput to generateClusterWhere, move
getClusterEnvironments to listClusterEnvironments (findMany takes the
list verb) and listStagesByClusterId to getStagesByClusterId (single
cluster payload), and rename the queries-utils first parameter from
prisma to tx, matching the majority convention of the tree.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I84a4e9d33bcf75e65fd96e2c7faebe8b6a6a6964
Add ParseUUIDPipe to the four :clusterId routes so an invalid id is
rejected with 400 before reaching Prisma, and drop the local makeCluster
in favor of the environment module's factory.

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: William Phetsinorath <william.phetsinorath-open@interieur.gouv.fr>
Change-Id: I49872a8fe7f2b17d4943f6ad19755d646a6a6964
@shikanime
shikanime force-pushed the pr/cluster-migration branch from baee446 to 60e743c Compare October 9, 2026 13:47
@cloud-pi-native-sonarqube

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

built refactor Refactor code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants