add: devops scripts and configs
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Дополнительный генератор: обогащает *_outputs.md реальными ключами
|
||||
из output_inventory (собранного со стендов).
|
||||
|
||||
Обновляет только секцию "## Реальные поля из облака" — не трогает остальное.
|
||||
|
||||
Запуск (из корня репо на VM):
|
||||
python3 devops/04b_enrich_outputs_from_inventory.py [--dry-run]
|
||||
"""
|
||||
|
||||
import json, yaml, glob, os, sys
|
||||
|
||||
BASE = "/home/naeel/terra/terraform"
|
||||
YAML_DIR = f"{BASE}/devops/profiles/test/generated/resources_yaml"
|
||||
INV_DIR = f"{BASE}/devops/profiles/test/generated/output_inventory"
|
||||
DOCS_DIR = f"{BASE}/docs/30_registry/resources"
|
||||
|
||||
SECTION_MARKER = "## Реальные поля из облака"
|
||||
|
||||
|
||||
def flatten(obj, prefix=""):
|
||||
"""Рекурсивно собирает dot-notation ключи из вложенного dict/list."""
|
||||
keys = []
|
||||
if isinstance(obj, dict):
|
||||
for k, v in obj.items():
|
||||
full = f"{prefix}.{k}" if prefix else k
|
||||
keys.append(full)
|
||||
if isinstance(v, (dict, list)):
|
||||
keys.extend(flatten(v, full))
|
||||
elif isinstance(obj, list):
|
||||
for i, v in enumerate(obj):
|
||||
full = f"{prefix}.{i}" if prefix else str(i)
|
||||
if isinstance(v, (dict, list)):
|
||||
keys.extend(flatten(v, full))
|
||||
# скалярные элементы списка не добавляем — нет смысла
|
||||
return keys
|
||||
|
||||
|
||||
def best_instance(instances):
|
||||
"""Выбираем лучший инстанс: running с данными > suspended с данными."""
|
||||
candidates = [i for i in instances if i.get("out") or i.get("params")]
|
||||
if not candidates:
|
||||
return None
|
||||
running = [i for i in candidates if i.get("status") == "running"]
|
||||
return running[-1] if running else candidates[-1]
|
||||
|
||||
|
||||
def build_section(inst):
|
||||
params = inst.get("params") or {}
|
||||
out = inst.get("out") or {}
|
||||
vault = inst.get("vault_keys") or []
|
||||
source = inst.get("source", "test")
|
||||
name = inst.get("name", "?")
|
||||
status = inst.get("status", "?")
|
||||
|
||||
lines = [
|
||||
f"{SECTION_MARKER} ({source} / {name} / {status})\n",
|
||||
"\n",
|
||||
"Поля ниже получены из реальных инстансов этого сервиса в облаке.\n",
|
||||
"Используйте их как готовые ключи для `state_out_flat` и `vault_secrets`.\n",
|
||||
]
|
||||
|
||||
if params:
|
||||
lines.append("\n### `state_params` ключи\n\n")
|
||||
for k in params.keys():
|
||||
lines.append(f'- `state_params["{k}"]`\n')
|
||||
|
||||
flat_out = flatten(out)
|
||||
if flat_out:
|
||||
lines.append("\n### `state_out_flat` ключи\n\n")
|
||||
for k in flat_out:
|
||||
lines.append(f'- `state_out_flat["{k}"]`\n')
|
||||
|
||||
if vault:
|
||||
lines.append("\n### `vault_secrets` ключи\n\n")
|
||||
for k in vault:
|
||||
lines.append(f'- `vault_secrets["{k}"]`\n')
|
||||
|
||||
return "".join(lines)
|
||||
|
||||
|
||||
def main():
|
||||
dry_run = "--dry-run" in sys.argv
|
||||
|
||||
# 1. service_id → name (из YAML)
|
||||
svc_to_name = {}
|
||||
for f in glob.glob(f"{YAML_DIR}/*.yaml"):
|
||||
try:
|
||||
d = yaml.safe_load(open(f))
|
||||
sid = d.get("service_id")
|
||||
name = d.get("name") or d.get("service_short_name")
|
||||
if sid and name:
|
||||
svc_to_name[sid] = name
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
updated = 0
|
||||
skipped = 0
|
||||
|
||||
for inv_file in sorted(glob.glob(f"{INV_DIR}/svc_*.json")):
|
||||
if "index" in inv_file:
|
||||
continue
|
||||
|
||||
inv = json.load(open(inv_file))
|
||||
svc_id = inv.get("serviceId")
|
||||
inst = best_instance(inv.get("instances", []))
|
||||
|
||||
if not inst:
|
||||
print(f" skip svc={svc_id}: нет данных")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
name = svc_to_name.get(svc_id)
|
||||
if not name:
|
||||
print(f" skip svc={svc_id}: нет YAML маппинга")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
out_path = f"{DOCS_DIR}/{name}_outputs.md"
|
||||
if not os.path.exists(out_path):
|
||||
print(f" skip svc={svc_id} ({name}): нет файла {name}_outputs.md")
|
||||
skipped += 1
|
||||
continue
|
||||
|
||||
new_section = build_section(inst)
|
||||
content = open(out_path).read()
|
||||
|
||||
if SECTION_MARKER in content:
|
||||
idx = content.index(SECTION_MARKER)
|
||||
new_content = content[:idx] + new_section
|
||||
else:
|
||||
new_content = content.rstrip("\n") + "\n\n" + new_section
|
||||
|
||||
if dry_run:
|
||||
print(f" dry-run svc={svc_id} ({name}): {out_path}")
|
||||
print(f" params={list((inst.get('params') or {}).keys())}")
|
||||
print(f" out_flat_count={len(flatten(inst.get('out') or {}))}")
|
||||
print(f" vault={inst.get('vault_keys', [])}")
|
||||
else:
|
||||
with open(out_path, "w") as f:
|
||||
f.write(new_content)
|
||||
print(f" updated svc={svc_id} ({name})")
|
||||
|
||||
updated += 1
|
||||
|
||||
print(f"\nДone: {updated} updated, {skipped} skipped")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user