chore: build
ci-api / test (push) Failing after 14m25s
ci-web / test (push) Failing after 8m17s

This commit is contained in:
Xor290
2026-09-20 16:14:29 +02:00
parent 76b1412968
commit 9e11f01c2c
15 changed files with 65 additions and 359 deletions
@@ -1,19 +1,16 @@
package demos
import (
"context"
"fmt"
"log"
"strconv"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// Projets vitrine (voir internal/projects) : même infrastructure que les
// démos (namespace isolé, NetworkPolicy, ResourceQuota, Traefik + WAF
// partagés, sauvegardes Velero par namespace) mais avec les charts du
// produit vitrine (h.vitrineChartsDir) et une durée d'abonnement en mois.
// Le cycle de vie (durée, suspension, suppression) vit dans internal/projects ;
// Le cycle de vie (durée, prolongation, suppression) vit dans internal/projects ;
// ce fichier ne contient que ce qui touche au cluster.
// Noms de release/chart utilisés par les charts vitrine (dossier
@@ -29,10 +26,6 @@ func vitrineFrontendService(namespace string) string {
return namespace + "-frontend-vitrine-frontend"
}
// replicasBeforeSuspendAnnotation : nombre de réplicas d'une charge de travail
// avant sa suspension, pour la relancer à l'identique (voir SuspendProject).
const replicasBeforeSuspendAnnotation = "omnex.app/replicas-before-suspend"
// ProjectSpec : ce dont le provisioner a besoin pour déployer un projet
// vitrine. AdminPassword ne doit jamais être persisté : il ne transite que
// par le fichier de valeurs Helm temporaire puis le Secret k8s du chart.
@@ -128,92 +121,6 @@ func (h *HelmProvisioner) TeardownProject(namespace string) error {
return h.Teardown(Demo{Namespace: namespace})
}
// SuspendProject met à 0 les Deployment et StatefulSet du namespace : le
// projet n'est plus servi (Traefik répond 503) mais ses PVC, ses Secrets et
// son IngressRoute restent en place. Le nombre de réplicas d'origine est
// noté en annotation pour ResumeProject. Idempotent.
func (h *HelmProvisioner) SuspendProject(namespace string) error {
return h.scaleProject(namespace, false)
}
// ResumeProject relance ce que SuspendProject a arrêté, au nombre de
// réplicas d'origine (1 à défaut d'annotation). Idempotent.
func (h *HelmProvisioner) ResumeProject(namespace string) error {
return h.scaleProject(namespace, true)
}
func (h *HelmProvisioner) scaleProject(namespace string, up bool) error {
ctx := context.Background()
deploys, err := h.k8sClient.AppsV1().Deployments(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("liste des deployments de %s: %w", namespace, err)
}
for i := range deploys.Items {
d := &deploys.Items[i]
if d.Spec.Replicas == nil {
one := int32(1)
d.Spec.Replicas = &one
}
target, changed := nextReplicas(d.Spec.Replicas, &d.Annotations, up)
if !changed {
continue
}
d.Spec.Replicas = &target
if _, err := h.k8sClient.AppsV1().Deployments(namespace).Update(ctx, d, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("mise à l'échelle du deployment %s: %w", d.Name, err)
}
}
sets, err := h.k8sClient.AppsV1().StatefulSets(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return fmt.Errorf("liste des statefulsets de %s: %w", namespace, err)
}
for i := range sets.Items {
s := &sets.Items[i]
if s.Spec.Replicas == nil {
one := int32(1)
s.Spec.Replicas = &one
}
target, changed := nextReplicas(s.Spec.Replicas, &s.Annotations, up)
if !changed {
continue
}
s.Spec.Replicas = &target
if _, err := h.k8sClient.AppsV1().StatefulSets(namespace).Update(ctx, s, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("mise à l'échelle du statefulset %s: %w", s.Name, err)
}
}
return nil
}
// nextReplicas calcule le nombre de réplicas cible d'une charge de travail et
// met à jour ses annotations. changed=false : rien à faire (déjà dans l'état
// demandé).
func nextReplicas(current *int32, annotations *map[string]string, up bool) (target int32, changed bool) {
if !up {
if *current == 0 {
return 0, false
}
if *annotations == nil {
*annotations = map[string]string{}
}
(*annotations)[replicasBeforeSuspendAnnotation] = strconv.Itoa(int(*current))
return 0, true
}
if *current != 0 {
return *current, false
}
target = 1
if v, ok := (*annotations)[replicasBeforeSuspendAnnotation]; ok {
if n, err := strconv.Atoi(v); err == nil && n > 0 {
target = int32(n)
}
delete(*annotations, replicasBeforeSuspendAnnotation)
}
return target, true
}
func (h *HelmProvisioner) buildVitrinePostgresValues(password string) map[string]interface{} {
// Nom de base et utilisateur : défauts du chart (vitrine_db / postgres),
// alignés sur database.name / database.user du chart backend.