ligbox-ops-platform/specs/026-purge-traefik-validation/spec.md
Ligbox Spec Hub 3a2c64834b Initial import: ligbox-ops-platform + specs + LAPTOP + obsidian merge (CT130)
Source: VM122 /opt + obsidian-infra + LAPTOP
Hub: CT130 spec-hub 10.10.10.130
2026-06-19 17:26:41 +00:00

10 KiB
Raw Permalink Blame History

Spec 026 — Purge VM112: validação Traefik pós-remoção (CT114)

Criado: 2026-06-19
Solicitado por: Roger
Prioridade: P0 (incidente produção)
Status: Implementado (VM112 + CT114, 2026-06-19)
Sistema: Wizard VM112 · Traefik CT114 · Desk VM122
Relacionado: Spec 017 (purge domínio) · Spec 025 (continuidade wizard) · Spec 018 (Serviços / drawer purge)


Incidente que motivou a spec

Data: 2026-06-19 ~02:18 UTC
Sintoma: https://onboard.ligbox.com.br/onboard404 page not found (Traefik Go default), afectando todos os domínios onboard — não só o domínio purgado.

Domínios purgados na sessão: iofficebooks.com, exuberanti.com.br.

Causa raiz:

  1. _purge_traefik_routers() em /opt/ligbox-wizard/backend/app/services/domain_orchestration.py remove routers por corte de texto (Host(...) → próximo \n ).
  2. Isso deixou bloco mail-mail-exuberanti-com-br-Router sem rule: e chave duplicada no dynamic.yml.
  3. Traefik v3.6 rejeitou o ficheiro inteiro:
    yaml: unmarshal errors: mapping key "mail-mail-exuberanti-com-br-Router" already defined
    
  4. Após restart, só 3 routers internos activos (acme, api, dashboard) — zero rotas de produção.
  5. O purge reportou traefik_ok porque validou apenas SSH write + restart, não carga efectiva da config.

Correcção manual aplicada (19/06): remoção de routers inválidos/duplicados + restart Traefik → 62 routers activos.


Objetivo

Tornar o purge de domínio seguro para a plataforma inteira: após remover um tenant, o Traefik tem de continuar operacional e o onboard tem de responder 200.

Regra de ouro (nova):

Purge só está concluído quando o domínio sumiu da base e o Traefik tem ≥ N routers e GET https://onboard.ligbox.com.br/onboard200 com HTML do wizard.


Fora de escopo

  • Reescrever Spec 017 (histórico Desk, RBAC, drawer)
  • Purge parcial (só DNS, só contas)
  • Validação de certificados LE por domínio purgado (opcional futuro)
  • Automatizar purge agendado

Problema na implementação actual (VM112)

Função Ficheiro Problema
_purge_traefik_routers domain_orchestration.py Corte textual frágil; não remove middleware webmail-pending-{slug}; não valida YAML
_purge_traefik_sni idem OK funcional; falta verificação pós-restart HAProxy
_execute_purge idem Marca traefik_ok sem smoke test
Portal users _purge_portal_users /var/lib/ibytera-mail-portal/portal_users/falta /var/lib/ligbox-wizard/portal_users/
Nginx Carbonio Não limpa vhosts mail.{domain} em /opt/zextras/conf/nginx/includes/
Branding / scripts deploy Não remove entrada tenant_branding.py nem refs em apply-admin-nginx-overrides.py

Solução proposta

Fase A — Remoção Traefik robusta (P0)

Substituir corte textual por script Python remoto no CT114 (mesmo padrão de infrastructure.do_traefik()):

  1. Backup antes de editar:
    /root/traefik/dynamic.yml.bak-purge-{domain_slug}-{timestamp}
    
  2. Parse YAML (yaml.safe_load para validação; edição linha-a-linha — nunca safe_dump no ficheiro inteiro).
  3. Remover, por domínio:
    • Router mail-mail-{slug}-Router (e variantes)
    • Middleware webmail-pending-{slug} (redirect regex para wizard)
    • Qualquer router cujo rule contenha Host(\mail.{domain}`)` ou alias mail
  4. Validação pré-restart:
    • YAML parse OK
    • Zero chaves duplicadas em http.routers
    • Todo router tem campo rule não vazio
    • Zero ocorrências de mail.{domain} no texto (sanity grep)
  5. Restart Traefik só se validação OK.
  6. Se validação falhar → rollback do backup sem restart.

Slug: {domain} com .- (ex.: exuberanti.com.brexuberanti-com-br).


Fase B — Verificação pós-purge (P0)

Novo step _execute_purge: traefik_validate (após traefik_routers).

# Check Comando / origem Critério
B1 Routers carregados curl -s http://127.0.0.1:8080/api/http/routers (CT114) count ≥ 10 (alerta se < 10; falha se < 5)
B2 Onboard router activo JSON routers existe onboard-ligbox-Router@file enabled
B3 Smoke HTTPS onboard curl -sf -o /dev/null -w '%{http_code}' https://onboard.ligbox.com.br/onboard 200
B4 Smoke API VM112 curl -sf -o /dev/null -w '%{http_code}' http://10.10.10.112:8090/onboard 200
B5 Sem refs domínio no dynamic grep -i {domain} em dynamic.yml 0 matches (excepto backup)
B6 Log Traefik limpo docker logs traefik 2>&1 | tail -20 sem unmarshal errors / invalid rule nos últimos 30s

Falha em B1B4: rollback dynamic.yml + restart Traefik + step traefik_validate = error + job purge = error (não done).

Timeline Desk: novo passo visível «Validar Traefik / onboard» com detalhe de cada check.


Fase C — Purge VM112 completo (P1)

Expandir _execute_purge com steps adicionais (ou sub-steps documentados):

Step Acção
portal_users_wizard_store Apagar JSON em /var/lib/ligbox-wizard/portal_users/ cujo email ∈ domínio
nginx_vhosts Remover server_name mail.{domain} de includes nginx Carbonio + nginx -t + reload
tenant_branding Remover linha em tenant_branding.py
deploy_scripts Remover mail.{domain} de apply-admin-nginx-overrides.py e sync-traefik-admin-certs.sh
traefik_export_certs Apagar mail-{slug}*.pem em /opt/zextras/ssl/letsencrypt/traefik-export/

Cada step reporta ok / error na timeline; falha nginx nginx -terror (não deixa mail quebrado).


Fase D — Desk / histórico (P2)

  • Persistir em vm112_json do job: resultado de traefik_validate (checks B1B6).
  • Badge error no histórico se rollback Traefik ocorreu.
  • Alerta ops (ntfy / webhook) quando purge falha em traefik_validate.

Alterações de API / timeline

VM112 — novos steps em POST /api/admin/domains/{domain}/purge

Ordem actualizada (trecho Traefik):

…
traefik_sni          → running → done|error
traefik_routers      → running → done|error   (Fase A — lógica nova)
traefik_validate     → running → done|error   (Fase B — NOVO)
…

Resposta result (campos novos):

{
  "traefik_validate": {
    "ok": true,
    "router_count": 62,
    "onboard_http": 200,
    "rollback": false
  },
  "traefik_rollback": null
}

Em falha:

{
  "traefik_validate": { "ok": false, "router_count": 3, "onboard_http": 404, "rollback": true },
  "traefik_rollback": "dynamic.yml.bak-purge-exuberanti-com-br-20260619T021800Z"
}

Ficheiros a alterar

VM Ficheiro Fase
112 backend/app/services/domain_orchestration.py A, B, C
112 backend/app/services/infrastructure.py A (reutilizar _router_key_for_host, SSH helpers)
114 /root/traefik/dynamic.yml (runtime — só via purge script)
122 api/app/vm112_domains_routes.py D (opcional — repassar novos campos)
122 frontend/assets/app.js D (render checks no modal histórico)

Deploy: VM112 systemctl restart ligbox-wizard após merge.


Critérios de aceitação

  1. Purge de domínio teste remove router/middleware sem duplicar chaves YAML.
  2. Após purge, Traefik API reporta ≥ 10 routers HTTP.
  3. curl -sf https://onboard.ligbox.com.br/onboard200 imediatamente após purge.
  4. Purge com YAML inválido simulado → rollback automático + job status error (não done).
  5. Portal users removidos de ambas as pastas (ibytera-mail-portal + ligbox-wizard).
  6. Histórico Desk (Spec 017 v2) mostra step traefik_validate com detalhe.
  7. Regressão: purge de domínio inexistente (no_zone, domínio já ausente) continua idempotente.

Test plan (E2E)

# Pré: criar domínio teste via wizard (zona CF pending OK)
DOMAIN=teste-purge-$(date +%s).example.com  # ou domínio real de lab

# Executar purge
curl -s -X POST "http://10.10.10.112:8090/api/admin/domains/${DOMAIN}/purge?sync=true" \
  -H "X-Api-Key: $ADMIN_API_KEY" | jq '.result.traefik_validate'

# Validar plataforma
curl -sf -o /dev/null -w "onboard:%{http_code}\n" https://onboard.ligbox.com.br/onboard
ssh root@10.10.10.114 'curl -s http://127.0.0.1:8080/api/http/routers | python3 -c "import sys,json; print(len(json.load(sys.stdin)),\"routers\")"'

Teste de regressão (incidente 19/06): purge exuberanti.com.br duas vezes seguidas → segunda execução idempotente, Traefik estável.


Riscos e mitigação

Risco Mitigação
Rollback falha Manter últimos 5 backups dynamic.yml.bak-purge-*
Traefik API :8080 fechado externamente Checks só via SSH CT114 localhost
Purge longo (>60s) Jobs async Spec 017 já existem; validate no final
Race: dois purges simultâneos Lock file CT114 /tmp/traefik-dynamic.lock

Prioridade no backlog

Fase Prioridade Motivo
A + B P0 Evita outage total do onboard
C P1 Limpeza completa tenant (nginx, branding)
D P2 Observabilidade Desk

Referências

  • Incidente: purge exuberanti.com.br 2026-06-19 — Traefik 3 routers only
  • Spec 017 — ordem purge VM112 + histórico Desk
  • Spec 025 — item backlog «Traefik YAML validation» (consolidar implementação aqui)
  • Log Traefik: mapping key "mail-mail-exuberanti-com-br-Router" already defined at line 475
  • Fix manual: dynamic.yml.bak-fix-dup-exuberanti-20260619

Conclusão (estado actual)

Fase Entrega Estado
A Remoção YAML estruturada + backup/rollback
B traefik_validate + smoke onboard
C Purge nginx / branding / wizard store (parcial — VM112)
D Histórico Desk + alerta ops 📋

Implementação pendente em VM112 — esta spec documenta o backlog acordado com Roger (2026-06-19).