# Tareas programadas

Comandos con horario en el VPS mediante un servicio y un timer de systemd, con atajos de minutos, ejecución manual y logs.

Fuente: https://ardumaker.com/docs/cron/

- Categoría: Servidores e infraestructura
- Estado: Disponible
- Requisitos: Un VPS de la empresa encendido, con systemd; Para crear, editar, controlar o borrar: ser admin de la empresa; Para listar y ver: ser miembro con acceso al VPS
- Herramientas MCP (Solo leen): `listar_tareas_vps`, `ver_tarea_vps`, `ver_logs_tarea_vps`
- Herramientas MCP (Escriben o modifican): `crear_tarea_vps`, `editar_tarea_vps`, `controlar_tarea_vps`
- Herramientas MCP (Destructivas): `eliminar_tarea_vps`

## ¿Qué permite hacer?

- Correr un comando con un horario: cada N minutos, a una hora fija, ciertos días o con los atajos `hourly`, `daily` y `weekly`.
- Ejecutar la tarea una vez en el momento, o cortar una ejecución en curso.
- Cambiar el comando o el horario de una tarea existente.
- Ver la próxima y la última ejecución, y leer los logs del comando y del timer.

## ¿Cuándo conviene usarlo?

- Limpiezas, exportaciones o sincronizaciones periódicas que no son de una app de ArduMaker.
- Un script que tiene que correr aunque el VPS haya estado apagado a esa hora (se ejecuta al encender).

> **INFO:** No se usa `crontab`: cada tarea es un par servicio + timer de systemd. El trabajo programado de una app de Git va como proceso `timer` en su configuración de deploy, no acá.

## ¿Cómo funciona en ArduMaker?

### Validaciones antes de tocar el VPS

- **Nombre**: las mismas reglas que un servicio systemd (`nombre_invalido`).
- **Comando**: una sola línea, hasta 4000 caracteres, con comillas cerradas (`comando_invalido`). Si usa tuberías, `&&`, variables, redirecciones o comandos internos de la shell, se envuelve solo en `/bin/sh -c "…"` con el escape necesario.
- **Horario**: una expresión `OnCalendar` (`daily`, `*-*-* 02:00:00`, `Mon..Fri *-*-* 10:00`, `*-*-* *:0/5:00 Europe/Madrid`). Atajos: `5` o `*/5` significan cada 5 minutos, y `1` cada minuto.
- El error de validación dice qué campo falló en `data.fields`.

### Alta

1. Se guardan copias de la unidad y del timer si ya existían.
2. Se escribe `/etc/systemd/system/<nombre>.service` (`Type=oneshot`, salida al journal con el nombre de la tarea) y `/etc/systemd/system/<nombre>.timer` (`OnCalendar`, `Persistent=true`).
3. `daemon-reload`, se arranca el timer y se habilita al inicio. Si un paso falla, se borra lo nuevo y se restauran las copias.
4. Con `start=true` el comando corre además una vez en ese momento (si esa ejecución falla, la tarea igual queda creada).

### Editar, controlar y borrar

- **Editar** lee el comando y el horario actuales, **borra la tarea y la vuelve a crear** con lo nuevo. La tarea editada queda habilitada.
- **`start`** corre el comando ahora; **`stop`** corta esa ejecución (no detiene el timer).
- **`enable`** y **`disable`** cambian si el timer arranca con el sistema. `disable` no detiene un timer que ya está activo: sigue disparando hasta el próximo reinicio del VPS.
- **Borrar** detiene y deshabilita el timer, quita los dos archivos y recarga systemd. No hay nombres protegidos.

## ¿Qué cambia realmente?

- `/etc/systemd/system/<nombre>.service` y `/etc/systemd/system/<nombre>.timer`, con permisos 644.
- El comando corre como `root`, y su salida queda en el journal identificada con el nombre de la tarea.
- Una entrada en el registro de actividad del VPS por cada alta, edición, acción y borrado.

```ini
# <nombre>.timer
[Unit]
Description=Timer for task: limpieza

[Timer]
OnCalendar=*-*-* 02:00:00
Unit=limpieza.service
Persistent=true

[Install]
WantedBy=timers.target
```

## Esquema

**Una tarea programada**

```text
Timer (OnCalendar dispara a la hora indicada)
  ↓
Servicio oneshot (corre el comando una vez y termina)
  ↓
journal (salida y errores con el nombre de la tarea)
```

Con `Persistent=true`, si el VPS estaba apagado a la hora prevista, la tarea corre al encender.

## Resultado esperado

- Crear, editar y controlar devuelven `timer_status`, `is_enabled` y, salvo al crear, `next_run`.
- `ver_tarea_vps` muestra el contenido del servicio y del timer, el estado, la próxima y la última ejecución.
- `ver_logs_tarea_vps` con `log_type: "service"` muestra la salida del comando; con `"timer"`, la del timer.

```bash
systemctl list-timers --no-pager
journalctl -u limpieza.service -n 50 --no-pager
```

## Errores y resolución

| Respuesta | Causa | Qué hacer |
| --- | --- | --- |
| 400 `nombre_invalido` en `schedule` | El horario trae caracteres que no son de una expresión OnCalendar (comillas, `$`, `;`) | Corregir el horario |
| 400 `comando_invalido` | Salto de línea, comillas sin cerrar o más de 4000 caracteres | Llevarlo a un script y llamar al script |
| 400 con `data.cause` | El VPS no pudo escribir o activar la tarea; `cause` dice en qué paso | Revisar el paso indicado |
| 404 `Task not found` | La tarea no existe | Listar primero |
| 403 | Solo un admin de la empresa puede crear, editar, controlar o borrar | Pedirlo a un admin |
| La tarea sigue corriendo después de `disable` | `disable` solo afecta el arranque | Borrar la tarea si tiene que dejar de correr ya |

> **PRECAUCION:** Editar borra y vuelve a crear la tarea: si la recreación falla (por ejemplo, el VPS deja de contestar en el medio), la tarea queda borrada. Conviene guardar el comando y el horario antes de editar.

Crear con el mismo nombre reemplaza la tarea; reintentar tras un corte es seguro con la misma `idempotency_key`. `controlar_tarea_vps` con `start` vuelve a correr el comando cada vez.

## Relación con MCP

Sí, en los perfiles `/mcp` y `/mcp/vps`.

| Herramienta | Tipo | Notas |
| --- | --- | --- |
| `listar_tareas_vps`, `ver_tarea_vps` | Lectura | Miembro |
| `ver_logs_tarea_vps` | Lectura | Contenido no confiable |
| `crear_tarea_vps` | Escritura | Admin; acepta `idempotency_key` |
| `editar_tarea_vps` | Escritura | Admin; borra y recrea |
| `controlar_tarea_vps` | Escritura | Admin; `start`, `stop`, `enable`, `disable` (sin `restart`) |
| `eliminar_tarea_vps` | Destructiva | Admin; sin nombres protegidos |

Referencia en [/mcp/docs](https://ardumaker.com/mcp/docs) y ejemplos en [/mcp/servicios](https://ardumaker.com/mcp/servicios).

## Funciones relacionadas

- [Servicios systemd](https://ardumaker.com/docs/systemd/)
- [Copias de seguridad](https://ardumaker.com/docs/backups/)
- [Monitorización](https://ardumaker.com/docs/monitorizacion/)
- [Servidores VPS](https://ardumaker.com/docs/vps/)
