diff --git a/libcloud/dns/drivers/rcodezero.py b/libcloud/dns/drivers/rcodezero.py index 21a79a87ee..8f453b0797 100644 --- a/libcloud/dns/drivers/rcodezero.py +++ b/libcloud/dns/drivers/rcodezero.py @@ -112,7 +112,7 @@ def __init__( secure=True, host=None, port=None, - api_version="v1", + api_version="v2", **kwargs, ): """ @@ -141,6 +141,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) @@ -565,8 +567,8 @@ def _to_patchrequest(self, zone, record, name, type, data, extra, action): 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 = {} 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 a77a0cf4d8..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") @@ -144,7 +162,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 +175,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 +191,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 +207,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 +221,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 +229,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"}',