From 9b0c1218c52afa11d86ac4ab5a56a947681da292 Mon Sep 17 00:00:00 2001 From: Michael Braunoeder Date: Fri, 3 Jan 2025 10:56:28 +0100 Subject: [PATCH 1/4] check rrset type before generating the patch request --- libcloud/dns/drivers/rcodezero.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libcloud/dns/drivers/rcodezero.py b/libcloud/dns/drivers/rcodezero.py index 854fbc9ac7..b33c6c75c2 100644 --- a/libcloud/dns/drivers/rcodezero.py +++ b/libcloud/dns/drivers/rcodezero.py @@ -542,8 +542,8 @@ def _to_patchrequest(self, zone, record, name, type, data, extra, action): # request continue - if name == r.name and r.id != id: - # we have other records with the same name so make an update + if name == r.name and type == r.type and r.id != id: + # we have other records with the same name and type so make an update # request rrset["changetype"] = "update" content = {} From 4ccf1ea664b26ee194605464aa9553870d0893cf Mon Sep 17 00:00:00 2001 From: Michael Braunoeder Date: Fri, 3 Jan 2025 10:57:28 +0100 Subject: [PATCH 2/4] use API v2 as default --- libcloud/dns/drivers/rcodezero.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libcloud/dns/drivers/rcodezero.py b/libcloud/dns/drivers/rcodezero.py index b33c6c75c2..2c335359d1 100644 --- a/libcloud/dns/drivers/rcodezero.py +++ b/libcloud/dns/drivers/rcodezero.py @@ -110,7 +110,7 @@ def __init__( secure=True, host=None, port=None, - api_version="v1", + api_version="v2", **kwargs, ): """ @@ -139,6 +139,8 @@ def __init__( if api_version == "v1": self.api_root = "/api/v1" + elif api_version == "v2": + self.api_root = "/api/v2" else: raise NotImplementedError("Unsupported API version: %s" % api_version) From 74515cb132e48a563b134051061ebbf15f22e7ec Mon Sep 17 00:00:00 2001 From: Michael Braunoeder Date: Mon, 3 Mar 2025 10:43:15 +0100 Subject: [PATCH 3/4] rcode0-driver: fix tests --- libcloud/test/dns/test_rcodezero.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libcloud/test/dns/test_rcodezero.py b/libcloud/test/dns/test_rcodezero.py index a77a0cf4d8..b02712ca98 100644 --- a/libcloud/test/dns/test_rcodezero.py +++ b/libcloud/test/dns/test_rcodezero.py @@ -144,7 +144,7 @@ class RcodeZeroDNSMockHttp(MockHttp): fixtures = DNSFileFixtures("rcodezero") base_headers = {"content-type": "application/json"} - def _api_v1_zones(self, method, url, body, headers): + def _api_v2_zones(self, method, url, body, headers): if method == "GET": # list_zones() body = self.fixtures.load("list_zones.json") @@ -157,7 +157,7 @@ def _api_v1_zones(self, method, url, body, headers): raise NotImplementedError("Unexpected method: %s" % method) return (httplib.OK, body, self.base_headers, httplib.responses[httplib.OK]) - def _api_v1_zones_example_at(self, method, *args, **kwargs): + def _api_v2_zones_example_at(self, method, *args, **kwargs): if method == "GET": # list_records() body = self.fixtures.load("get_zone_details.json") @@ -173,10 +173,10 @@ def _api_v1_zones_example_at(self, method, *args, **kwargs): raise NotImplementedError("Unexpected method: %s" % method) return (httplib.OK, body, self.base_headers, httplib.responses[httplib.OK]) - def _api_v1_zones_example_at__rrsets(self, method, *args, **kwargs): - return self._api_v1_zones_example_at_rrsets(method, *args, **kwargs) + def _api_v2_zones_example_at__rrsets(self, method, *args, **kwargs): + return self._api_v2_zones_example_at_rrsets(method, *args, **kwargs) - def _api_v1_zones_example_at_rrsets(self, method, *args, **kwargs): + def _api_v2_zones_example_at_rrsets(self, method, *args, **kwargs): if method == "GET": # list_records() body = self.fixtures.load("list_records.json") @@ -189,7 +189,7 @@ def _api_v1_zones_example_at_rrsets(self, method, *args, **kwargs): raise NotImplementedError("Unexpected method: %s" % method) return (httplib.OK, body, self.base_headers, httplib.responses[httplib.OK]) - def _api_v1_zones_EXISTS(self, method, url, body, headers): + def _api_v2_zones_EXISTS(self, method, url, body, headers): # create_zone() is a POST. Raise on all other operations to be safe. if method != "POST": raise NotImplementedError("Unexpected method: %s" % method) @@ -203,7 +203,7 @@ def _api_v1_zones_EXISTS(self, method, url, body, headers): "Unprocessable Entity", ) - def _api_v1_zones_example_com_MISSING(self, *args, **kwargs): + def _api_v2_zones_example_com_MISSING(self, *args, **kwargs): return ( httplib.NOT_FOUND, '{"status": "failed","message": "Zone not found"}', @@ -211,7 +211,7 @@ def _api_v1_zones_example_com_MISSING(self, *args, **kwargs): "Unprocessable Entity", ) - def _api_v1_zones_example_at_MISSING(self, *args, **kwargs): + def _api_v2_zones_example_at_MISSING(self, *args, **kwargs): return ( httplib.NOT_FOUND, '{"status": "failed","message": "Zone not found"}', From 75e4f48c304cad1dbcdbbb308f7341da1abe8899 Mon Sep 17 00:00:00 2001 From: Michael Braunoeder Date: Mon, 3 Mar 2025 11:47:56 +0100 Subject: [PATCH 4/4] rcode0-driver: add regressiontest for #2042 --- .../dns/fixtures/rcodezero/list_records.json | 11 ++++++++++ libcloud/test/dns/test_rcodezero.py | 20 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/libcloud/test/dns/fixtures/rcodezero/list_records.json b/libcloud/test/dns/fixtures/rcodezero/list_records.json index 4744b31e26..c57586c0fe 100644 --- a/libcloud/test/dns/fixtures/rcodezero/list_records.json +++ b/libcloud/test/dns/fixtures/rcodezero/list_records.json @@ -26,6 +26,17 @@ "disabled": false } ] + }, + { + "name": "aaaaexisting.example.at.", + "type": "AAAA", + "ttl": 3600, + "records": [ + { + "content": "2001:db8::42", + "disabled": false + } + ] } ], "from": 1, diff --git a/libcloud/test/dns/test_rcodezero.py b/libcloud/test/dns/test_rcodezero.py index b02712ca98..c08102409e 100644 --- a/libcloud/test/dns/test_rcodezero.py +++ b/libcloud/test/dns/test_rcodezero.py @@ -89,7 +89,7 @@ def test_list_record_types(self): def test_list_records(self): records = self.driver.list_records(self.test_zone) - self.assertEqual(len(records), 3) + self.assertEqual(len(records), 4) def test_list_zones(self): zones = self.driver.list_zones() @@ -118,6 +118,24 @@ def test_update_record(self): self.assertEqual(record.type, RecordType.A) self.assertEqual(record.ttl, 300) + # test issue #2042 + def test_add_other_type_to_existing_record(self): + payload = self.driver._to_patchrequest( + self.test_record.zone.id, + self.test_record, + "aaaaexisting", + RecordType.A, + "127.0.0.1", + {"ttl": 300}, + "update", + ) + self.assertEqual(payload[0]["name"], "aaaaexisting.example.at.") + self.assertEqual(payload[0]["type"], RecordType.A) + self.assertEqual(payload[0]["ttl"], 300) + self.assertEqual(payload[0]["changetype"], "update") + expected_record = [{"content": "127.0.0.1"}] + self.assertEqual(payload[0]["records"], expected_record) + def test_update_zone(self): with self.assertRaises(NotImplementedError): self.driver.update_zone(self.test_zone, "example.at")