From 9bcd2ae814b1cbb8905a6a6107a34114b9be808b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix-Antoine=20Fortin?= Date: Fri, 4 Sep 2026 12:53:12 -0400 Subject: [PATCH] Fixed the DetachedInstanceError Cause: reading status for a DESTROY_SUCCESS cluster deletes and detaches its ORM instance before state accesses the lazy-loaded project. --- mchub/models/magic_castle/magic_castle.py | 6 +++++- tests/unit/magic_castle/test_magic_castle.py | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mchub/models/magic_castle/magic_castle.py b/mchub/models/magic_castle/magic_castle.py index 4c3e3308..a6849464 100644 --- a/mchub/models/magic_castle/magic_castle.py +++ b/mchub/models/magic_castle/magic_castle.py @@ -414,6 +414,10 @@ def get_progress(self): @property def state(self): config = self.applied_config if self.applied_config else self.config + # Reading the status of a successfully destroyed cluster deletes its ORM + # instance. Preserve project metadata before that commit detaches the + # instance, so the final DESTROY_SUCCESS state can still be serialized. + cloud = {"name": self.project.name, "id": self.project.id} return { **config, "hostname": self.hostname, @@ -421,7 +425,7 @@ def state(self): "freeipa_passwd": self.freeipa_passwd, "age": self.age, "expiration_date": self.expiration_date, - "cloud": {"name": self.project.name, "id": self.project.id}, + "cloud": cloud, "hieradata_entries": _hieradata_to_entries(config.get("hieradata", "")), } diff --git a/tests/unit/magic_castle/test_magic_castle.py b/tests/unit/magic_castle/test_magic_castle.py index 3808926d..914b569a 100644 --- a/tests/unit/magic_castle/test_magic_castle.py +++ b/tests/unit/magic_castle/test_magic_castle.py @@ -117,7 +117,7 @@ def test_get_status_valid(app): assert valid1.orm.status == ClusterStatusCode.PROVISIONING_SUCCESS -def test_destroyed_cluster_archives_github_repo(app, mocker): +def test_destroyed_cluster_state_archives_github_repo(app, mocker): from mchub.database import db from mchub.models.magic_castle.cluster_status_code import ClusterStatusCode from mchub.models.magic_castle.magic_castle import MagicCastle, MagicCastleORM @@ -130,7 +130,10 @@ def test_destroyed_cluster_archives_github_repo(app, mocker): archive_repo = mocker.spy(get_github_storage(), "archive_repo") cluster.orm.status = ClusterStatusCode.DESTROY_SUCCESS - assert cluster.status == ClusterStatusCode.DESTROY_SUCCESS + state = cluster.state + + assert state["status"] == ClusterStatusCode.DESTROY_SUCCESS + assert state["cloud"] == {"name": "project-alice", "id": 1} archive_repo.assert_called_once_with("valid1.magic-castle.cloud")