Документация и runtime: полный production-fix по fix6 без legacy
This commit is contained in:
+13
-101
@@ -1,111 +1,23 @@
|
||||
# reboot-scheduler
|
||||
# awg2-reboot-scheduler
|
||||
|
||||
`reboot-scheduler` — небольшой `systemd`-сервис на Rust, который раз в запуск планирует перезагрузку в случайную секунду окна **[01:00, 02:00) UTC**, затем запрашивает reboot через `systemctl --no-block reboot`.
|
||||
`awg2-reboot-scheduler` — production auxiliary binary в составе пакета AWG2 Harness.
|
||||
|
||||
## Состав репозитория
|
||||
Он читает общий конфиг [`/etc/awg2-harness/config.toml`](../config.example.toml), выбирает случайный момент в окне reboot и запрашивает перезагрузку через `systemctl --no-block reboot`.
|
||||
|
||||
```text
|
||||
reboot-scheduler/
|
||||
├── src/
|
||||
│ └── main.rs
|
||||
├── Cargo.toml
|
||||
├── Cargo.lock
|
||||
├── reboot-scheduler.service
|
||||
├── .gitignore
|
||||
└── README.md
|
||||
```
|
||||
## Production model
|
||||
|
||||
## Требования
|
||||
- binary: `/usr/sbin/awg2-reboot-scheduler`
|
||||
- unit: [`/lib/systemd/system/awg2-reboot-scheduler.service`](../awg2-reboot-scheduler.service)
|
||||
- config: `/etc/awg2-harness/config.toml`
|
||||
- runtime plan file: `/run/awg2-harness/reboot.plan`
|
||||
|
||||
- Linux x86_64
|
||||
- `systemd`
|
||||
- Rust toolchain (cargo + rustc)
|
||||
- Поддерживаемые дистрибутивы: Debian 12/13 (и совместимые)
|
||||
Сервис ставится и управляется общим packaging flow [`tools/build.sh`](../tools/build.sh), вручную в `/opt/...` ничего не раскладывается.
|
||||
|
||||
## Сборка
|
||||
## Проверка на host
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
awg2-reboot-scheduler --config /etc/awg2-harness/config.toml
|
||||
systemctl status awg2-reboot-scheduler
|
||||
journalctl -u awg2-reboot-scheduler -f
|
||||
```
|
||||
|
||||
Итоговый бинарь:
|
||||
|
||||
```text
|
||||
target/release/reboot-scheduler
|
||||
```
|
||||
|
||||
По умолчанию release-сборка даёт **динамически связанный** glibc-бинарь. Это штатное поведение.
|
||||
|
||||
## Установка
|
||||
|
||||
Команды выполняются от root (или через `sudo`).
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/reboot-scheduler
|
||||
cp target/release/reboot-scheduler /opt/reboot-scheduler/reboot-scheduler
|
||||
chmod 755 /opt/reboot-scheduler/reboot-scheduler
|
||||
|
||||
cp reboot-scheduler.service /etc/systemd/system/reboot-scheduler.service
|
||||
chmod 644 /etc/systemd/system/reboot-scheduler.service
|
||||
|
||||
systemd-analyze verify /etc/systemd/system/reboot-scheduler.service
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now reboot-scheduler.service
|
||||
```
|
||||
|
||||
## Поведение сервиса
|
||||
|
||||
1. На старте вычисляет целевой timestamp в окне **(01:00, 02:00) UTC**.
|
||||
2. Ожидает наступления времени, проверяя раз в 30 секунд.
|
||||
3. Вызывает `systemctl --no-block reboot`.
|
||||
4. Завершается:
|
||||
- кодом `0`, если reboot-запрос принят;
|
||||
- кодом `1`, если команда завершилась ошибкой или не запустилась.
|
||||
|
||||
Это важно для корректной работы `Restart=on-failure` в unit-файле.
|
||||
|
||||
## Логирование
|
||||
|
||||
Сервис не пишет в собственные файлы. Логи идут в `journald` (stdout/stderr):
|
||||
|
||||
```bash
|
||||
journalctl -u reboot-scheduler -f
|
||||
```
|
||||
|
||||
Формат сообщений:
|
||||
|
||||
```text
|
||||
[YYYY-MM-DD HH:MM:SS UTC] Сообщение
|
||||
```
|
||||
|
||||
## Управление
|
||||
|
||||
```bash
|
||||
systemctl status reboot-scheduler
|
||||
systemctl restart reboot-scheduler
|
||||
systemctl stop reboot-scheduler
|
||||
systemctl disable reboot-scheduler
|
||||
```
|
||||
|
||||
## Проверки качества
|
||||
|
||||
Рекомендуемый локальный прогон перед релизом:
|
||||
|
||||
```bash
|
||||
cargo fmt --check
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
cargo test --release
|
||||
cargo build --release
|
||||
file target/release/reboot-scheduler
|
||||
ldd target/release/reboot-scheduler
|
||||
```
|
||||
|
||||
## Удаление
|
||||
|
||||
```bash
|
||||
systemctl stop reboot-scheduler
|
||||
systemctl disable reboot-scheduler
|
||||
rm -f /etc/systemd/system/reboot-scheduler.service
|
||||
rm -rf /opt/reboot-scheduler
|
||||
systemctl daemon-reload
|
||||
```
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
[Unit]
|
||||
Description=Daily random reboot scheduler (01:00-02:00 UTC)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
ExecStart=/opt/reboot-scheduler/reboot-scheduler
|
||||
Restart=on-failure
|
||||
RestartSec=60
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
# Базовое hardening
|
||||
CapabilityBoundingSet=CAP_SYS_BOOT
|
||||
AmbientCapabilities=CAP_SYS_BOOT
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -96,6 +96,9 @@ fn persist_plan(planned: NaiveDateTime) {
|
||||
fn next_reboot_time(start_hhmm: &str, end_hhmm: &str) -> anyhow::Result<NaiveDateTime> {
|
||||
let start = parse_hhmm(start_hhmm)?;
|
||||
let end = parse_hhmm(end_hhmm)?;
|
||||
if start == end {
|
||||
anyhow::bail!("reboot window start and end must differ");
|
||||
}
|
||||
|
||||
let now = Utc::now().naive_utc();
|
||||
let now_t = NaiveTime::from_hms_opt(now.hour(), now.minute(), now.second())
|
||||
@@ -233,5 +236,11 @@ mod tests {
|
||||
assert_eq!(s, now);
|
||||
assert_eq!(e.format("%Y-%m-%d %H:%M:%S").to_string(), "2026-01-11 02:00:00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_window_boundaries_rejected() {
|
||||
let err = next_reboot_time("01:00", "01:00").err();
|
||||
assert!(err.is_some());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user