airvzxf / airvzxf/ftp-deployment-action

test(integration): INPUT_MAX_RETRIES=3 enmascara flake de lftp AUTH TLS sin diagnosticar la causa raíz

Open
#138 2 comments 0 reactions 0 assignees View on GitHub
bug pending-human pending-validation
Dominant language
Shell
Stars
37
Forks
9
Avg merge
44m
Merged PRs (30d)
47

Description

## Summary

Durante la investigación de #135 se encontró un segundo modo de falla (Mode B) distinto al `apk add` race: el handshake TLS AUTH entre vsftpd Alpine 3.24 y lftp 4.9.3 OpenSSL 3.x falla transientemente, y `lftp` sale con código 1 en el primer intento. Como defensa contra este flake, el PR de #135 bumpea `INPUT_MAX_RETRIES=1` a `INPUT_MAX_RETRIES=3` en los escenarios 03 y 04. El bump hace el test menos estricto y puede enmascarar bugs reales sin diagnosticar la causa raíz.

## Archivos afectados

- `tests/integration/scenarios/03-ftps-explicit-upload.sh:89`
- `tests/integration/scenarios/04-ftps-implicit-upload.sh:83`

Ambos ahora pasan `INPUT_MAX_RETRIES=3` en vez de `INPUT_MAX_RETRIES=1`.

## Observación Mode B

Run CI [`33705252859`](https://github.com/airvzxf/ftp-deployment-action/actions/runs/33705252859) (mismo run que destapó el Mode A de #135) tuvo también un fallo de lftp en el primer intento del escenario 04:

```
[DEBUG lftp] ---> AUTH TLS
<--- 234 AUTH TLS OK.
[SSL negotiation...]
lftp: error: Fatal error: TLS handshake failure
```

El error es de handshake TLS — lftp 4.9.3 OpenSSL 3.x contra vsftpd Alpine 3.24. En retry (con el `INPUT_MAX_RETRIES=3` que pusimos como defensa), el mismo escenario pasó. No hay aserción sobre `Try #1` que pueda distinguir "el primer intento falló pero los retries lo recuperaron" de "el primer intento funcionó".

## Por qué es un problema

`INPUT_MAX_RETRIES=3` es un setting de producción del action (los usuarios lo ven y lo setean). Bumpearlo en los tests para defense-in-depth significa:

1. **El test ya no detecta regresiones reales de lftp-vs-vsftpd.** Si una nueva versión de lftp introduce un TLS bug que solo se manifiesta en el primer intento, el test pasa gracias al retry y nunca lo vemos.
2. **El test no diagnostica la causa raíz.** No hay log que diga "Try #1 falló por X, Try #2 pasó". El operador que vea el fallo tiene que adivinar.
3. **Mode B sugiere un problema del servidor, no del cliente.** El flake se manifiesta en `vsftpd` con `ssl_ciphers=HIGH:MEDIUM:!DHE:!DH` y `seccomp_sandbox=NO`. Vale la pena investigar si la combinación de cipher list + sandbox disabled es lo que hace que el primer handshake falle mientras que los retries pasen (probable: race entre init del sandbox y accept del primer cliente).

## Suggested fix

Tres pasos en orden de preferencia:

### A. Restaurar `INPUT_MAX_RETRIES=1` y abrir issue específico de Mode B

Si Mode B no reaparece en 5 corridas consecutivas post-merge de #135, restaurar `INPUT_MAX_RETRIES=1` en los escenarios 03 y 04. Esto devuelve el rigor al test.

### B. Investigar el path TLS entre vsftpd Alpine 3.24 y lftp 4.9.3 OpenSSL 3.x

Hipótesis a validar:

- ¿El cipher list `HIGH:MEDIUM:!DHE:!DH` fuerza un cipher que OpenSSL 3.x tarda en negociar la primera vez pero negocia rápido en los retries?
- ¿`seccomp_sandbox=NO` es la causa del primer-fail? ¿Activar `seccomp_sandbox=YES` cambia el comportamiento?
- ¿Es un bug específico de la versión de vsftpd Alpine 3.24? ¿Bajar a 3.23 (alineado con la del action image) lo elimina?
- ¿Es un bug específico de lftp 4.9.3 OpenSSL 3.x? ¿Forzar lftp 4.9.2 (GnuTLS) lo elimina?

### C. Añadir aserción sobre `Try #1` y `Try #N`

Independientemente del path A o B, los tests deberían poder distinguir "pasó al primer intento" de "pasó tras N retries". El output de la action incluye líneas como `::group::Mirror attempt 1/3` que se pueden grepear:

```sh
# Cerca del final del escenario
assert_log_contains "Mirror attempt 1/N" "${WORK_DIR}/action.log" \
|| log_fail "Expected 'Mirror attempt 1/N' in log, got retries only"
```

Si la aserción falla, el test falla con un mensaje claro en vez de pasar silenciosamente gracias al retry.

## Acceptance criteria

1. Si Mode B no reaparece post-#135, `INPUT_MAX_RETRIES=1` restaurado en escenarios 03 y 04.
2. Si Mode B reaparece, hay un issue específico de Mode B abierto con hipótesis (A/B/C de arriba) validadas o refutadas.
3. Los escenarios 03 y 04 tienen una aserción que falla si el primer intento del action falla (independientemente del retry que lo recupere).
4. CI 7/7 verde.

## Out of scope

Este cambio es independiente del fix de #135 — son archivos distintos y la investigación de Mode B es open-ended. El PR que cierra #135 deja `INPUT_MAX_RETRIES=3` como defense temporal y este issue es para tracking del seguimiento.

Si el contributor decide abordar Mode B en un PR futuro, no es bloqueante para merge de #135 (los tests pasan con `INPUT_MAX_RETRIES=3`). Pero sí es bloqueante para v2.11.x: queremos rigor en los tests antes de cortar el próximo release candidate.

## Related

- PR: https://github.com/airvzxf/ftp-deployment-action/pull/139
- Original investigation: #135 (Mode A: `apk add` race)
- Sister issues: #136 (`lftp_run_script` latente), #137 (`smoke.sh` latente)

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.