docs: what OTA changes in the system (docs/ota.md)
bt-update runs as root and installs helpers, sudoers, systemd units and config; none of it is rolled back. Documents the order of stage steps, the 'old bt-update runs the OTA' pitfall and how to add a system change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+63
@@ -0,0 +1,63 @@
|
||||
# OTA: что обновление может менять в системе
|
||||
|
||||
Изначально OTA задумывалась как замена только приложения (бинарник, веб, коллектор). На практике `bt-update` выполняется **от root** и уже меняет системные файлы: хелперы, sudoers, юниты systemd, конфиги systemd. Здесь описано, что именно он делает, в каком порядке и как добавить новую системную правку.
|
||||
|
||||
## Как запускается
|
||||
|
||||
1. Веб (пользователь `buttontask`) принимает пакет и вызывает `sudo -n /opt/buttontask/scripts/bt-update stage <pkg>`. Это разрешено правилом в `/etc/sudoers.d/buttontask`.
|
||||
2. `bt-update` перезапускает сам себя через `systemd-run` в отдельном временном юните `buttontask-update-<ts>`. Поэтому он переживает перезапуск `buttontask-web` посреди обновления.
|
||||
3. Прогресс пишется в `run/update-status.json`, подробный лог в `run/update.log`.
|
||||
|
||||
## Порядок `stage`
|
||||
|
||||
| Шаг | Что делается | Куда пишет |
|
||||
|---|---|---|
|
||||
| 1. распаковка | пакет → `versions/<ver>`, проверка `manifest.json` | `/opt/buttontask/versions/` |
|
||||
| 2. `sync_helpers` | все `webconfig/scripts/bt-*` → root-owned хелперы; sudoers ставится только если прошёл `visudo -c` | `/opt/buttontask/scripts/`, `/etc/sudoers.d/buttontask` |
|
||||
| 3. коллектор | `buttontask-collector.service`, enable/disable по `collectorEnabled` | `/etc/systemd/system/` |
|
||||
| 4. bluetooth | drop-in `Restart=always` для `bluetooth.service` | `/etc/systemd/system/bluetooth.service.d/override.conf` |
|
||||
| 5. `sync_system` | `buttontask.service`, `buttontask-displaywatch.service`, аппаратный вотчдог; `daemon-reload` или `daemon-reexec`, enable/restart displaywatch | `/etc/systemd/system/`, `/etc/systemd/system.conf.d/` |
|
||||
| 6. переключение | `current` → новая версия, перезапуск сервисов | |
|
||||
| 7. health-check | `/healthz`, свежий `run/qt.alive`, `buttontask` active (30 с) | |
|
||||
| 8. откат | при провале `current` → предыдущая версия, перезапуск | |
|
||||
|
||||
Подкоманды для ручного запуска:
|
||||
|
||||
```sh
|
||||
sudo /opt/buttontask/scripts/bt-update sync-helpers # шаг 2 из текущей версии
|
||||
sudo /opt/buttontask/scripts/bt-update sync-system # шаг 5 из текущей версии
|
||||
```
|
||||
|
||||
## Важные ограничения
|
||||
|
||||
### 1. OTA выполняет **старый** `bt-update`
|
||||
|
||||
Обновление запускает `bt-update`, который уже установлен на устройстве, а не тот, что лежит в пакете. Новый `bt-update` приезжает на шаге 2 и работает только **со следующей** OTA. Значит, системную правку, добавленную в `bt-update`, первая OTA не применит.
|
||||
|
||||
Как это обойти (сделано для вотчдога): веб при каждом старте вызывает `sudo bt-update sync-system` (`sync_system_units()` в `webconfig/system_info.py`). На шаге 6 веб перезапускается уже из новой версии, вызывает новый `bt-update` (его установил шаг 2), и правка применяется в ту же OTA. Поэтому `sync-system` должен быть **идемпотентным**: без изменений он ничего не делает (`install_if_changed` + `cmp`).
|
||||
|
||||
### 2. Системные правки **не откатываются**
|
||||
|
||||
Шаги 2–5 выполняются до health-check, и откат возвращает только симлинк `current`. Юниты, sudoers и конфиги остаются от новой версии. Отсюда правило: **любая системная правка должна работать и со старыми версиями приложения.** Пример: `buttontask.service` не задаёт `WatchdogSec=`, таймаут включает сам бинарник через `WATCHDOG_USEC`. Иначе старые версии без пингов убивались бы каждые 30 секунд.
|
||||
|
||||
### 3. Изменённый юнит не применяется к уже запущенному процессу
|
||||
|
||||
`daemon-reload` обновляет юнит, но переменные окружения (например, `NOTIFY_SOCKET`) процесс получает только при старте. Если юнит поменялся после перезапуска на шаге 6 (это случай из пункта 1), новые настройки заработают со следующего рестарта сервиса или перезагрузки.
|
||||
|
||||
### 4. Чего OTA не умеет
|
||||
|
||||
- **apt/deb-пакеты.** Для них нужен полный deploy-бандл (`winDeployScripts/`, `device-install.sh`).
|
||||
- **Произвольные одноразовые скрипты-миграции.** Отдельного механизма нет: каждая правка вписывается в `bt-update` руками (см. ниже).
|
||||
- **Перезагрузку** после установки. Если она нужна, это ручной шаг.
|
||||
|
||||
## Как добавить новую системную правку
|
||||
|
||||
1. Положите файл в пакет, в `webconfig/…` (всё из `webconfig/` попадает в OTA через `make-package.sh`).
|
||||
2. В `bt-update` в `sync_system` добавьте `install_if_changed <src> <dst>` и нужный `reload`/`reexec`/`enable`. Не добавляйте новый шаг прямо в `cmd_stage`: тогда правка не применится при первой OTA (пункт 1).
|
||||
3. Проверьте, что правка совместима со старыми версиями приложения (пункт 2).
|
||||
4. Повторите то же в `winDeployScripts/device-install.sh` для чистой установки. Если добавили новый хелпер `bt-*`, впишите его в списки в `device-install.sh` и `make-adb-bundle.ps1`.
|
||||
5. Новые типы файлов (`*.conf` и т.п.) пропишите в `.gitattributes` с `eol=lf`.
|
||||
|
||||
## Безопасность
|
||||
|
||||
Пакет OTA фактически исполняется от root: хелперы, sudoers и юниты из него ставятся без подписи. От подмены защищают только пароль веб-конфигуратора и (необязательная) сверка sha256 при загрузке. Доступ к вкладке «Обновление» равен root-доступу к устройству.
|
||||
Reference in New Issue
Block a user