Skip to content

CLOUD-599 Rebuild a damaged provider config instead of exiting - #77

Draft
smintank wants to merge 29 commits into
mainfrom
feature/cloud-599_self-healing-configs
Draft

CLOUD-599 Rebuild a damaged provider config instead of exiting#77
smintank wants to merge 29 commits into
mainfrom
feature/cloud-599_self-healing-configs

Conversation

@smintank

@smintank smintank commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Что происходит; кому и зачем нужно:

CLOUD-599 (инцидент CLOUD-592). После аварийной перезагрузки конфиг провайдера
/etc/wb-cloud-agent/providers/<провайдер>/wb-cloud-agent.conf оказался пустым, агент падал
со статусом 6, и контроллер выпадал из облака до ручного вмешательства. Теперь агент чинит
такой файл сам.


Что поменялось для пользователей:

  • Отсутствующий, пустой или битый конфиг провайдера восстанавливается: сначала из копии
    последнего рабочего конфига в /var/lib/wb-cloud-agent/providers/<провайдер>/, затем из
    поставляемого /etc/wb-cloud-agent.conf с адресом облака из имени каталога провайдера.
  • Провайдер, добавленный с --name, не переключается на угаданный адрес: файл остаётся как есть,
    агент не ходит в облако, пишет WARNING и «Broken configuration» в MQTT-контрол status,
    и подхватывает файл сразу после восстановления. Так же он ведёт себя, если восстановленный
    конфиг не удаётся записать — например, когда /etc смонтирован только для чтения.
  • show-providers показывает такого провайдера как «Broken configuration», а не пропускает его.
  • Битый файл сохраняется рядом как wb-cloud-agent.conf.broken-<дата>, все восстановления видны
    в journalctl -u wb-cloud-agent@<провайдер>.
  • Конфиги пишутся атомарно, так что обрыв питания больше не оставляет обрезанный файл.
  • Пустой или нечитаемый frpc.conf удаляется при старте, чтобы frpc не перезапускался по кругу.
  • Условие запуска юнита проверяет каталог провайдера, а не маску *.conf, иначе отсутствующий
    конфиг нельзя было бы восстановить; check-certs.sh пропускает правку ATECC на пустом файле.

Как проверял/а:

  • Юнит-тесты на каждый сценарий восстановления (пустой файл, битый JSON, отсутствующий файл,
    нечитаемый файл, недоступный для записи каталог, --name-провайдер, здоровый конфиг
    не переписывается) на tmp_path.

  • Полный прогон: black, isort, pylint 10.00/10, pytest — 236 тестов, покрытие 94.0%.

  • Проверено на тестовых контроллерах WB6/WB7/WB8 (сборка 1.8.3): пустой, битый и отсутствующий конфиг восстанавливаются на всех трёх, после восстановления в журнале остаются INFO-строки в настроенном формате; при удержании контрол status показывает «Broken configuration» уже через цикл и держит его все 150 с, сетевой цикл paho жив, запросов в облако нет.

Denis Smagin added 3 commits September 5, 2026 00:05
A missing, empty or unparsable provider config used to stop the service with
status 6 until someone fixed the file by hand. It is now rebuilt from the last
known good copy kept in /var/lib, or from the packaged default plus the cloud
host name taken from the provider directory. A provider added under a custom
--name has no trustworthy source, so its config is left untouched and reported
instead of being repointed at a host that does not exist. Config files are
written atomically, and a non-empty broken file is kept as .broken-<timestamp>.
The daemon now starts its MQTT client first, publishes the broken-configuration
state and re-reads the config every cycle instead of making cloud requests, so
it recovers as soon as the file is restored. The unit condition matches the
provider directory rather than a config glob, otherwise a missing config could
never be healed, and check-certs.sh skips its ATECC fixup on an empty file
instead of failing ExecStartPre. An empty or unreadable frpc.conf is removed at
start so frpc stops restarting until the cloud sends a new one.
@coveralls

coveralls commented Sep 5, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 8

Coverage increased (+0.09%) to 97.566%

Details

  • Coverage increased (+0.09%) from the base build.
  • Patch coverage: 16 uncovered changes across 5 files (654 of 670 lines covered, 97.61%).
  • 1 coverage regression across 1 file.

Uncovered Changes

File Changed Covered %
wb/cloud_agent/services/tunnel.py 17 12 70.59%
tests/conftest.py 68 64 94.12%
wb/cloud_agent/commands.py 25 22 88.0%
wb/cloud_agent/utils.py 56 53 94.64%
wb/cloud_agent/settings.py 85 84 98.82%
Total (14 files) 670 654 97.61%

Coverage Regressions

1 previously-covered line in 1 file lost coverage.

File Lines Losing Coverage Coverage
tests/test_providers_tools.py 1 96.55%

Coverage Stats

Coverage Status
Relevant Lines: 2876
Covered Lines: 2806
Line Coverage: 97.57%
Coverage Strength: 1.95 hits per line

💛 - Coveralls

Denis Smagin added 26 commits September 5, 2026 00:25
In the hold state the settings fall back to the packaged defaults, so the
retained value would reach the cloud with the wrong client certificate key.
The daemon subscribes once the hold ends.
paho runs on_message in the network loop thread, so an exception escaping
the handler stops publishes and keepalives for the rest of the run.
The hold loop already restarts a stopped loop before publishing; without
the same check the mainline status updates are lost once it dies.
AppSettings reads the provider config from PROVIDERS_CONF_DIR, so the fixture
let the host decide config_error: where that file is damaged the hold tests
spin in wait_for_usable_config instead of reporting a result.
paho holds MQTTMessage.topic as bytes and decodes it on read, so reading it
to log a handler failure raised out of the guard that exists to keep the
network loop alive.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants