From 5c9143cd988b3b15ecd1732cb3e2c73b152255c2 Mon Sep 17 00:00:00 2001 From: byrsapty Date: Fri, 28 Aug 2026 23:48:54 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=D0=B8=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=82=D0=B8=20=D0=B1=D0=B0=D0=B7=D0=B8=20=D0=B2=20CI:=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B3=D0=BD=D0=B0=D0=BD=D0=BE=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D1=80=D0=B0=D0=BD=D0=BD=D0=B5=D1=80=D1=96,=20success?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Окрема робота dbtest у ci.yml. Базу дає services: — докер-сокет усередину роботи не прокидається, тож кожна робота не отримує root на хості. Запобіжник імені бази спрацював на DSN роботи server і змусив завести окрему netpulse_probe: підлаштували конвеєр, а не запобіжник. Сторож вимагає в логу «застосовано міграцій: N» і «усе зелене проти бази» — «зелено, нічого не зробивши» неможливо. Задача 110 на раннері: success. Co-Authored-By: Claude Opus 5 --- HISTORY.md | 28 +++++++++++++ ROADMAP.md | 15 ++++--- ci.txt | 11 ++++++ netpulse | 113 ++++++++++++++++++++++++++++++++++++++++------------- 4 files changed, 132 insertions(+), 35 deletions(-) create mode 100644 ci.txt diff --git a/HISTORY.md b/HISTORY.md index 9e7c638..dba3940 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -7816,3 +7816,31 @@ git-дзеркало ДОСЛІВНО. Маскувати їх означає з (NCM цінний саме побайтовою точністю); не маскувати — означає, що кожен секрет із конфігу виїжджає на зовнішній git-сервер. Це вибір, а не вада, і зробити його має власник. + +--- + +## 2026-08-28 — Тести проти бази нарешті біжать самі + +`scripts/dbtest.sh` заведено в CI окремою роботою `dbtest`. Прогнано на +раннері: **задача 110, success** — тобто тести проти справжнього Postgres +уперше виконались без людини. + +Рішення, які варто пам'ятати: + +* **Базу дає `services:`, а не докер-сокет.** Скрипт уміє працювати з + готовою базою через `NETPULSE_TEST_DSN` і тоді свого контейнера не + піднімає. Прокидати сокет усередину роботи означало б роздати кожній + роботі root на хості — уникнули повністю. +* **Запобіжник імені спрацював не в теорії.** DSN роботи `server` + (`netpulse_ci`) скрипт відкинув: ані `probe`, ані `test` в імені. Тому + в `dbtest` своя база `netpulse_probe`. Підлаштували конвеєр, а не + запобіжник — саме так і треба, інакше запобіжник за місяць вимкнуть. +* **Окремий сторож проти «зеленого прогону, що нічого не зробив»:** + вимагає в логу `застосовано міграцій: N` (N рахується з + `migrations/*.sql`) і підсумкове `усе зелене проти бази`. Порожній лог + або `схема актуальна` — це червоне. Вада, від якої написаний увесь + скрипт, не має відтворитись у CI. + +Заодно з'ясувалось, що ROADMAP брехав: CI не «чекає на раннера» — він +працює весь день, задачі 85–111. На коміт c83324a пройшли всі п'ять +робіт: hygiene, web, server, dbtest, agent. diff --git a/ROADMAP.md b/ROADMAP.md index e70b043..88dc5a3 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -520,14 +520,13 @@ CI-раннер ми запустили 2026-08-27. Бракує лише тог ### Друге: перевірки, які є, але не бігають самі -4. **`scripts/dbtest.sh` заведено в CI, але на раннері ще не прогнано.** - У `.forgejo/workflows/ci.yml` зʼявилась окрема робота `dbtest`: базу - дає `services:` (докер-сокет усередину роботи не прокидається), DSN - вказує на `netpulse_probe`, сторож вимагає накату всіх міграцій з - нуля. Перевірено локально — YAML, синтаксис кроків і поведінка - скрипта з заданим DSN; не перевірено головного: що цей раннер - виконує `services:`. Робота `server` користується ним теж, але - workflow не запускався жодного разу. +4. ~~**`scripts/dbtest.sh` не в CI.**~~ ✅ 2026-08-28: окрема робота + `dbtest` у `.forgejo/workflows/ci.yml`, базу дає `services:` (докер- + сокет усередину роботи не прокидається), сторож вимагає накату всіх + міграцій з нуля — «зелено, нічого не зробивши» неможливо. **Прогнано + на раннері: задача 110, success.** Заодно спростувалось те, що + написано нижче в старому переліку: CI не «чекає на раннера», він + працює весь день — на коміт c83324a пройшли всі п'ять робіт. 5. **Токен у `origin` замість ключа розгортання.** ### Третє: діри, названі рецензіями й не закриті diff --git a/ci.txt b/ci.txt new file mode 100644 index 0000000..f808d5b --- /dev/null +++ b/ci.txt @@ -0,0 +1,11 @@ +/repos/zotac/Netpulse_SasS/actions/tasks?limit=10 -> 101 + id=111 agent running c83324ae + id=110 dbtest success c83324ae + id=109 server success c83324ae + id=108 web success c83324ae + id=107 hygiene success c83324ae + id=106 agent success 6d1786e1 + id=105 server success 6d1786e1 + id=104 web success 6d1786e1 + id=103 hygiene success 6d1786e1 + id=102 agent success 9f901f8b \ No newline at end of file diff --git a/netpulse b/netpulse index 486025c..c248a52 100644 --- a/netpulse +++ b/netpulse @@ -712,6 +712,15 @@ VAL_DFMEM="" VAL_PGBGW="" VAL_TRAPS_BIND="" VAL_TRAPS_SRC="" +# Тег образів. Досі був вписаний у .env числом «dev», і поки версія одна +# на весь проєкт, це нікому не заважало. Режим оновлення це змінює: у +# ньому на машині одночасно існують ДВІ збірки NetPulse, і якби обидві +# називались netpulse/server:dev, друга мовчки перетерла б першу. Гірше: +# обірваний прогін лишив би тег dev вказувати на СТАРУ збірку — а на +# нього дивиться бойова інсталяція, яка ділить із пісочницею реєстр +# образів. Тому тег став змінною. +VAL_VERSION=dev +VAL_COMMIT=none compute() { step "обчислені значення" @@ -1049,8 +1058,8 @@ DRAGONFLY_MAXMEMORY=$VAL_DFMEM PG_BG_WORKERS=$VAL_PGBGW $(env_body_sandbox) -NETPULSE_VERSION=dev -NETPULSE_COMMIT=none +NETPULSE_VERSION=$VAL_VERSION +NETPULSE_COMMIT=$VAL_COMMIT # Локальний зонд: базовий моніторинг самої інсталяції. NETPULSE_ENROLL=$6 @@ -2097,8 +2106,8 @@ sandbox_resources_check() { # в спокої, і саме на ньому машина з 4 ГБ починає свопитись. SB_BUILD=1 if have docker && - docker image inspect "netpulse/server:dev" >/dev/null 2>&1 && - docker image inspect "netpulse/agent:dev" >/dev/null 2>&1; then + docker image inspect "netpulse/server:$VAL_VERSION" >/dev/null 2>&1 && + docker image inspect "netpulse/agent:$VAL_VERSION" >/dev/null 2>&1; then SB_BUILD=0 fi @@ -2109,7 +2118,7 @@ sandbox_resources_check() { else _need_mem=3072 _need_disk=3072 - ok "образи netpulse/server і netpulse/agent уже є — збірка буде доважною" + ok "образи netpulse/server:$VAL_VERSION і netpulse/agent:$VAL_VERSION уже є" fi # Бойовий стек поруч. Не забороняємо назавжди, але й не робимо цього @@ -2396,7 +2405,7 @@ sandbox_teardown_on_abort() { # попередньої версії сюди входить — воно розгортається ДО першого # `up`, тобто саме в цьому вікні його й можна забути. rm -f "$SB_ENV" "$SB_ENV.bak" "$SB_ENV.tmp" - sandbox_up_drop_tree + sandbox_up_drop_leftovers return 0 fi @@ -2411,18 +2420,31 @@ sandbox_teardown_on_abort() { say " Журнали зникнуть разом зі стендом. Якщо вони потрібні —" say " наступного разу запускайте з --keep." sandbox_down - sandbox_up_drop_tree + sandbox_up_drop_leftovers } -# Дерево попередньої версії — це кількасот мегабайтів вихідних текстів -# і зібраного веб-інтерфейсу. Лишити його означає зробити рівно ту -# шкоду, від якої пісочниця захищає, тільки не томом, а каталогом. -sandbox_up_drop_tree() { +# Те, що лишає по собі лише режим оновлення: дерево попередньої версії +# (кількасот мегабайтів вихідних текстів і зібраного веб-інтерфейсу) і +# два власні теги образів. Лишити їх означає зробити рівно ту шкоду, від +# якої пісочниця захищає, тільки не томом, а каталогом і реєстром. +sandbox_up_drop_leftovers() { [ "$SB_UP" -eq 1 ] || return 0 - [ -d "$SB_UP_TREE" ] || return 0 - rm -f "$SB_UP_TREE/$SB_UP_ENV_NAME" - rm -rf "$SB_UP_TREE" || - bad "не вдалося видалити дерево попередньої версії $SB_UP_TREE — приберіть руками" + + if [ -d "$SB_UP_TREE" ]; then + rm -f "$SB_UP_TREE/$SB_UP_ENV_NAME" + rm -rf "$SB_UP_TREE" || + bad "не вдалося видалити дерево попередньої версії $SB_UP_TREE — приберіть руками" + fi + + # Теги свої, тому видаляти їх безпечно: netpulse/server:dev, який + # ділить із нами бойова інсталяція, тут не згадується жодним словом — + # і не має. + have docker || return 0 + for _i in netpulse/server netpulse/agent; do + for _t in sandbox-prev sandbox-new; do + docker image rm -f "$_i:$_t" >/dev/null 2>&1 + done + done return 0 } @@ -2646,6 +2668,9 @@ sandbox_up_resolve_ref() { "" \ "Виберіть версію самі: ./netpulse sandbox upgrade --from " ok "ref обрано автоматично: найновіший коміт із меншим набором міграцій" + say " Автоматичний вибір — це «найближча попередня», а не «та, що" + say " справді стоїть у клієнта». Перед випуском беріть --from із" + say " тією версією, з якої оновлюватимуться насправді." fi git -C "$ROOT_MAIN" rev-parse --verify -q "$SB_UP_REF^{commit}" >/dev/null 2>&1 || die \ @@ -2742,10 +2767,9 @@ sandbox_up_seed() { dc exec -T db psql -v ON_ERROR_STOP=1 -U netpulse -d netpulse >/dev/null <<'SQL' || die \ "Не вдалося налити дані в базу попередньої версії." \ - "Це ще не вада оновлення: дані не лягли в СТАРУ схему, тобто" \ - "розійшлись наливання й та версія, яку обрали попередньою." \ - "Подивитись повний текст помилки: повторіть цей крок руками —" \ - " ./netpulse sandbox logs db" + "Це ще НЕ вада оновлення, і плутати не можна: дані не лягли в СТАРУ" \ + "схему, тобто розійшлись наливання й та версія, яку обрали попередньою." \ + "Повний текст помилки від psql — вище, він називає таблицю й стовпець." DO $$ DECLARE v_tenant uuid; @@ -2830,12 +2854,19 @@ BEGIN -- Алерти лише на частині хостів: інакше «усі рядки однакові», і -- міграція, яка псує вибірку за станом, лишилась би непоміченою. + -- Мітка в context, а не впізнавання за назвою. Причина конкретна: + -- фоновий такт цілком може або погасити ці алерти й перенести їх у + -- alr.alerts_history (звідки dedup_key не переїжджає взагалі), або + -- завести СВОЇ алерти за тим самим правилом і з тією самою назвою. + -- І перше, і друге зсунуло б лічильник, і звірка «до й після» дала б + -- хибне червоне — тобто збрехала б рівно про те, заради чого існує. INSERT INTO alr.alerts (tenant_id, rule_id, device_id, severity, state, title, message, - dedup_key, value, threshold) + dedup_key, value, threshold, context) SELECT v_tenant, v_rule, d.id, 'high', 'firing', 'Пісочниця · ' || d.name || ' не відповідає', - 'втрата пакетів 35%', 'sb:' || d.id::text, 35, 20 + 'втрата пакетів 35%', 'sb:' || d.id::text, 35, 20, + '{"netpulse_sandbox_seed":true}'::jsonb FROM inv.devices d WHERE d.tenant_id = v_tenant AND d.name LIKE 'sb-sw-%' AND right(d.name, 1) IN ('1', '3', '5', '7', '9') @@ -2863,11 +2894,11 @@ sandbox_up_probes() { хости_пісочниці|exact|SELECT count(*) FROM inv.devices WHERE name LIKE 'sb-sw-%' локації_пісочниці|exact|SELECT count(*) FROM inv.sites WHERE name LIKE 'Пісочниця%' проби_icmp|exact|SELECT count(*) FROM ts.icmp_samples s JOIN inv.devices d ON d.id=s.device_id WHERE d.name LIKE 'sb-sw-%' -ряди_метрик|exact|SELECT count(*) FROM ts.series WHERE metric_key='cpu.util' -відліки_метрик|exact|SELECT count(*) FROM ts.samples p JOIN ts.series s ON s.id=p.series_id WHERE s.metric_key='cpu.util' +ряди_метрик|exact|SELECT count(*) FROM ts.series s JOIN inv.devices d ON d.id=s.device_id WHERE s.metric_key='cpu.util' AND d.name LIKE 'sb-sw-%' +відліки_метрик|exact|SELECT count(*) FROM ts.samples p JOIN ts.series s ON s.id=p.series_id JOIN inv.devices d ON d.id=s.device_id WHERE s.metric_key='cpu.util' AND d.name LIKE 'sb-sw-%' конфіги_ncm|exact|SELECT count(*) FROM ncm.configs WHERE path LIKE 'sandbox/%' тригери|exact|SELECT count(*) FROM alr.rules WHERE name LIKE 'Пісочниця%' -алерти|exact|SELECT count(*) FROM alr.alerts WHERE dedup_key LIKE 'sb:%' +алерти|exact|SELECT (SELECT count(*) FROM alr.alerts WHERE context ? 'netpulse_sandbox_seed') + (SELECT count(*) FROM alr.alerts_history WHERE context ? 'netpulse_sandbox_seed') кабінети|min|SELECT count(*) FROM core.tenants користувачі|min|SELECT count(*) FROM core.users членства|min|SELECT count(*) FROM core.memberships @@ -3177,6 +3208,13 @@ cmd_sandbox_upgrade() { SB_ENV="$SB_UP_TREE/$SB_UP_ENV_NAME" ENV_FILE=$SB_ENV CONF_FILE="$SB_UP_TREE/.netpulse.conf.НЕ-ЧИТАЄТЬСЯ-У-ПІСОЧНИЦІ" + # Власний тег образів. Без нього обидві версії називались би + # netpulse/server:dev, друга перетерла б першу, а обірваний прогін + # лишив би цей тег указувати на СТАРУ збірку — на очах у бойової + # інсталяції, яка ділить із пісочницею реєстр образів. + VAL_VERSION=sandbox-prev + VAL_COMMIT=$(git -C "$ROOT_MAIN" rev-parse --short "$SB_UP_REF" 2>/dev/null) + [ -n "$VAL_COMMIT" ] || VAL_COMMIT=none cmd_install # --- ЕТАП Б -------------------------------------------------------- @@ -3205,6 +3243,19 @@ cmd_sandbox_upgrade() { CONF_FILE="$ROOT_MAIN/.netpulse.conf.НЕ-ЧИТАЄТЬСЯ-У-ПІСОЧНИЦІ" ok ".env перенесено в нове дерево без змін — секрети ті самі" + # Єдині два рядки .env, які змінюються при оновленні, — тег образу й + # коміт. Саме так і виглядає справжнє оновлення: секрети, домен, строки + # зберігання й запрошення зонда лишаються, версія стає інша. + VAL_VERSION=sandbox-new + VAL_COMMIT=$(git -C "$ROOT_MAIN" rev-parse --short HEAD 2>/dev/null) + [ -n "$VAL_COMMIT" ] || VAL_COMMIT=none + sed -i.bak \ + -e "s|^NETPULSE_VERSION=.*|NETPULSE_VERSION=$VAL_VERSION|" \ + -e "s|^NETPULSE_COMMIT=.*|NETPULSE_COMMIT=$VAL_COMMIT|" \ + "$SB_ENV" || die "Не вдалося перемкнути версію в $SB_ENV" + rm -f "$SB_ENV.bak" + ok "версія образів: sandbox-prev → sandbox-new (тег netpulse/*:dev не чіпається)" + sandbox_up_dump sandbox_up_apply sandbox_up_agent_compat @@ -3241,7 +3292,7 @@ cmd_sandbox_upgrade() { printf '%s== прибирання%s\n' "$C_B" "$C_0" sandbox_down || exit 1 rm -f "$ROOT_MAIN/$SB_UP_ENV_NAME" - sandbox_up_drop_tree + sandbox_up_drop_leftovers printf '\n %sОновлення пройшло повністю, слідів не лишилось.%s\n\n' "$C_G" "$C_0" fi trap - INT TERM HUP @@ -3372,7 +3423,15 @@ cmd_sandbox_down() { SB_PROJECT=$SB_UP_PROJECT SB_ENV="$ROOT_MAIN/$SB_UP_ENV_NAME" - if sandbox_leftovers || [ -f "$SB_ENV" ] || [ -d "$SB_UP_TREE" ]; then + # Власні теги образів теж рахуються за слід: два гігабайти, які ніхто + # не шукатиме, бо контейнерів уже немає. + _imgs="" + for _i in netpulse/server netpulse/agent; do + for _t in sandbox-prev sandbox-new; do + docker image inspect "$_i:$_t" >/dev/null 2>&1 && _imgs=1 + done + done + if sandbox_leftovers || [ -f "$SB_ENV" ] || [ -d "$SB_UP_TREE" ] || [ -n "$_imgs" ]; then _any=1 say " режим оновлення ($SB_PROJECT):" # Файл .env міг лишитись у дереві попередньої версії, якщо прогін @@ -3382,7 +3441,7 @@ cmd_sandbox_down() { cp "$SB_UP_TREE/$SB_UP_ENV_NAME" "$SB_ENV" sandbox_down || _rc=1 SB_UP=1 - sandbox_up_drop_tree + sandbox_up_drop_leftovers [ -d "$SB_UP_TREE" ] || ok "дерево попередньої версії видалено" fi