feat(install): --update-in-place, actualización sin privilegios y reversible
Hay entornos donde no se usa root en absoluto, así que ni `sudo -n` ni una cuenta root son opciones. Este modo actualiza una instalación existente dejando su unit de systemd intacto, que es lo único que una actualización necesita de verdad: reemplazar el binario y reiniciar el proceso. Se apoya en dos hechos, uno de ellos contrario a lo que decía el propio repo: - `install` NO sufre ETXTBSY. A diferencia de `cp` —que abre con O_TRUNC—, desvincula el destino antes de crearlo, y por eso `make install` funciona sobre binarios en ejecución. Comprobado: `cp` sobre un ELF corriendo da "Text file busy" y `install` no. La consecuencia es la que importa: reemplazar el binario exige escritura en el DIRECTORIO, no en el archivo. El comentario de install.sh, BUILD.md y el CHANGELOG afirmaban lo contrario y mandaban al operador a diagnosticar un archivo en uso cuando lo que tenía era un EACCES. - El unit corre como el usuario que instaló (cadena SUDO_USER) y trae Restart=always, así que esa cuenta puede señalizar el proceso y systemd lo relevanta con el binario nuevo. No hace falta systemctl ni tocar /etc. Robustez, que es donde estaba el trabajo real: - **Reversible.** Respalda el binario antes de reemplazarlo y, si el nuevo no arranca, lo restaura y confirma que el proceso volvió. Sin esto, una actualización fallida deja el servidor sin agente. Si tampoco puede revertir, conserva el respaldo y lo dice en vez de fingir éxito. - **No interrumpe restauraciones.** El agente no atiende SIGTERM: matarlo a media restauración deja ese respaldo vetado para siempre (has_blocking_job_by_hash) y puede dejar la base en SINGLE_USER. Se comprueba Temp/ dos veces —antes de copiar y otra vez justo antes de señalizar, para cerrar la ventana— y sale con 75 (EX_TEMPFAIL), que el panel traduce a "reintenta luego" y no a un fallo. - **Diagnostica por qué no volvió**: distingue un unit sin Restart=always de un StartLimitBurst agotado, con el comando de recuperación. - Omite el bootstrap de 20 s: es redundante en una actualización (ensure_runtime_layout corre en cada arranque) y una segunda instancia junto a la viva purgaría el Temp de la que está trabajando. Un bug que solo aparecía fuera del camino feliz: con `set -euo pipefail`, un `$(pgrep ... | head -1)` sin resultados hace fallar la sustitución y `set -e` mataba el script en silencio — justo en el caso "el proceso no volvió", que es el que había que manejar. Por eso el rollback no se ejecutaba nunca.
This commit is contained in:
28
BUILD.md
28
BUILD.md
@@ -153,15 +153,33 @@ credenciales. Lo de abajo es el camino manual y lo que el PANEL ejecuta por dent
|
||||
```bash
|
||||
tar xzf CloudRestoreAS-<version>-linux-x86_64.tar.gz && cd CloudRestoreAS
|
||||
|
||||
sudo ./install.sh --service # servicio systemd 24/7 headless (recomendado en servidor)
|
||||
./install.sh --desktop # autostart .desktop (requiere sesión gráfica)
|
||||
./install.sh # solo instala + bootstrap; lo corres a mano
|
||||
sudo ./install.sh --service # servicio systemd 24/7 headless (recomendado en servidor)
|
||||
./install.sh --user-service # 24/7 SIN privilegios: unit de systemd de usuario
|
||||
./install.sh --update-in-place # actualiza una instalación existente SIN privilegios
|
||||
./install.sh --desktop # autostart .desktop (requiere sesión gráfica)
|
||||
./install.sh # solo instala + bootstrap; lo corres a mano
|
||||
./install.sh --help
|
||||
```
|
||||
Variables: `PREFIX=/opt/cloudrestoreas` (destino), `SERVICE_USER=<usuario>` (usuario del servicio).
|
||||
|
||||
Detiene el servicio antes de reemplazar el binario (un ELF en ejecución da `ETXTBSY`) y lo
|
||||
vuelve a levantar si estaba activo.
|
||||
Detiene el servicio antes de reemplazar el binario en los modos de servicio, para que el apagado
|
||||
sea ordenado. **No** porque la copia lo exija: `install` desvincula el destino antes de crearlo —a
|
||||
diferencia de `cp`, que abre con `O_TRUNC` y sí da `ETXTBSY`—, y por eso `make install` funciona
|
||||
sobre binarios en ejecución. Comprobado. Lo que hace falta para reemplazar el binario es permiso de
|
||||
escritura en el **directorio**, no en el archivo. (Esta nota decía lo contrario y mandó a más de
|
||||
uno por la pista equivocada al diagnosticar un `EACCES`.)
|
||||
|
||||
### Sin privilegios
|
||||
|
||||
`--user-service` instala bajo el home con un unit de systemd **de usuario** (lingering, y si el
|
||||
destino no lo permite, `@reboot` en el crontab del usuario más un vigilante). Sirve para
|
||||
instalaciones nuevas donde nunca vas a tener root.
|
||||
|
||||
`--update-in-place` actualiza una instalación **que ya existe**, dejando su unit intacto: solo
|
||||
reemplaza el binario y señaliza al proceso para que `Restart=always` lo relevante. Exige que el
|
||||
directorio sea escribible por la cuenta, que el unit corra con ese mismo usuario y que tenga
|
||||
`Restart=always`. Se niega si hay una restauración en curso (sale con **75**, `EX_TEMPFAIL`) y
|
||||
**revierte al binario anterior** si el nuevo no arranca.
|
||||
|
||||
Servicio systemd:
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user