152 lines
5.1 KiB
Python
152 lines
5.1 KiB
Python
#!/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()
|