From 7c0b9942b74546be09c194c87228be514b6f3c62 Mon Sep 17 00:00:00 2001 From: Ross McFarland Date: Fri, 7 Jan 2022 09:36:33 -0800 Subject: [PATCH] Extract & shim AkamaiProvider into octodns_edgedns --- CHANGELOG.md | 1 + README.md | 2 +- octodns/provider/edgedns.py | 524 +----------------- octodns/provider/fastdns.py | 13 +- requirements.txt | 1 - tests/fixtures/edgedns-invalid-content.json | 35 -- .../fixtures/edgedns-records-prev-other.json | 166 ------ tests/fixtures/edgedns-records-prev.json | 166 ------ tests/fixtures/edgedns-records.json | 173 ------ tests/test_octodns_provider_edgedns.py | 154 +---- 10 files changed, 31 insertions(+), 1204 deletions(-) delete mode 100644 tests/fixtures/edgedns-invalid-content.json delete mode 100644 tests/fixtures/edgedns-records-prev-other.json delete mode 100644 tests/fixtures/edgedns-records-prev.json delete mode 100644 tests/fixtures/edgedns-records.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5611203..9e80be0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ https://github.com/octodns/octodns/issues/622 & https://github.com/octodns/octodns/pull/822 for more information. Providers that have been extracted in this release include: + * [AkamaiProvider](https://github.com/octodns/octodns-edgedns/) * [CloudflareProvider](https://github.com/octodns/octodns-cloudflare/) * [ConstellixProvider](https://github.com/octodns/octodns-constellix/) * [DigitalOceanProvider](https://github.com/octodns/octodns-digitalocean/) diff --git a/README.md b/README.md index e7ce969..6e5a682 100644 --- a/README.md +++ b/README.md @@ -193,7 +193,7 @@ The table below lists the providers octoDNS supports. We're currently in the pro | Provider | Module | Requirements | Record Support | Dynamic | Notes | |--|--|--|--|--|--| | [AzureProvider](/octodns/provider/azuredns.py) | | azure-identity, azure-mgmt-dns, azure-mgmt-trafficmanager | A, AAAA, CAA, CNAME, MX, NS, PTR, SRV, TXT | Alpha (A, AAAA, CNAME) | | -| [Akamai](/octodns/provider/edgedns.py) | | edgegrid-python | A, AAAA, CNAME, MX, NAPTR, NS, PTR, SPF, SRV, SSHFP, TXT | No | | +| [AkamaiProvider](https://github.com/octodns/octodns-edgedns/) | [octodns_edgedns](https://github.com/octodns/octodns-edgedns/) | | | | | | [CloudflareProvider](https://github.com/octodns/octodns-cloudflare/) | [octodns_cloudflare](https://github.com/octodns/octodns-cloudflare/) | | | | | | [ConstellixProvider](https://github.com/octodns/octodns-constellix/) | [octodns_constellix](https://github.com/octodns/octodns-constellix/) | | | | | | [DigitalOceanProvider](https://github.com/octodns/octodns-digitalocean/) | [octodns_digitalocean](https://github.com/octodns/octodns-digitalocean/) | | | | | diff --git a/octodns/provider/edgedns.py b/octodns/provider/edgedns.py index 5e7bc1e..7bc79dd 100644 --- a/octodns/provider/edgedns.py +++ b/octodns/provider/edgedns.py @@ -5,515 +5,19 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -from requests import Session -from akamai.edgegrid import EdgeGridAuth -from collections import defaultdict -from urllib.parse import urljoin - from logging import getLogger -from ..record import Record -from . import ProviderException -from .base import BaseProvider - - -class AkamaiClientNotFound(ProviderException): - - def __init__(self, resp): - message = "404: Resource not found" - super(AkamaiClientNotFound, self).__init__(message) - - -class AkamaiClient(object): - ''' - Client for making calls to Akamai Fast DNS API using Python Requests - - Edge DNS Zone Management API V2, found here: - https://developer.akamai.com/api/cloud_security/edge_dns_zone_management/v2.html - - Info on Python Requests library: - https://2.python-requests.org/en/master/ - - ''' - - def __init__(self, client_secret, host, access_token, client_token): - - self.base = "https://" + host + "/config-dns/v2/" - - sess = Session() - sess.auth = EdgeGridAuth( - client_token=client_token, - client_secret=client_secret, - access_token=access_token - ) - self._sess = sess - - def _request(self, method, path, params=None, data=None, v1=False): - - url = urljoin(self.base, path) - resp = self._sess.request(method, url, params=params, json=data) - - if resp.status_code == 404: - raise AkamaiClientNotFound(resp) - resp.raise_for_status() - - return resp - - def record_create(self, zone, name, record_type, content): - path = f'zones/{zone}/names/{name}/types/{record_type}' - result = self._request('POST', path, data=content) - - return result - - def record_delete(self, zone, name, record_type): - path = f'zones/{zone}/names/{name}/types/{record_type}' - result = self._request('DELETE', path) - - return result - - def record_replace(self, zone, name, record_type, content): - path = f'zones/{zone}/names/{name}/types/{record_type}' - result = self._request('PUT', path, data=content) - - return result - - def zone_get(self, zone): - path = f'zones/{zone}' - result = self._request('GET', path) - - return result - - def zone_create(self, contractId, params, gid=None): - path = f'zones?contractId={contractId}' - - if gid is not None: - path += f'&gid={gid}' - - result = self._request('POST', path, data=params) - - return result - - def zone_recordset_get(self, zone, page=None, pageSize=None, search=None, - showAll="true", sortBy="name", types=None): - - params = { - 'page': page, - 'pageSize': pageSize, - 'search': search, - 'showAll': showAll, - 'sortBy': sortBy, - 'types': types - } - - path = f'zones/{zone}/recordsets' - result = self._request('GET', path, params=params) - - return result - - -class AkamaiProvider(BaseProvider): - - ''' - Akamai Edge DNS Provider - - edgedns.py: - - Example config file with variables: - " - --- - providers: - config: - class: octodns.provider.yaml.YamlProvider - directory: ./config (example path to directory of zone files) - edgedns: - class: octodns.provider.edgedns.AkamaiProvider - client_secret: env/AKAMAI_CLIENT_SECRET - host: env/AKAMAI_HOST - access_token: env/AKAMAI_ACCESS_TOKEN - client_token: env/AKAMAI_CLIENT_TOKEN - contract_id: env/AKAMAI_CONTRACT_ID (optional) - - zones: - example.com.: - sources: - - config - targets: - - edgedns - " - - The first four variables above can be hidden in environment variables - and octoDNS will automatically search for them in the shell. It is - possible to also hard-code into the config file: eg, contract_id. - - The first four values can be found by generating credentials: - https://control.akamai.com/ - Configure > Organization > Manage APIs > New API Client for me - Select appropriate group, and fill relevant fields. - For API Service Name, select DNS-Zone Record Management - and then set appropriate Access level (Read-Write to make changes). - Then select the "New Credential" button to generate values for above - - The contract_id paramater is optional, and only required for creating - a new zone. If the zone being managed already exists in Akamai for the - user in question, then this paramater is not needed. - - ''' - - SUPPORTS_GEO = False - SUPPORTS_DYNAMIC = False - - SUPPORTS = set(('A', 'AAAA', 'CNAME', 'MX', 'NAPTR', 'NS', 'PTR', 'SPF', - 'SRV', 'SSHFP', 'TXT')) - - def __init__(self, id, client_secret, host, access_token, client_token, - contract_id=None, gid=None, *args, **kwargs): - - self.log = getLogger(f'AkamaiProvider[{id}]') - self.log.debug('__init__: id=%s, ') - super(AkamaiProvider, self).__init__(id, *args, **kwargs) - - self._dns_client = AkamaiClient(client_secret, host, access_token, - client_token) - - self._zone_records = {} - self._contractId = contract_id - self._gid = gid - - def zone_records(self, zone): - """ returns records for a zone, looks for it if not present, or - returns empty [] if can't find a match - """ - if zone.name not in self._zone_records: - try: - name = zone.name[:-1] - response = self._dns_client.zone_recordset_get(name) - self._zone_records[zone.name] = response.json()["recordsets"] - - except (AkamaiClientNotFound, KeyError): - return [] - - return self._zone_records[zone.name] - - def populate(self, zone, target=False, lenient=False): - self.log.debug('populate: name=%s', zone.name) - - values = defaultdict(lambda: defaultdict(list)) - for record in self.zone_records(zone): - - _type = record.get('type') - # Akamai sends down prefix.zonename., while octodns expects prefix - _name = record.get('name').split("." + zone.name[:-1], 1)[0] - if _name == zone.name[:-1]: - _name = '' # root / @ - - if _type not in self.SUPPORTS: - continue - values[_name][_type].append(record) - - before = len(zone.records) - for name, types in values.items(): - for _type, records in types.items(): - data_for = getattr(self, f'_data_for_{_type}') - record = Record.new(zone, name, data_for(_type, records[0]), - source=self, lenient=lenient) - zone.add_record(record, lenient=lenient) - - exists = zone.name in self._zone_records - found = len(zone.records) - before - self.log.info('populate: found %s records, exists=%s', found, exists) - - return exists - - def _apply(self, plan): - desired = plan.desired - changes = plan.changes - self.log.debug('apply: zone=%s, chnges=%d', desired.name, len(changes)) - - zone_name = desired.name[:-1] - try: - self._dns_client.zone_get(zone_name) - - except AkamaiClientNotFound: - self.log.info("zone not found, creating zone") - params = self._build_zone_config(zone_name) - self._dns_client.zone_create(self._contractId, params, self._gid) - - for change in changes: - class_name = change.__class__.__name__ - getattr(self, f'_apply_{class_name}')(change) - - # Clear out the cache if any - self._zone_records.pop(desired.name, None) - - def _apply_Create(self, change): - - new = change.new - record_type = new._type - - params_for = getattr(self, f'_params_for_{record_type}') - values = self._get_values(new.data) - rdata = params_for(values) - - zone = new.zone.name[:-1] - name = self._set_full_name(new.name, zone) - - content = { - "name": name, - "type": record_type, - "ttl": new.ttl, - "rdata": rdata - } - - self._dns_client.record_create(zone, name, record_type, content) - - return - - def _apply_Delete(self, change): - - zone = change.existing.zone.name[:-1] - name = self._set_full_name(change.existing.name, zone) - record_type = change.existing._type - - self._dns_client.record_delete(zone, name, record_type) - - return - - def _apply_Update(self, change): - - new = change.new - record_type = new._type - - params_for = getattr(self, f'_params_for_{record_type}') - values = self._get_values(new.data) - rdata = params_for(values) - - zone = new.zone.name[:-1] - name = self._set_full_name(new.name, zone) - - content = { - "name": name, - "type": record_type, - "ttl": new.ttl, - "rdata": rdata - } - - self._dns_client.record_replace(zone, name, record_type, content) - - return - - def _data_for_multiple(self, _type, records): - - return { - 'ttl': records['ttl'], - 'type': _type, - 'values': [r for r in records['rdata']] - } - - _data_for_A = _data_for_multiple - _data_for_AAAA = _data_for_multiple - _data_for_NS = _data_for_multiple - _data_for_SPF = _data_for_multiple - - def _data_for_CNAME(self, _type, records): - value = records['rdata'][0] - if (value[-1] != '.'): - value = f'{value}.' - - return { - 'ttl': records['ttl'], - 'type': _type, - 'value': value - } - - def _data_for_MX(self, _type, records): - values = [] - for r in records['rdata']: - preference, exchange = r.split(" ", 1) - values.append({ - 'preference': preference, - 'exchange': exchange - }) - return { - 'ttl': records['ttl'], - 'type': _type, - 'values': values - } - - def _data_for_NAPTR(self, _type, records): - values = [] - for r in records['rdata']: - order, preference, flags, service, regexp, repl = r.split(' ', 5) - - values.append({ - 'flags': flags[1:-1], - 'order': order, - 'preference': preference, - 'regexp': regexp[1:-1], - 'replacement': repl, - 'service': service[1:-1] - }) - return { - 'type': _type, - 'ttl': records['ttl'], - 'values': values - } - - def _data_for_PTR(self, _type, records): - - return { - 'ttl': records['ttl'], - 'type': _type, - 'value': records['rdata'][0] - } - - def _data_for_SRV(self, _type, records): - values = [] - for r in records['rdata']: - priority, weight, port, target = r.split(' ', 3) - values.append({ - 'port': port, - 'priority': priority, - 'target': target, - 'weight': weight - }) - - return { - 'type': _type, - 'ttl': records['ttl'], - 'values': values - } - - def _data_for_SSHFP(self, _type, records): - values = [] - for r in records['rdata']: - algorithm, fp_type, fingerprint = r.split(' ', 2) - values.append({ - 'algorithm': algorithm, - 'fingerprint': fingerprint.lower(), - 'fingerprint_type': fp_type - }) - - return { - 'type': _type, - 'ttl': records['ttl'], - 'values': values - } - - def _data_for_TXT(self, _type, records): - values = [] - for r in records['rdata']: - r = r[1:-1] - values.append(r.replace(';', '\\;')) - - return { - 'ttl': records['ttl'], - 'type': _type, - 'values': values - } - - def _params_for_multiple(self, values): - return [r for r in values] - - def _params_for_single(self, values): - return values - - _params_for_A = _params_for_multiple - _params_for_AAAA = _params_for_multiple - _params_for_NS = _params_for_multiple - - _params_for_CNAME = _params_for_single - _params_for_PTR = _params_for_single - - def _params_for_MX(self, values): - rdata = [] - - for r in values: - preference = r['preference'] - exchange = r['exchange'] - rdata.append(f'{preference} {exchange}') - - return rdata - - def _params_for_NAPTR(self, values): - rdata = [] - - for r in values: - ordr = r['order'] - prf = r['preference'] - flg = "\"" + r['flags'] + "\"" - srvc = "\"" + r['service'] + "\"" - rgx = "\"" + r['regexp'] + "\"" - rpl = r['replacement'] - rdata.append(f'{ordr} {prf} {flg} {srvc} {rgx} {rpl}') - - return rdata - - def _params_for_SPF(self, values): - rdata = [] - - for r in values: - txt = "\"" + r.replace('\\;', ';') + "\"" - rdata.append(txt) - - return rdata - - def _params_for_SRV(self, values): - rdata = [] - for r in values: - priority = r['priority'] - weight = r['weight'] - port = r['port'] - target = r['target'] - rdata.append(f'{priority} {weight} {port} {target}') - - return rdata - - def _params_for_SSHFP(self, values): - rdata = [] - for r in values: - algorithm = r['algorithm'] - fp_type = r['fingerprint_type'] - fp = r['fingerprint'] - - rdata.append(f'{algorithm} {fp_type} {fp}') - - return rdata - - def _params_for_TXT(self, values): - rdata = [] - - for r in values: - txt = "\"" + r.replace('\\;', ';') + "\"" - rdata.append(txt) - - return rdata - - def _build_zone_config(self, zone, _type="primary", comment=None, - masters=[]): - - if self._contractId is None: - raise NameError("contractId not specified to create zone") - - return { - "zone": zone, - "type": _type, - "comment": comment, - "masters": masters - } - - def _get_values(self, data): - - try: - vals = data['values'] - except KeyError: - vals = [data['value']] - - return vals - - def _set_full_name(self, name, zone): - name = name + '.' + zone - - # octodns's name for root is '' - if (name[0] == '.'): - name = name[1:] - return name +logger = getLogger('Akamai') +try: + logger.warn('octodns_edgedns shimmed. Update your provider class to ' + 'octodns_edgedns.AkamaiProvider. ' + 'Shim will be removed in 1.0') + from octodns_edgedns import AkamaiProvider + AkamaiProvider # pragma: no cover +except ModuleNotFoundError: + logger.exception('AkamaiProvider has been moved into a seperate module, ' + 'octodns_edgedns is now required. Provider class should ' + 'be updated to octodns_edgedns.AkamaiProvider. See ' + 'https://github.com/octodns/octodns/README.md#updating-' + 'to-use-extracted-providers for more information.') + raise diff --git a/octodns/provider/fastdns.py b/octodns/provider/fastdns.py index 6aa842d..b422e38 100644 --- a/octodns/provider/fastdns.py +++ b/octodns/provider/fastdns.py @@ -5,12 +5,11 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -from .edgedns import AkamaiProvider from logging import getLogger -# Quell unused warning -AkamaiProvider - -log = getLogger('octodns.provider.fastdns.AkamaiProvider') -log.warn('DEPRECATION NOTICE: AkamaiProvider has been moved to ' - 'octodns.provider.fastdns.AkamaiProvider') +logger = getLogger('Akamai') +logger.warn('AkamaiProvider has been moved into a seperate module, ' + 'octodns_edgedns is now required. Provider class should ' + 'be updated to octodns_edgedns.AkamaiProvider. See ' + 'https://github.com/octodns/octodns/README.md#updating-' + 'to-use-extracted-providers for more information.') diff --git a/requirements.txt b/requirements.txt index 1ee7cd8..d4e6353 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,6 @@ azure-mgmt-dns==8.0.0 azure-mgmt-trafficmanager==0.51.0 dnspython==1.16.0 docutils==0.16 -edgegrid-python==1.1.1 fqdn==1.5.0 google-cloud-core==1.4.1 google-cloud-dns==0.32.0 diff --git a/tests/fixtures/edgedns-invalid-content.json b/tests/fixtures/edgedns-invalid-content.json deleted file mode 100644 index 8932f66..0000000 --- a/tests/fixtures/edgedns-invalid-content.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "recordsets": [ - { - "rdata": [ - "", - "12 20 foo-2.unit.tests." - ], - "type": "SRV", - "name": "_srv._tcp.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "", - "1 1" - ], - "type": "SSHFP", - "name": "unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "", - "100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." - ], - "type": "NAPTR", - "name": "naptr.unit.tests", - "ttl": 600 - } - ], - "metadata": { - "totalElements": 3, - "showAll": true - } -} \ No newline at end of file diff --git a/tests/fixtures/edgedns-records-prev-other.json b/tests/fixtures/edgedns-records-prev-other.json deleted file mode 100644 index acae3ec..0000000 --- a/tests/fixtures/edgedns-records-prev-other.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "recordsets": [ - { - "rdata": [ - "10 20 30 foo-1.other.tests.", - "12 20 30 foo-2.other.tests." - ], - "type": "SRV", - "name": "_srv._tcp.old.other.tests", - "ttl": 600 - }, - { - "rdata": [ - "10 20 30 foo-1.other.tests.", - "12 20 30 foo-2.other.tests." - ], - "type": "SRV", - "name": "_srv._tcp.old.other.tests", - "ttl": 600 - }, - { - "rdata": [ - "2601:644:500:e210:62f8:1dff:feb8:9471" - ], - "type": "AAAA", - "name": "aaaa.old.other.tests", - "ttl": 600 - }, - { - "rdata": [ - "ns1.akam.net.", - "ns2.akam.net.", - "ns3.akam.net.", - "ns4.akam.net." - ], - "type": "NS", - "name": "old.other.tests", - "ttl": 3600 - }, - { - "rdata": [ - "1.2.3.4", - "1.2.3.5" - ], - "type": "A", - "name": "old.other.tests", - "ttl": 300 - }, - { - "rdata": [ - "ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300" - ], - "type": "SOA", - "name": "other.tests", - "ttl": 3600 - }, - { - "rdata": [ - "1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49", - "1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73" - ], - "type": "SSHFP", - "name": "old.other.tests", - "ttl": 3600 - }, - { - "rdata": [ - "other.tests." - ], - "type": "CNAME", - "name": "old.cname.other.tests", - "ttl": 300 - }, - { - "rdata": [ - "other.tests." - ], - "type": "CNAME", - "name": "excluded.old.other.tests", - "ttl": 3600 - }, - { - "rdata": [ - "other.tests." - ], - "type": "CNAME", - "name": "included.old.other.tests", - "ttl": 3600 - }, - { - "rdata": [ - "10 smtp-4.other.tests.", - "20 smtp-2.other.tests.", - "30 smtp-3.other.tests.", - "40 smtp-1.other.tests." - ], - "type": "MX", - "name": "mx.old.other.tests", - "ttl": 300 - }, - { - "rdata": [ - "10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" .", - "100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." - ], - "type": "NAPTR", - "name": "naptr.old.other.tests", - "ttl": 600 - }, - { - "rdata": [ - "foo.bar.com." - ], - "type": "PTR", - "name": "ptr.old.other.tests", - "ttl": 300 - }, - { - "rdata": [ - "\"v=spf1 ip4:192.168.0.1/16-all\"" - ], - "type": "SPF", - "name": "spf.old.other.tests", - "ttl": 600 - }, - { - "rdata": [ - "ns1.other.tests.", - "ns2.other.tests." - ], - "type": "NS", - "name": "under.old.other.tests", - "ttl": 3600 - }, - { - "rdata": [ - "\"Bah bah black sheep\"", - "\"have you any wool.\"", - "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"" - ], - "type": "TXT", - "name": "txt.old.other.tests", - "ttl": 600 - }, - { - "rdata": [ - "2.2.3.7" - ], - "type": "A", - "name": "www.other.tests", - "ttl": 300 - }, - { - "rdata": [ - "2.2.3.6" - ], - "type": "A", - "name": "www.sub.old.other.tests", - "ttl": 300 - } - ], - "metadata": { - "totalElements": 16, - "showAll": true - } -} \ No newline at end of file diff --git a/tests/fixtures/edgedns-records-prev.json b/tests/fixtures/edgedns-records-prev.json deleted file mode 100644 index b07c63f..0000000 --- a/tests/fixtures/edgedns-records-prev.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "recordsets": [ - { - "rdata": [ - "10 20 30 foo-1.unit.tests.", - "12 20 30 foo-2.unit.tests." - ], - "type": "SRV", - "name": "_srv._tcp.old.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "10 20 30 foo-1.unit.tests.", - "12 20 30 foo-2.unit.tests." - ], - "type": "SRV", - "name": "_srv._tcp.old.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "2601:644:500:e210:62f8:1dff:feb8:9471" - ], - "type": "AAAA", - "name": "aaaa.old.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "ns1.akam.net.", - "ns2.akam.net.", - "ns3.akam.net.", - "ns4.akam.net." - ], - "type": "NS", - "name": "old.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "1.2.3.4", - "1.2.3.5" - ], - "type": "A", - "name": "old.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300" - ], - "type": "SOA", - "name": "unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49", - "1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73" - ], - "type": "SSHFP", - "name": "old.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "unit.tests" - ], - "type": "CNAME", - "name": "old.cname.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "unit.tests." - ], - "type": "CNAME", - "name": "excluded.old.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "unit.tests." - ], - "type": "CNAME", - "name": "included.old.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "10 smtp-4.unit.tests.", - "20 smtp-2.unit.tests.", - "30 smtp-3.unit.tests.", - "40 smtp-1.unit.tests." - ], - "type": "MX", - "name": "mx.old.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" .", - "100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." - ], - "type": "NAPTR", - "name": "naptr.old.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "foo.bar.com." - ], - "type": "PTR", - "name": "ptr.old.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "\"v=spf1 ip4:192.168.0.1/16-all\"" - ], - "type": "SPF", - "name": "spf.old.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "ns1.unit.tests.", - "ns2.unit.tests." - ], - "type": "NS", - "name": "under.old.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "\"Bah bah black sheep\"", - "\"have you any wool.\"", - "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"" - ], - "type": "TXT", - "name": "txt.old.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "2.2.3.7" - ], - "type": "A", - "name": "www.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "2.2.3.6" - ], - "type": "A", - "name": "www.sub.old.unit.tests", - "ttl": 300 - } - ], - "metadata": { - "totalElements": 16, - "showAll": true - } -} \ No newline at end of file diff --git a/tests/fixtures/edgedns-records.json b/tests/fixtures/edgedns-records.json deleted file mode 100644 index a5ce14f..0000000 --- a/tests/fixtures/edgedns-records.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "recordsets": [ - { - "rdata": [ - "10 20 30 foo-1.unit.tests.", - "12 20 30 foo-2.unit.tests." - ], - "type": "SRV", - "name": "_srv._tcp.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "0 0 0 ." - ], - "type": "SRV", - "name": "_imap._tcp.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "0 0 0 ." - ], - "type": "SRV", - "name": "_pop3._tcp.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "2601:644:500:e210:62f8:1dff:feb8:947a" - ], - "type": "AAAA", - "name": "aaaa.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "ns1.akam.net.", - "ns2.akam.net.", - "ns3.akam.net.", - "ns4.akam.net." - ], - "type": "NS", - "name": "unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "1.2.3.4", - "1.2.3.5" - ], - "type": "A", - "name": "unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "ns1.akam.net hostmaster.akamai.com 1489074932 86400 7200 604800 300" - ], - "type": "SOA", - "name": "unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "1 1 7491973e5f8b39d5327cd4e08bc81b05f7710b49", - "1 1 bf6b6825d2977c511a475bbefb88aad54a92ac73" - ], - "type": "SSHFP", - "name": "unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "unit.tests." - ], - "type": "CNAME", - "name": "cname.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "unit.tests." - ], - "type": "CNAME", - "name": "excluded.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "unit.tests." - ], - "type": "CNAME", - "name": "included.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "10 smtp-4.unit.tests.", - "20 smtp-2.unit.tests.", - "30 smtp-3.unit.tests.", - "40 smtp-1.unit.tests." - ], - "type": "MX", - "name": "mx.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "10 100 \"S\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" .", - "100 100 \"U\" \"SIP+D2U\" \"!^.*$!sip:info@bar.example.com!\" ." - ], - "type": "NAPTR", - "name": "naptr.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "foo.bar.com." - ], - "type": "PTR", - "name": "ptr.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "\"v=spf1 ip4:192.168.0.1/16-all\"" - ], - "type": "SPF", - "name": "spf.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "ns1.unit.tests.", - "ns2.unit.tests." - ], - "type": "NS", - "name": "under.unit.tests", - "ttl": 3600 - }, - { - "rdata": [ - "\"Bah bah black sheep\"", - "\"have you any wool.\"", - "\"v=DKIM1;k=rsa;s=email;h=sha256;p=A/kinda+of/long/string+with+numb3rs\"" - ], - "type": "TXT", - "name": "txt.unit.tests", - "ttl": 600 - }, - { - "rdata": [ - "2.2.3.6" - ], - "type": "A", - "name": "www.unit.tests", - "ttl": 300 - }, - { - "rdata": [ - "2.2.3.6" - ], - "type": "A", - "name": "www.sub.unit.tests", - "ttl": 300 - } - ], - "metadata": { - "totalElements": 18, - "showAll": true - } -} diff --git a/tests/test_octodns_provider_edgedns.py b/tests/test_octodns_provider_edgedns.py index 146c435..d1ba0d3 100644 --- a/tests/test_octodns_provider_edgedns.py +++ b/tests/test_octodns_provider_edgedns.py @@ -5,153 +5,17 @@ from __future__ import absolute_import, division, print_function, \ unicode_literals -# from mock import Mock, call -from os.path import dirname, join -from requests import HTTPError -from requests_mock import ANY, mock as requests_mock from unittest import TestCase -from octodns.record import Record -from octodns.provider.edgedns import AkamaiProvider -from octodns.provider.fastdns import AkamaiProvider as LegacyAkamaiProvider -from octodns.provider.yaml import YamlProvider -from octodns.zone import Zone +# Just for coverage +import octodns.provider.fastdns +# Quell warnings +octodns.provider.fastdns -class TestEdgeDnsProvider(TestCase): - expected = Zone('unit.tests.', []) - source = YamlProvider('test', join(dirname(__file__), 'config')) - source.populate(expected) +class TestAkamaiShim(TestCase): - # Our test suite differs a bit, add our NS and remove the simple one - expected.add_record(Record.new(expected, 'under', { - 'ttl': 3600, - 'type': 'NS', - 'values': [ - 'ns1.unit.tests.', - 'ns2.unit.tests.', - ] - })) - for record in list(expected.records): - if record.name == 'sub' and record._type == 'NS': - expected._remove_record(record) - break - - def test_populate(self): - provider = AkamaiProvider("test", "secret", "akam.com", "atok", "ctok") - - # Bad Auth - with requests_mock() as mock: - mock.get(ANY, status_code=401, text='{"message": "Unauthorized"}') - - with self.assertRaises(Exception) as ctx: - zone = Zone('unit.tests.', []) - provider.populate(zone) - - self.assertEquals(401, ctx.exception.response.status_code) - - # general error - with requests_mock() as mock: - mock.get(ANY, status_code=502, text='Things caught fire') - - with self.assertRaises(HTTPError) as ctx: - zone = Zone('unit.tests.', []) - provider.populate(zone) - self.assertEquals(502, ctx.exception.response.status_code) - - # Non-existant zone doesn't populate anything - with requests_mock() as mock: - mock.get(ANY, status_code=404, - text='{"message": "Domain `foo.bar` not found"}') - - zone = Zone('unit.tests.', []) - provider.populate(zone) - self.assertEquals(set(), zone.records) - - # No diffs == no changes - with requests_mock() as mock: - - with open('tests/fixtures/edgedns-records.json') as fh: - mock.get(ANY, text=fh.read()) - - zone = Zone('unit.tests.', []) - provider.populate(zone) - self.assertEquals(18, len(zone.records)) - changes = self.expected.changes(zone, provider) - self.assertEquals(0, len(changes)) - - # 2nd populate makes no network calls/all from cache - again = Zone('unit.tests.', []) - provider.populate(again) - self.assertEquals(18, len(again.records)) - - # bust the cache - del provider._zone_records[zone.name] - - def test_apply(self): - provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok", - "cid", "gid") - - # tests create update delete through previous state config json - with requests_mock() as mock: - - with open('tests/fixtures/edgedns-records-prev.json') as fh: - mock.get(ANY, text=fh.read()) - - plan = provider.plan(self.expected) - mock.post(ANY, status_code=201) - mock.put(ANY, status_code=200) - mock.delete(ANY, status_code=204) - - changes = provider.apply(plan) - self.assertEquals(31, changes) - - # Test against a zone that doesn't exist yet - with requests_mock() as mock: - with open('tests/fixtures/edgedns-records-prev-other.json') as fh: - mock.get(ANY, status_code=404) - - plan = provider.plan(self.expected) - mock.post(ANY, status_code=201) - mock.put(ANY, status_code=200) - mock.delete(ANY, status_code=204) - - changes = provider.apply(plan) - self.assertEquals(16, changes) - - # Test against a zone that doesn't exist yet, but gid not provided - with requests_mock() as mock: - with open('tests/fixtures/edgedns-records-prev-other.json') as fh: - mock.get(ANY, status_code=404) - provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok", - "cid") - plan = provider.plan(self.expected) - mock.post(ANY, status_code=201) - mock.put(ANY, status_code=200) - mock.delete(ANY, status_code=204) - - changes = provider.apply(plan) - self.assertEquals(16, changes) - - # Test against a zone that doesn't exist, but cid not provided - - with requests_mock() as mock: - mock.get(ANY, status_code=404) - - provider = AkamaiProvider("test", "s", "akam.com", "atok", "ctok") - plan = provider.plan(self.expected) - mock.post(ANY, status_code=201) - mock.put(ANY, status_code=200) - mock.delete(ANY, status_code=204) - - try: - changes = provider.apply(plan) - except NameError as e: - expected = "contractId not specified to create zone" - self.assertEquals(str(e), expected) - - -class TestDeprecatedAkamaiProvider(TestCase): - - def test_equivilent(self): - self.assertEquals(LegacyAkamaiProvider, AkamaiProvider) + def test_missing(self): + with self.assertRaises(ModuleNotFoundError): + from octodns.provider.edgedns import AkamaiProvider + AkamaiProvider