Отсев баз через source.exclude_databases
list_databases() прячит базы по массиву строк: подстрока без регистра или маска со * ? [. Фильтр — единственное место рождения списка, поэтому одинаков для меню, пульта и list; явное имя в CLI не трогает.
This commit is contained in:
@@ -75,6 +75,9 @@ matches(text, query) ; общий фильтр для pick() и обоих с
|
||||
`kind` (`dump` или `sync`), а правки полей уезжают в `OpSpec.overrides` и накладываются
|
||||
копией конфига в `op_cfg()`. Файл `config.json` пульт не переписывает, значения живут
|
||||
до выхода в `DashboardScreen.op_defaults`.
|
||||
- `source.exclude_databases` отсеивается в `list_databases()` — единственном месте, где
|
||||
список рождается, поэтому фильтр одинаков для меню, пульта и `list`. Явное имя базы в CLI
|
||||
через список не проходит и потому не фильтруется.
|
||||
- Имя базы — параметр, а не поле конфига. `source` описывает только сервер
|
||||
(`maintenance_database` — куда подключаться, чтобы прочитать `pg_database`),
|
||||
`target.database: null` означает «локально как на стенде»; переопределяется `--target-db`.
|
||||
|
||||
@@ -142,6 +142,7 @@ python pg_stand_sync.py restore dumps/zpas-20260820-031332.dump
|
||||
| `dump_dir` | `dumps` | куда складывать дампы (относительный путь — от каталога скрипта) |
|
||||
| `keep_dumps` | `5` | сколько последних дампов хранить, `0` — не чистить |
|
||||
| `source.maintenance_database` | `postgres` | база, к которой подключаться для чтения списка баз |
|
||||
| `source.exclude_databases` | `[]` | массив строк: базы, которые не показывать в списках |
|
||||
| `source.password_env` | — | имя переменной окружения с паролем вместо `password` в файле |
|
||||
| `target.database` | `null` | имя локальной базы; `null` — как на стенде |
|
||||
| `target.recreate` | `true` | DROP + CREATE локальной базы перед восстановлением |
|
||||
@@ -150,6 +151,13 @@ python pg_stand_sync.py restore dumps/zpas-20260820-031332.dump
|
||||
| `restore.exit_on_error` | `false` | падать на первой ошибке восстановления |
|
||||
| `post_restore_sql` | `[]` | список SQL, выполняемых в целевой базе после восстановления |
|
||||
|
||||
```json
|
||||
"exclude_databases": ["_old", "keycloak", "*_tmp"]
|
||||
```
|
||||
|
||||
Строка без спецсимволов — подстрока (регистр не важен), со `*`, `?` или `[` — маска.
|
||||
Фильтр действует только на списки: базу, названную прямо (`sync zpas_old`), он не прячет.
|
||||
|
||||
Пароль ищется в три шага: `password` в конфиге → переменная окружения из `password_env` →
|
||||
запрос с терминала (один раз за запуск, ввод скрыт). В неинтерактивном запуске третьего шага
|
||||
нет — либо `password_env`, либо `pgpass.conf`. Пароль всегда уходит в дочерний процесс через
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"host": "postgresql.lan",
|
||||
"port": 5432,
|
||||
"maintenance_database": "postgres",
|
||||
"exclude_databases": [],
|
||||
"user": "postgres",
|
||||
"password": null,
|
||||
"password_env": "STAND_PGPASSWORD",
|
||||
|
||||
+24
-1
@@ -7,6 +7,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import fnmatch
|
||||
import getpass
|
||||
import importlib.util
|
||||
import json
|
||||
@@ -358,8 +359,28 @@ def psql_exec(tools: dict, node: dict, dbname: str, sql: str, what: str,
|
||||
run(cmd, env, what, dry_run)
|
||||
|
||||
|
||||
def excluded(name: str, patterns) -> bool:
|
||||
"""Прячется ли база фильтром: подстрока без регистра, а со звёздочкой — маска."""
|
||||
low = name.lower()
|
||||
for raw in patterns or []:
|
||||
pattern = str(raw).strip().lower()
|
||||
if not pattern:
|
||||
continue
|
||||
if any(ch in pattern for ch in "*?["):
|
||||
if fnmatch.fnmatch(low, pattern):
|
||||
return True
|
||||
elif pattern in low:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def list_databases(tools: dict, node: dict, label: str) -> list:
|
||||
"""Список баз сервера: (имя, человекочитаемый размер)."""
|
||||
"""Список баз сервера: (имя, человекочитаемый размер).
|
||||
|
||||
Отсев по node.exclude_databases делается здесь, в единственном месте, где список
|
||||
вообще рождается: и меню, и пульт, и `list` получают его уже без мусорных баз.
|
||||
Явное имя базы в CLI фильтр не трогает — оно списком не ходит.
|
||||
"""
|
||||
admin_db = node.get("maintenance_database") or node.get("database") or "postgres"
|
||||
env = conn_env(node, label, admin_db)
|
||||
sql = (
|
||||
@@ -375,6 +396,8 @@ def list_databases(tools: dict, node: dict, label: str) -> list:
|
||||
if not line.strip():
|
||||
continue
|
||||
name, _, size = line.partition("\t")
|
||||
if excluded(name, node.get("exclude_databases")):
|
||||
continue
|
||||
rows.append((name, size or ""))
|
||||
return rows
|
||||
|
||||
|
||||
@@ -1034,3 +1034,32 @@ def test_arrows_walk_dialog_fields(cfg, monkeypatch):
|
||||
await pilot.press("escape")
|
||||
await pilot.pause()
|
||||
asyncio.run(scenario())
|
||||
|
||||
|
||||
def test_exclude_databases_hides_rows(monkeypatch):
|
||||
"""exclude_databases — массив строк: подстрока без регистра, со звёздочкой — маска."""
|
||||
assert core.excluded("zpas_test", ["test"])
|
||||
assert core.excluded("ZPAS_TEST", ["test"]) # регистр не важен
|
||||
assert core.excluded("zpas_old_2", ["*_old_?"]) # маска
|
||||
assert not core.excluded("zpas", ["test", "*_old"])
|
||||
assert not core.excluded("zpas", []) # пустой список ничего не прячет
|
||||
assert not core.excluded("zpas", ["", " "]) # и пустые строки тоже
|
||||
|
||||
node = {"host": "stand", "user": "postgres", "password": "x",
|
||||
"maintenance_database": "postgres",
|
||||
"exclude_databases": ["_old", "keycloak", "*_tmp"]}
|
||||
monkeypatch.setattr(core, "capture", lambda cmd, env, what:
|
||||
"zpas\t1284 MB\nzpas_old\t312 MB\nkeycloak\t12 MB\n"
|
||||
"media\t8 MB\nbuild_tmp\t1 MB\n")
|
||||
rows = core.list_databases({"psql": "psql"}, node, "стенд")
|
||||
assert [name for name, _ in rows] == ["zpas", "media"]
|
||||
|
||||
|
||||
def test_exclude_databases_does_not_touch_explicit_name(cfg, monkeypatch):
|
||||
"""Скрытая из списка база остаётся доступной, если имя названо прямо в CLI."""
|
||||
cfg["source"]["exclude_databases"] = ["zpas"]
|
||||
called = []
|
||||
monkeypatch.setattr(core, "dump", lambda cfg_, tools, dbname, out, dry:
|
||||
called.append(dbname) or out)
|
||||
core.do_dump(cfg, {"pg_dump": "pg_dump"}, ["zpas"], dry_run=True)
|
||||
assert called == ["zpas"]
|
||||
|
||||
Reference in New Issue
Block a user