diff --git a/.gitea/workflows/APP_NAME-build.yaml b/.gitea/workflows/APP_NAME-build.yaml index d720f59..3c31004 100644 --- a/.gitea/workflows/APP_NAME-build.yaml +++ b/.gitea/workflows/APP_NAME-build.yaml @@ -16,6 +16,19 @@ on: - '.gitea/workflows/__APP_NAME__-build.yaml' workflow_dispatch: {} +# ── Serialización de deploys (TENANT-BUILD-NO-CONCURRENCY-GUARD) ────────────── +# Sin este guard, múltiples dispatches del mismo repo (scaffold `on:push` + +# tenant-manager fase 1/fase 2 + re-dispatch por rotación de kubeconfig + smoke) +# corren `helm upgrade --install` EN PARALELO sobre el MISMO release → Helm aborta +# con "another operation (install/upgrade/rollback) is in progress" y deja el +# release trabado en `pending-install` → el primer deploy del tenant nunca +# completa (vivido E2E 2026-06-10, tenant 9e6babb0; destrabado a mano borrando el +# secret del release). Los runs se ENCOLAN, NO se cancelan: cancelar un run a mitad +# de `helm install` es justo lo que CREA el estado `pending-install`. +concurrency: + group: deploy-${{ gitea.repository }} + cancel-in-progress: false + jobs: # ── Gate de credenciales de build (GOLDEN-PATH-BUILD-DEPLOY-ORDER-RACE) ─── # Los secrets HARBOR_ROBOT_USER/TOKEN los siembra tenant-manager en su fase 1 @@ -180,6 +193,61 @@ jobs: echo "$KUBECONFIG_B64" | base64 -d > "${HOME}/.kube/config" chmod 600 "${HOME}/.kube/config" + - name: Esperar rancher-webhook ready (RANCHER-WEBHOOK-RACE-RECOVERY) + # Race condition durante el bootstrap del cluster tenant: el + # `rancher-webhook` Deployment en cattle-system aún no tiene endpoints + # cuando el primer `helm upgrade --install` intenta crear el namespace + # `apps`, lo que dispara el webhook + # `rancher.cattle.io.namespaces.create-non-kubesystem` y aborta el + # release con `no endpoints available for service rancher-webhook`. + # Paridad con el template web-backend (este paso faltaba en rest-api). + run: | + # `kubectl wait` falla con NotFound si el Deployment aún no existe: + # durante el bootstrap del cluster tenant, `rancher-webhook` puede no + # haberse creado todavía cuando corre este step. Primero esperamos a + # que el objeto APAREZCA (poll), luego a que esté Available. + until kubectl get deployment rancher-webhook -n cattle-system >/dev/null 2>&1; do + echo "Esperando a que el Deployment rancher-webhook exista en cattle-system..." + sleep 5 + done + kubectl wait deployment rancher-webhook \ + -n cattle-system \ + --for=condition=Available \ + --timeout=600s + + - name: Recuperar release Helm atascado (TENANT-BUILD-NO-CONCURRENCY-GUARD) + # Red de seguridad ADEMÁS del guard `concurrency`: si un run previo quedó a + # mitad de una operación Helm (cancelado, runner reiniciado, o carrera + # histórica anterior a este fix), el release queda en un estado `pending-*` + # y el siguiente `helm upgrade --install` aborta. Lo detectamos y recuperamos + # antes de desplegar. + # Fail-safe: SOLO actúa sobre estados `pending-*`/`uninstalling`; un release + # `deployed`, `failed` o inexistente NO se toca. Si el parseo no encuentra + # STATUS (formato inesperado) cae al branch no-op → comportamiento idéntico + # al actual, nunca peor. + run: | + STATUS="$(helm status __APP_NAME__ -n apps 2>/dev/null | sed -n 's/^STATUS: //p')" + echo "Estado actual del release __APP_NAME__: ${STATUS:-}" + case "$STATUS" in + pending-install|uninstalling) + # Nunca hubo una revisión desplegada con éxito → no se puede rollback. + # `helm uninstall` limpia el secret del release trabado (equivale al + # destrabado manual del 2026-06-10) sin riesgo de datos: el primer deploy + # aún no levantó la app. + echo "Release en '${STATUS}' (sin revisión estable) → helm uninstall para limpiar." + helm uninstall __APP_NAME__ -n apps --wait --timeout 2m || true + ;; + pending-upgrade|pending-rollback) + # Hubo una revisión estable previa → rollback a la última desplegada + # (preserva la app viva; el upgrade siguiente reintenta la nueva imagen). + echo "Release en '${STATUS}' → helm rollback a la última revisión estable." + helm rollback __APP_NAME__ 0 -n apps --wait --timeout 2m || true + ;; + *) + echo "Release en estado estable o inexistente → sin recuperación necesaria." + ;; + esac + - name: Helm upgrade --install env: TENANT_ID: ${{ vars.TENANT_ID }}