|
|
|
@@ -102,11 +102,12 @@ func (h *HelmProvisioner) Provision(d Demo, resources []ExternalResource, cfg Pr
|
|
|
|
|
return fmt.Errorf("création namespace %s: %w", d.Namespace, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Préparer les valeurs. Pas de persistence à la création : les démos
|
|
|
|
|
// d'essai sont éphémères (TTL 30j) — voir UpgradeToPaid pour le passage
|
|
|
|
|
// en stockage persistant lorsqu'un client devient payant.
|
|
|
|
|
postgresValues := h.buildPostgresValues(d, false)
|
|
|
|
|
redisValues := h.buildRedisValues(d, false)
|
|
|
|
|
// PVC dès la création de la démo (pas d'emptyDir) : si le client ne
|
|
|
|
|
// souscrit pas, le teardown du namespace (TTL ou suppression admin)
|
|
|
|
|
// supprime le PVC avec le reste. S'il souscrit, TransferToPaid n'a rien
|
|
|
|
|
// d'autre à faire côté stockage — les données sont déjà en place.
|
|
|
|
|
postgresValues := h.buildPostgresValues(d)
|
|
|
|
|
redisValues := h.buildRedisValues(d)
|
|
|
|
|
|
|
|
|
|
// Secret partagé backend <-> lbtelegram (authentifie les appels de
|
|
|
|
|
// lbtelegram vers backend) — généré une seule fois si le load-balancer
|
|
|
|
@@ -199,7 +200,8 @@ func (h *HelmProvisioner) EnsureSharedInfra() error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Teardown détruit une démo en supprimant son namespace (cascading).
|
|
|
|
|
// Teardown détruit une démo en supprimant son namespace (cascading) — les
|
|
|
|
|
// PVC postgres/redis de la démo partent avec, puisqu'ils sont namespaced.
|
|
|
|
|
func (h *HelmProvisioner) Teardown(d Demo) error {
|
|
|
|
|
return h.deleteNamespace(d.Namespace)
|
|
|
|
|
}
|
|
|
|
@@ -259,56 +261,6 @@ const (
|
|
|
|
|
demoDBPass = "demo-postgres-pass"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// UpgradeToPaid bascule postgresql/redis d'une démo en stockage persistant
|
|
|
|
|
// lorsqu'un client passe en abonnement payant, en préservant les données
|
|
|
|
|
// postgres déjà créées pendant l'essai : pg_dump avant la bascule (le volume
|
|
|
|
|
// actuel est un emptyDir éphémère), puis restore une fois le nouveau volume
|
|
|
|
|
// persistant en place. Redis n'est pas migré (cache/sessions — reconstruit
|
|
|
|
|
// naturellement), seule sa persistence est activée pour la suite.
|
|
|
|
|
func (h *HelmProvisioner) UpgradeToPaid(d Demo) error {
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
|
|
pgPod, err := h.findPod(ctx, d.Namespace, "postgresql")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("pod postgresql introuvable: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dumpCmd := fmt.Sprintf("PGPASSWORD=%s pg_dump -h localhost -U %s %s", demoDBPass, demoDBUser, demoDBName)
|
|
|
|
|
dump, stderr, err := h.execInPod(ctx, d.Namespace, pgPod, "postgresql", []string{"sh", "-c", dumpCmd}, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("pg_dump: %w (%s)", err, stderr)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bascule postgresql en stockage persistant : le pod redémarre (nouveau
|
|
|
|
|
// PVC, initialement vide) — c'est pour ça que le dump ci-dessus doit
|
|
|
|
|
// être pris AVANT cet appel. helm upgrade --wait attend que le nouveau
|
|
|
|
|
// pod soit prêt (readinessProbe = pg_isready) avant de retourner.
|
|
|
|
|
if err := h.upgradeInstallChart(d.Namespace, d.Namespace+"-postgresql", "postgresql", h.buildPostgresValues(d, true)); err != nil {
|
|
|
|
|
return fmt.Errorf("upgrade postgresql (persistence): %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
newPgPod, err := h.findPod(ctx, d.Namespace, "postgresql")
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("pod postgresql introuvable après upgrade: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if strings.TrimSpace(dump) != "" {
|
|
|
|
|
restoreCmd := fmt.Sprintf("PGPASSWORD=%s psql -h localhost -U %s %s", demoDBPass, demoDBUser, demoDBName)
|
|
|
|
|
_, stderr, err := h.execInPod(ctx, d.Namespace, newPgPod, "postgresql", []string{"sh", "-c", restoreCmd}, strings.NewReader(dump))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return fmt.Errorf("restore pg_dump: %w (%s)", err, stderr)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := h.upgradeInstallChart(d.Namespace, d.Namespace+"-redis", "redis", h.buildRedisValues(d, true)); err != nil {
|
|
|
|
|
return fmt.Errorf("upgrade redis (persistence): %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.Printf("Démo %s basculée en stockage persistant (données postgres migrées)", d.Namespace)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// findPod retourne le nom du premier pod du namespace dont le nom contient nameContains.
|
|
|
|
|
func (h *HelmProvisioner) findPod(ctx context.Context, namespace, nameContains string) (string, error) {
|
|
|
|
|
pods, err := h.k8sClient.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{})
|
|
|
|
@@ -694,7 +646,8 @@ func (h *HelmProvisioner) buildSecrets(resources []ExternalResource) map[string]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// buildPostgresValues construit les valeurs pour le chart postgresql.
|
|
|
|
|
func (h *HelmProvisioner) buildPostgresValues(d Demo, persistent bool) map[string]interface{} {
|
|
|
|
|
// Toujours persistant (PVC) : voir Provision.
|
|
|
|
|
func (h *HelmProvisioner) buildPostgresValues(d Demo) map[string]interface{} {
|
|
|
|
|
return map[string]interface{}{
|
|
|
|
|
"auth": map[string]interface{}{
|
|
|
|
|
"password": "demo-postgres-pass", // Mot de passe OBLIGATOIRE (champ correct pour le chart)
|
|
|
|
@@ -706,13 +659,14 @@ func (h *HelmProvisioner) buildPostgresValues(d Demo, persistent bool) map[strin
|
|
|
|
|
"port": 5432,
|
|
|
|
|
},
|
|
|
|
|
"persistence": map[string]interface{}{
|
|
|
|
|
"enabled": persistent,
|
|
|
|
|
"enabled": true,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// buildRedisValues construit les valeurs pour le chart redis.
|
|
|
|
|
func (h *HelmProvisioner) buildRedisValues(d Demo, persistent bool) map[string]interface{} {
|
|
|
|
|
// Toujours persistant (PVC) : voir Provision.
|
|
|
|
|
func (h *HelmProvisioner) buildRedisValues(d Demo) map[string]interface{} {
|
|
|
|
|
return map[string]interface{}{
|
|
|
|
|
"service": map[string]interface{}{
|
|
|
|
|
"type": "ClusterIP",
|
|
|
|
@@ -722,7 +676,7 @@ func (h *HelmProvisioner) buildRedisValues(d Demo, persistent bool) map[string]i
|
|
|
|
|
"password": "demo-redis-pass", // Mot de passe simple pour les démos
|
|
|
|
|
},
|
|
|
|
|
"persistence": map[string]interface{}{
|
|
|
|
|
"enabled": persistent,
|
|
|
|
|
"enabled": true,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|