Skip to content

Commit 8c7ffb7

Browse files
fix(schema): heal stale component-level 'root' op de component-catalog (#120)
De migratie _migrate_v2_to_v2_1 (root: true -> deployment-level root-component) scande alleen deployment-component-refs, niet de project-level component-catalog, en was version-gated. Bestanden die al op schema-version 2.2 stonden met een achtergebleven `root:`-key op een catalog-component faalden daardoor elke deploy op schemavalidatie (components/0: Additional properties are not allowed ('root')). Voegt _fixup_catalog_root toe, aangeroepen vanuit het onvoorwaardelijke _fixup_v2_data, dat: - `root` van catalog-componenten verwijdert ongeacht schema-version - een echte root lift naar root-component op elke deployment die de component refereert en er nog geen heeft - `root: false` simpelweg dropt Werkt zo ook al-2.2-gestempelde bestanden bij op de eerstvolgende load. Verwijdert daarnaast een stale docstring in router_wizard.py die nog naar de oude root:true-conversie verwees.
1 parent 494a5e0 commit 8c7ffb7

3 files changed

Lines changed: 128 additions & 1 deletion

File tree

operations-manager/python/opi/services/schema_migration.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,13 +427,59 @@ def _fixup_v2_data(project_data: dict[str, Any]) -> bool:
427427
if _fixup_flat_resources(entity):
428428
cleaned = True
429429

430+
# Strip stale root flags left on the project-level component catalog
431+
if _fixup_catalog_root(project_data):
432+
cleaned = True
433+
430434
if cleaned:
431435
project_name = project_data.get("name", "unknown")
432436
logger.info(f"Cleaned up stale data in project '{project_name}'")
433437

434438
return cleaned
435439

436440

441+
def _fixup_catalog_root(project_data: dict[str, Any]) -> bool:
442+
"""Strip stale component-level ``root`` flags from the top-level component catalog.
443+
444+
The original nice-url feature stored the root marker as ``root: true`` on
445+
catalog components. The schema later moved this to deployment-level
446+
``root-component`` (see ``_migrate_v2_to_v2_1``), but that migration only
447+
scans deployment component refs, not the project-level catalog, and it is
448+
version-gated -- so files already stamped at the latest version keep the
449+
stale catalog key and fail schema validation. This fixup runs
450+
unconditionally to repair them.
451+
452+
For any catalog component still marked ``root: true``, the marker is lifted
453+
to ``root-component`` on every deployment that references it and does not
454+
already have one, then the ``root`` key is removed. ``root: false`` is
455+
dropped outright.
456+
457+
Returns True if any cleanup was performed.
458+
"""
459+
catalog = [c for c in project_data.get("components", []) if isinstance(c, dict)]
460+
deployments = [d for d in project_data.get("deployments", []) if isinstance(d, dict)]
461+
cleaned = False
462+
463+
for comp in catalog:
464+
if "root" not in comp:
465+
continue
466+
467+
is_root = comp.get("root") is True
468+
comp_name = comp.get("name")
469+
del comp["root"]
470+
cleaned = True
471+
472+
if is_root and comp_name:
473+
for dep in deployments:
474+
if dep.get("root-component"):
475+
continue
476+
refs = {r.get("reference") for r in dep.get("components", []) if isinstance(r, dict)}
477+
if comp_name in refs:
478+
dep["root-component"] = comp_name
479+
480+
return cleaned
481+
482+
437483
def _fixup_flat_resources(entity: dict[str, Any]) -> bool:
438484
"""Migrate old flat resource format to nested requests/limits structure.
439485

operations-manager/python/opi/web/router_wizard.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1157,7 +1157,9 @@ def _assemble_deployment(final_data: dict[str, Any]) -> None:
11571157
11581158
- Sets ``name``, ``cluster``, ``namespace``, ``repository``
11591159
- Builds ``components`` array from component names
1160-
- Converts ``root-component`` to ``root: true`` on the matching component
1160+
1161+
The root component is carried as deployment-level ``root-component`` and is
1162+
left untouched here (it is set during the domain step).
11611163
"""
11621164
deployments = final_data.get("deployments", [{}])
11631165
deployment = deployments[0] if deployments else {}

operations-manager/python/tests/test_fixup_v2_data.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,82 @@ def test_upload_cleanup(self):
302302
assert comp["resources"]["limits"]["cpu"] == "1"
303303
assert comp["resources"]["limits"]["memory"] == "256Mi"
304304
assert comp["resources"]["requests"]["memory"] == "256Mi"
305+
306+
307+
# ---------------------------------------------------------------------------
308+
# _fixup_catalog_root: stale root flag on the top-level component catalog
309+
# ---------------------------------------------------------------------------
310+
311+
312+
class TestFixupCatalogRoot:
313+
def test_strips_catalog_root_and_lifts_to_deployment(self):
314+
"""A catalog component marked root: true lifts to root-component on the
315+
referencing deployment, and the stale catalog key is removed."""
316+
data = {
317+
"schema-version": 2.2,
318+
"components": [{"name": "component-1", "root": True}],
319+
"deployments": [
320+
{"name": "main", "components": [{"reference": "component-1"}]},
321+
],
322+
}
323+
assert _fixup_v2_data(data) is True
324+
assert "root" not in data["components"][0]
325+
assert data["deployments"][0]["root-component"] == "component-1"
326+
327+
def test_strips_catalog_root_when_deployment_already_has_root_component(self):
328+
"""If the deployment already has root-component, it takes precedence and
329+
the stale catalog flag is simply removed."""
330+
data = {
331+
"schema-version": 2.2,
332+
"components": [{"name": "component-1", "root": True}],
333+
"deployments": [
334+
{
335+
"name": "main",
336+
"components": [{"reference": "component-1"}],
337+
"root-component": "component-1",
338+
},
339+
],
340+
}
341+
assert _fixup_v2_data(data) is True
342+
assert "root" not in data["components"][0]
343+
assert data["deployments"][0]["root-component"] == "component-1"
344+
345+
def test_drops_root_false_without_lifting(self):
346+
data = {
347+
"schema-version": 2.2,
348+
"components": [{"name": "component-1", "root": False}],
349+
"deployments": [
350+
{"name": "main", "components": [{"reference": "component-1"}]},
351+
],
352+
}
353+
assert _fixup_v2_data(data) is True
354+
assert "root" not in data["components"][0]
355+
assert "root-component" not in data["deployments"][0]
356+
357+
def test_runs_on_already_latest_version_via_migrate_to_latest(self):
358+
"""The bouwm-6gn case: a file already stamped 2.2 with a catalog root key
359+
is still repaired (the version-gated _migrate_v2_to_v2_1 would skip it)."""
360+
data = {
361+
"schema-version": 2.2,
362+
"components": [{"name": "component-1", "root": True}],
363+
"deployments": [
364+
{
365+
"name": "main",
366+
"components": [{"reference": "component-1"}],
367+
"root-component": "component-1",
368+
},
369+
],
370+
}
371+
result, migrated = migrate_to_latest(data)
372+
assert migrated is True
373+
assert "root" not in result["components"][0]
374+
375+
def test_no_change_when_catalog_has_no_root(self):
376+
data = {
377+
"schema-version": 2.2,
378+
"components": [{"name": "component-1"}],
379+
"deployments": [
380+
{"name": "main", "components": [{"reference": "component-1"}]},
381+
],
382+
}
383+
assert _fixup_v2_data(data) is False

0 commit comments

Comments
 (0)