fix(node): make environment progress readable from the desktop

This commit is contained in:
DCCONSTRUCTIONS
2026-09-05 19:54:08 +03:00
parent 59e14c5bc0
commit 9062126913
6 changed files with 43 additions and 4 deletions
@@ -1,6 +1,6 @@
{
"schema": "missioncore.node.environment/v1",
"revision": "ubuntu-24.04-amd64/1",
"revision": "ubuntu-24.04-amd64/2",
"steps": [
{"id":"platform","label":"Проверка системы","description":"Операционная система и архитектура БК","requires":[]},
{"id":"packages","label":"Установка системных пакетов","description":"OpenSSH Server и зависимости окружения","requires":["platform"]},
+1 -1
View File
@@ -13,7 +13,7 @@ import tarfile
ROOT = Path(__file__).resolve().parents[1]
VERSION = "0.4.0"
VERSION = "0.4.1"
BRAND_SHA256 = "8bfee8ca9f98e0db48d98aae3af4b32493b8593e18b064a0239d513d824182af"
@@ -38,10 +38,16 @@ def command(argv, *, timeout=15):
def trusted_directory(path, mode=0o755):
created = not path.exists() and not path.is_symlink()
path.mkdir(mode=mode, parents=True, exist_ok=True)
info = path.lstat()
if not stat.S_ISDIR(info.st_mode) or info.st_uid != 0 or info.st_mode & 0o022:
raise SetupError("Каталог настройки имеет неподходящие права. Переустановите пакет Node через интерфейс системы.")
# umask 0077 must protect working files, but this nonsensitive report and
# newly created configuration directories must be traversable by readers.
# The only existing directory repaired here is our dedicated report store.
if created or path == STATE:
path.chmod(mode)
def publish(path, data):
@@ -1,6 +1,8 @@
"""Workflow acceptance boundaries without touching the host OS or credentials."""
import copy
import json
import os
from types import SimpleNamespace
from pathlib import Path
import subprocess
import tempfile
@@ -98,6 +100,23 @@ class EnvironmentWorkflowTests(unittest.TestCase):
self.assertTrue(target.is_symlink())
self.assertEqual(template.read_bytes(), b'owned')
def test_restrictive_umask_does_not_hide_report_and_existing_report_dir_is_repaired(self):
with tempfile.TemporaryDirectory() as directory:
target = Path(directory)/'report'
with patch.object(helper, 'STATE', target), patch.object(Path, 'lstat', autospec=True, side_effect=lambda path: SimpleNamespace(st_uid=0, st_mode=os.stat(path).st_mode)):
previous = os.umask(0o077)
try:
helper.trusted_directory(target)
self.assertEqual(target.stat().st_mode & 0o777, 0o755)
target.chmod(0o700)
helper.trusted_directory(target)
self.assertEqual(target.stat().st_mode & 0o777, 0o755)
foreign = Path(directory)/'foreign'
foreign.mkdir(mode=0o700)
helper.trusted_directory(foreign)
self.assertEqual(foreign.stat().st_mode & 0o777, 0o700)
finally: os.umask(previous)
def test_delayed_service_start_retries_only_readiness(self):
with patch.object(helper, 'probe_node', side_effect=[OSError('not listening'), {}]) as read, patch.object(helper.time, 'sleep'), patch.object(helper, 'command') as mutation:
helper.wait_for_node()
+2 -2
View File
@@ -14,11 +14,11 @@ export function EnvironmentView({environment,failure,success}: {environment:Retu
return <div className="node-content">
<SettingsCard title="Настройка окружения" description="Установка и проверка компонентов для работы с бортовым компьютером.">
<p className="node-note">Сконфигурируйте окружение при первом запуске или повторите проверку после изменений. Приложение установит нужные пакеты, настроит службы и получит системные сведения. Вход в частную сеть и доверенный SSH-ключ подтверждаются ниже.</p>
<Button onClick={start} disabled={running||!value||!environmentSetupAvailable()}>{running?"Настраиваем…":value?.run?"Проверить и сконфигурировать":"Сконфигурировать"}</Button>
<Button onClick={start} disabled={running||!value||!environmentSetupAvailable()}>{running?"Настраиваем…":"Сконфигурировать"}</Button>
{!environmentSetupAvailable()&&<p className="node-note">Настройка запускается из установленного приложения на БК.</p>}
{pending&&(!value?.run||value.run.state!=="running")&&<ActivityIndicator label="Подтвердите действие в системном окне. Ожидаем начало настройки…" />}
{!value?loading?<ActivityIndicator label="Получаем этапы настройки" />:<p className="node-note">Сведения о настройке недоступны. Обновите страницу.</p>:<>
{!value.available&&<p className="node-note">Не удалось прочитать результат предыдущей настройки. Запустите настройку, чтобы заново проверить окружение.</p>}
{!value.available&&<p className="node-note">Журнал предыдущего запуска недоступен. Нажмите «Сконфигурировать» приложение восстановит доступ и выполнит этапы заново.</p>}
<ResourceList aria-label="Этапы настройки окружения">{value.profile.steps.map(specification=>{
const step=value.run?.steps.find(item=>item.id===specification.id);
const state=step?.state??"pending";
@@ -258,3 +258,17 @@ Web API читает состояние, но не запускает приви
SSH-формы и состояния Tailscale. Чистая установка, перезагрузка и полный
GUI upgrade/remove остаются отдельными проверками P1; отладочный вызов
helper по SSH не заменяет ни одну из них.
### Исправление отчёта настройки 0.4.1
Первый запуск штатной кнопкой на физическом БК выполнил семь системных этапов:
сеть (три интерфейса), USB (13 системных объектов), SSH и Tailscale проверены.
В UI обнаружен дефект 0.4.0: umask 0077 делал каталог несекретного журнала 0700,
поэтому непривилегированные служба и окно не могли читать результат.
В профиль /2 включено явное создание каталога отчёта с правами 0755 и ремонт
его прежнего режима; файлы с ключами и чужие каталоги этим не затрагиваются.
Повторный запуск той же кнопкой выполняет ремонт. Сообщение теперь называет
точную кнопку и ожидаемое действие. Тест проверяет restrictive umask, повторный
ремонт и сохранение прав чужого каталога. Физическая GUI-проверка исправленной
версии фиксируется по наблюдаемому результату в отчёте Ops.