MIGRATION — agroworoba → capbe-agro

📄 Source : CAPBE-AGRO/docs/MIGRATION.md 🕒 Généré le 06/08/2026 09:56
Sommaire

Document technique qui explique comment et pourquoi l'application agroworoba (pile autonome : Caddy local + FastAPI + Postgres + Odoo dans un seul conteneur) a été transformée en capbe-agro dans l'architecture CAPBE.

Le script source de cette migration est playbook/12-migrate-projects.sh. Ce kit en contient un wrapper spécialisé : deploy/migrate-agro.sh.


1. L'architecture cible (CAPBE)

CAPBE mutualise les backends dans des conteneurs dédiés, connectés sur le réseau privé capbe-br0 (10.10.10.0/24, NAT sortant). Un seul point d'entrée public : capbe-caddy (TLS auto, On-Demand).

Backend partagé Conteneur Rôle
PostgreSQL 18 + PostGIS capbe-pgis base de données + hébergement des API migrées (uvicorn)
Oracle AI DB Free capbe-oracle stack #2 (non utilisé par agro)
Odoo 19 multi-tenant capbe-odoo ERP (un tenant = une base, dbfilter ^%d$)
Messagerie Stalwart capbe-mail SMTP/IMAP/JMAP + webmail

Le projet agro y est découpé en 3 morceaux :

agroworoba (1 conteneur autonome)          capbe-agro (architecture CAPBE)
┌──────────────────────────┐      ┌──────────────────────────────────────┐
│ Caddy local (TLS)        │  →   │ capbe-caddy : https://agro.app.ci.capbe.ovh
│ Front Flutter            │  →   │ capbe-agro : nginx :80 (/var/www/html)
│ FastAPI + base Postgres  │  →   │ capbe-pgis :8001 → /srv/agro/api + base agro_api
│ Odoo + base              │  →   │ capbe-odoo :8069 → base agro (tenant)
└──────────────────────────┘      └──────────────────────────────────────┘
        (INTACT — jamais touché)

2. Les 3 phases de la migration (détail réel)

Phase 1 — Frontend statique → capbe-agro

  • Création du conteneur capbe-agro (profil front, réseau capbe-br0).
  • Provisionnement nginx :80 (root /var/www/html, try_files ... /index.html).
  • Détection de la racine web de l'héritage : /var/www/html (nginx) ou /usr/share/caddypiège constaté en réel pour agroworoba : le front Flutter vivait dans la racine par défaut de Caddy (/usr/share/caddy), le script ne cherchait que /var/www/html → front 403 avant correction.
  • Copie du build via tar | incus exec tar (ne dépend pas de scp/rsync).

Phase 2 — API FastAPI + base → capbe-pgis

  • Détection du code API : /opt/<legacy>/api (layout historique) ou /app (layout supervisor/venv) — piège agroworoba : l'API tournait en uvicorn app.main:app depuis /app ; sans cette détection, l'étape API était silencieusement ignorée.
  • Base : pg_dump --no-owner --no-privileges de la base source (nom lu dans alembic.inisqlalchemy.url), restore sur pgis en base <id>_api.
  • Port : un service uvicorn par port sur pgis — agro a pris le 8001 (meca occupant le 8000). Routage Caddy même-origine : agro.app.ci.capbe.ovh/api/* → pgis:8001.
  • .env : recopié vers /srv/agro/api/.env (chmod 600) avec DATABASE_URL/DB_PASSWORD réécrits vers pgis (rôle app). Si aucun .env hérité : généré avec DATABASE_URL (+ DATABASE_URL_SYNC pour SQLAlchemy async) et REDIS_URL.
  • alembic.ini (tous) : sqlalchemy.url repointé vers capbe-pgis.
  • Service systemd agro-api sur pgis : uvicorn app.main:app --port 8001.
  • Redis + Celery déployés si l'app déclare un celery_app.

Phase 3 — Odoo → tenant de capbe-odoo

  • Base Odoo source détectée via db_name de la config — repli convention <legacy>_odoo. Piège agroworoba constaté en réel : la base « Odoo » de l'héritage ne contenait en fait que les tables orm_signaling_* de la FastAPI (jamais initialisée comme ERP) → le script détecte l'absence de ir_module_module et initialise un tenant VIERGE (odoo -i base) pour une URL fonctionnelle (aucune donnée ERP à attendre).
  • pg_dump --no-owner → base <id> sur pgis (rôle odoo), servie par capbe-odoo avec dbfilter ^%d$https://agro.ci.capbe.ovh.
  • Filestore : copié si un chemin standard existe, sinon avertissement.

3. Pièges rencontrés en réel (corrections incluses dans le script)

  1. Racine web Caddy (/usr/share/caddy) : le front agroworoba n'était pas dans /var/www/html → 403. Le script détecte les deux.
  2. Layout API /app : l'API agroworoba tournait depuis /app (pas /opt/<legacy>/api) → étape ignorée en silence avant la correction.
  3. Tenant Odoo non initialisé : base source sans ir_module_module → tenant vierge créé (odoo -i base).
  4. Propriété PostgreSQL (PG15+) : REASSIGN OWNED échoue → fix_owner + sweep_owner (DO blocks) réattribuent les objets utilisateur. Le sweep est indispensable pour les tables orm_signaling_* (invisibles au rôle odoo sinon → DuplicateTable, registre KO, login 500 — constaté sur meca).
  5. /tmp/ et non /root/ : le restore tourne via su postgres (ne peut pas lire /root/, 700).
  6. Héritage jamais touché : agroworoba n'est ni arrêté ni supprimé — son sort est décidé manuellement après validation (playbook/POST-DEPLOY.md).

4. État réel constaté (validation du 03/08/2026)

Vérification Résultat
https://agro.app.ci.capbe.ovh ✅ 200 (front Flutter)
https://agro.app.ci.capbe.ovh/api/v1/auth/me ✅ 401 sans token (relay → pgis:8001 prouvé)
https://agro.ci.capbe.ovh/web/login?db=agro ✅ 200 (tenant Odoo)
agro-api sur pgis ✅ actif, uvicorn :8001
Base agro_api ✅ présente
Tenant agro ✅ présent (676 modules Odoo)
Héritage agroworoba ⏳ RUNNING (sort différé)

5. Rejouer la migration

sudo ./deploy/migrate-agro.sh      # wrapper : playbook 12-migrate-projects.sh agroworoba=agro
sudo ./playbook/09-caddy-config.sh # (re)déploie le Caddyfile si besoin
sudo ./ops/smoke-agro.sh           # validation

Idempotent : les composants déjà migrés sont détectés et ignorés (warn) ; les manquants sont complétés.