Skip to content

Commit eeb2726

Browse files
Address CoPilot fixes
1 parent f4252a2 commit eeb2726

11 files changed

Lines changed: 135 additions & 25 deletions

‎linode_api4/groups/vpc.py‎

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ def create(
7373
"description": description,
7474
"ipv4": ipv4,
7575
"ipv6": ipv6,
76-
"subnets": subnets,
7776
}
7877

7978
if subnets is not None and len(subnets) > 0:
@@ -85,10 +84,19 @@ def create(
8584

8685
params.update(kwargs)
8786

88-
result = self.client.post(
89-
"/vpcs",
90-
data=drop_null_keys(_flatten_request_body_recursive(params)),
91-
)
87+
data = drop_null_keys(_flatten_request_body_recursive(params))
88+
89+
# Preserve the ``{"id": null}`` detach signal on any subnet's
90+
# ``natgateway`` field: ``drop_null_keys`` is recursive and would
91+
# otherwise strip an explicit ``id: None`` inside ``natgateway``.
92+
# Bypass ``drop_null_keys`` for the entire ``subnets`` payload -
93+
# ``JSONObject`` values already strip their own optional ``None``
94+
# fields via ``_serialize``, and raw-dict subnets are expected to
95+
# be clean.
96+
if subnets is not None:
97+
data["subnets"] = _flatten_request_body_recursive(subnets)
98+
99+
result = self.client.post("/vpcs", data=data)
92100

93101
if not "id" in result:
94102
raise UnexpectedResponseError(

‎linode_api4/objects/networking.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,9 @@ class NATGatewayType(JSONObject):
583583
price: Optional[Price] = None
584584

585585

586+
# TODO: replace with base once the region_prices and transfer fields are in use and returned by the API
587+
588+
586589
@dataclass
587590
class NATGatewaySettings(JSONObject):
588591
allowed_ports_per_interface: List[int] = field(default_factory=list)
@@ -605,7 +608,7 @@ class NATGateway(Base):
605608

606609
properties = {
607610
"id": Property(identifier=True),
608-
"region": Property(),
611+
"region": Property(slug_relationship=Region),
609612
"addresses": Property(json_object=NATGatewayAddress),
610613
"address_autoscale_max": Property(),
611614
"default_ports_per_interface": Property(),
@@ -677,6 +680,7 @@ def address_assignment_create(
677680
model=self,
678681
data={"address": address},
679682
)
683+
self.invalidate()
680684

681685
if "address" not in result:
682686
raise UnexpectedResponseError(
@@ -702,6 +706,7 @@ def address_assignment_delete(self, address: str) -> bool:
702706
"{}/addresses/{}".format(NATGateway.api_endpoint, address),
703707
model=self,
704708
)
709+
self.invalidate()
705710

706711
if "error" in resp:
707712
return False

‎linode_api4/objects/vpc.py‎

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,9 @@ def subnet_create(
208208
ipv6: Optional[
209209
List[Union[VPCSubnetIPv6RangeOptions, Dict[str, Any]]]
210210
] = None,
211-
natgateway: Optional[VPCSubnetNATGatewayOptions] = None,
211+
natgateway: Optional[
212+
Union[VPCSubnetNATGatewayOptions, Dict[str, Any]]
213+
] = None,
212214
**kwargs,
213215
) -> VPCSubnet:
214216
"""
@@ -229,15 +231,22 @@ def subnet_create(
229231
"label": label,
230232
"ipv4": ipv4,
231233
"ipv6": ipv6,
232-
"natgateway": natgateway,
233234
}
234235

235236
params.update(kwargs)
236237

238+
data = drop_null_keys(_flatten_request_body_recursive(params))
239+
240+
# Preserve the ``{"id": null}`` detach signal separately: ``drop_null_keys``
241+
# is recursive and would otherwise strip an explicit ``id: None`` inside
242+
# ``natgateway``, collapsing the detach payload to ``{}``.
243+
if natgateway is not None:
244+
data["natgateway"] = _flatten_request_body_recursive(natgateway)
245+
237246
result = self._client.post(
238247
"{}/subnets".format(VPC.api_endpoint),
239248
model=self,
240-
data=drop_null_keys(_flatten_request_body_recursive(params)),
249+
data=data,
241250
)
242251
self.invalidate()
243252

‎test/fixtures/networking_natgateways_42_addresses.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
{
44
"address": "203.0.113.42",
55
"in_use": true,
6-
"interface_count": 2,
7-
"interface_url": "/v4/linode/instances/123/interfaces/456",
6+
"interface_count": 1,
7+
"interface_url": "/v4/networking/natgateways/42/addresses/203.0.113.42/interfaces",
88
"portset_assignments": 15,
99
"portset_capacity": 30
1010
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"address": "203.0.113.42",
33
"in_use": true,
4-
"interface_count": 2,
5-
"interface_url": "/v4/linode/instances/123/interfaces/456",
4+
"interface_count": 1,
5+
"interface_url": "/v4/networking/natgateways/42/addresses/203.0.113.42/interfaces",
66
"portset_assignments": 15,
77
"portset_capacity": 30
88
}

‎test/fixtures/networking_natgateways_42_interfaces.json‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
"type": "linode",
99
"url": "/v4/linode/instances/1001"
1010
},
11-
"addresses": ["172.24.213.144"],
11+
"addresses": ["203.0.113.42"],
1212
"portsets": [
1313
{
14-
"address": "172.24.213.144",
14+
"address": "203.0.113.42",
1515
"ports": [{"start": 2048, "end": 3071}]
1616
}
1717
]
@@ -24,10 +24,10 @@
2424
"type": "linode",
2525
"url": "/v4/linode/instances/1002"
2626
},
27-
"addresses": ["172.24.213.144"],
27+
"addresses": ["203.0.113.43"],
2828
"portsets": [
2929
{
30-
"address": "172.24.213.144",
30+
"address": "203.0.113.43",
3131
"ports": [{"start": 3072, "end": 4095}]
3232
}
3333
]

‎test/fixtures/vpcs_123456_subnets_789.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
]
5151
}
5252
]
53-
},
53+
},
5454
"created": "2018-01-01T00:01:01",
5555
"updated": "2018-01-01T00:01:01"
5656
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"id": 790,
3+
"label": "no-gateway-subnet",
4+
"ipv4": "10.0.1.0/24",
5+
"ipv6": [],
6+
"linodes": [],
7+
"databases": [],
8+
"created": "2018-01-01T00:01:01",
9+
"updated": "2018-01-01T00:01:01"
10+
}
11+

‎test/unit/groups/vpc_test.py‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,37 @@ def test_create_vpc_with_subnet_natgateway(self):
102102
self.assertEqual(vpc._populated, True)
103103
self.validate_vpc_123456(vpc)
104104

105+
def test_create_vpc_with_subnet_natgateway_detach_signal(self):
106+
"""
107+
Tests that a subnet's ``natgateway`` set to
108+
``VPCSubnetNATGatewayOptions(id=None)`` on VPC create is preserved
109+
as ``{"natgateway": {"id": null}}`` through ``drop_null_keys``.
110+
"""
111+
112+
with self.mock_post("/vpcs/123456") as m:
113+
self.client.vpcs.create(
114+
"test-vpc",
115+
"us-southeast",
116+
subnets=[
117+
{
118+
"label": "test-subnet",
119+
"ipv4": "10.0.0.0/24",
120+
"natgateway": VPCSubnetNATGatewayOptions(id=None),
121+
},
122+
],
123+
)
124+
125+
self.assertEqual(
126+
m.call_data.get("subnets"),
127+
[
128+
{
129+
"label": "test-subnet",
130+
"ipv4": "10.0.0.0/24",
131+
"natgateway": {"id": None},
132+
},
133+
],
134+
)
135+
105136
def test_list_ips(self):
106137
"""
107138
Validates that all VPC IPs can be listed.

‎test/unit/objects/networking_test.py‎

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ class NATGatewayTest(ClientBaseCase):
469469
def assert_natgateway_42(natgateway: NATGateway):
470470
assert natgateway.id == 42
471471
assert natgateway.label == "the-natgateway"
472-
assert natgateway.region == "us-east"
472+
assert natgateway.region.id == "us-east"
473473
assert natgateway.address_autoscale_max == 4
474474
assert natgateway.default_ports_per_interface == 4096
475475
assert natgateway.portset_assignments == 15
@@ -521,10 +521,10 @@ def test_delete_natgateway(self):
521521
def assert_address_assignment(assignment: NATGatewayAddressAssignment):
522522
assert assignment.address == "203.0.113.42"
523523
assert assignment.in_use is True
524-
assert assignment.interface_count == 2
524+
assert assignment.interface_count == 1
525525
assert (
526526
assignment.interface_url
527-
== "/v4/linode/instances/123/interfaces/456"
527+
== "/v4/networking/natgateways/42/addresses/203.0.113.42/interfaces"
528528
)
529529
assert assignment.portset_assignments == 15
530530
assert assignment.portset_capacity == 30
@@ -590,15 +590,17 @@ def assert_interfaces(interfaces):
590590
assert first.linode.label == "linode1001"
591591
assert first.linode.type == "linode"
592592
assert first.linode.url == "/v4/linode/instances/1001"
593-
assert first.addresses == ["172.24.213.144"]
594-
assert first.portsets[0].address == "172.24.213.144"
593+
assert first.addresses == ["203.0.113.42"]
594+
assert first.portsets[0].address == "203.0.113.42"
595595
assert first.portsets[0].ports[0].start == 2048
596596
assert first.portsets[0].ports[0].end == 3071
597597

598598
second = interfaces[1]
599599
assert second.id == 143
600600
assert second.linode.id == 1002
601601
assert second.linode.label == "linode1002"
602+
assert second.addresses == ["203.0.113.43"]
603+
assert second.portsets[0].address == "203.0.113.43"
602604

603605
def test_list_interfaces(self):
604606
"""
@@ -612,8 +614,9 @@ def test_list_address_interfaces(self):
612614
"""
613615
Tests GET /networking/natgateways/{id}/addresses/{address}/interfaces.
614616
615-
The per-address endpoint only returns interfaces actually using that
616-
address, so the response is a subset of the gateway-wide list.
617+
The per-address endpoint returns only the interfaces using that
618+
specific address. In the fixture, only interface 142 uses
619+
203.0.113.42; interface 143 uses 203.0.113.43.
617620
"""
618621
natgateway = NATGateway(self.client, 42)
619622
interfaces = natgateway.address_interfaces("203.0.113.42")

0 commit comments

Comments
 (0)