From 4c497e6c57f87ab47fea6a644fe71ff32fde9d43 Mon Sep 17 00:00:00 2001 From: Lukasz Drozdz Date: Thu, 30 Jul 2026 08:34:06 +0200 Subject: [PATCH] fix(ingester): don't clear a cluster's scope oneof when building a VirtualMachine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VirtualMachine(cluster=..., site=...) filled the cluster's scope_site whenever HasField("scope_site") was False. Cluster.scope is a oneof, so that guard is also False when the caller already scoped the cluster to a SiteGroup, Location or Region — and CopyFrom then silently cleared it. Guard on WhichOneof("scope") instead: the shortcut still fills an unscoped cluster, but leaves an already-scoped one alone. Co-Authored-By: Claude Opus 5 --- netboxlabs/diode/sdk/ingester.py | 5 ++++- tests/test_ingester.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/netboxlabs/diode/sdk/ingester.py b/netboxlabs/diode/sdk/ingester.py index c37d7c0..b742707 100644 --- a/netboxlabs/diode/sdk/ingester.py +++ b/netboxlabs/diode/sdk/ingester.py @@ -7007,7 +7007,10 @@ def __new__( if site is not None: if device is not None and not device.HasField("site"): device.site.CopyFrom(site) - if cluster is not None and not cluster.HasField("scope_site"): + # scope_site shares a oneof with scope_location/region/site_group, so + # HasField("scope_site") is False while a sibling holds the scope and + # CopyFrom would silently clear it. + if cluster is not None and not cluster.WhichOneof("scope"): cluster.scope_site.CopyFrom(site) if role is not None: if device is not None and not device.HasField("role"): diff --git a/tests/test_ingester.py b/tests/test_ingester.py index 1c9b5b8..853a62a 100644 --- a/tests/test_ingester.py +++ b/tests/test_ingester.py @@ -43,6 +43,7 @@ Prefix, Role, Site, + SiteGroup, Tag, VirtualDisk, VMInterface, @@ -608,6 +609,22 @@ def test_virtual_machine_instantiation_with_cluster_without_site(): assert virtual_machine.cluster.scope_site.name == "Site1" +def test_virtual_machine_instantiation_keeps_cluster_scope_site_group(): + """Check VirtualMachine leaves a cluster that is already scoped to a SiteGroup untouched.""" + cluster = Cluster(name="gc-us-east1", scope_site_group=SiteGroup(name="SiteGroup1")) + + virtual_machine = VirtualMachine( + name="vm1", + cluster=cluster, + site=Site(name="Site1"), + ) + + assert cluster.WhichOneof("scope") == "scope_site_group" + assert cluster.scope_site_group.name == "SiteGroup1" + assert virtual_machine.cluster.WhichOneof("scope") == "scope_site_group" + assert virtual_machine.cluster.scope_site_group.name == "SiteGroup1" + + def test_virtual_disk_instantiation_with_all_fields(): """Check VirtualDisk instantiation with all fields.""" virtual_disk = VirtualDisk(