Added support for the GoDaddy driver to purchase domains, list records, add and 
update records, cancel domains, check availability, validate purchase requests

Signed-off-by: Anthony Shaw <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/libcloud/repo
Commit: http://git-wip-us.apache.org/repos/asf/libcloud/commit/a3d1103c
Tree: http://git-wip-us.apache.org/repos/asf/libcloud/tree/a3d1103c
Diff: http://git-wip-us.apache.org/repos/asf/libcloud/diff/a3d1103c

Branch: refs/heads/trunk
Commit: a3d1103cdee285ba53827d2db0dbf9496b584f9a
Parents: 7b38ce3
Author: Anthony Shaw <[email protected]>
Authored: Fri Nov 27 14:28:23 2015 +1100
Committer: Anthony Shaw <[email protected]>
Committed: Fri Nov 27 17:30:11 2015 +1100

----------------------------------------------------------------------
 libcloud/dns/drivers/godaddy.py                 |  369 ++++-
 .../test/dns/fixtures/godaddy/v1_domains.json   |   77 +
 .../v1_domains_aperture_platform_com.json       |   86 ++
 ...1_domains_aperture_platform_com_records.json |   88 ++
 ...ins_aperture_platform_com_records_A_www.json |    8 +
 .../fixtures/godaddy/v1_domains_available.json  |    7 +
 .../godaddy/v1_domains_purchase_schema_com.json |  432 ++++++
 .../dns/fixtures/godaddy/v1_domains_tlds.json   | 1326 ++++++++++++++++++
 libcloud/test/dns/test_godaddy.py               |  139 ++
 libcloud/test/secrets.py-dist                   |    1 +
 10 files changed, 2495 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/dns/drivers/godaddy.py
----------------------------------------------------------------------
diff --git a/libcloud/dns/drivers/godaddy.py b/libcloud/dns/drivers/godaddy.py
index ce1ae7d..55587ab 100644
--- a/libcloud/dns/drivers/godaddy.py
+++ b/libcloud/dns/drivers/godaddy.py
@@ -51,23 +51,16 @@ class GoDaddyDNSResponse(JsonResponse):
     def parse_body(self):
         if not self.body:
             return None
+        # json.loads doesn't like the regex expressions used in godaddy schema
+        self.body = self.body.replace('\\.', '\\\\.')
 
         data = json.loads(self.body)
         return data
 
     def parse_error(self):
         data = self.parse_body()
-
-        if self.status == httplib.UNAUTHORIZED:
-            raise GoDaddyDNSException('%(code)s:%(message)s' % (data['error']))
-        elif self.status == httplib.PRECONDITION_FAILED:
-            raise GoDaddyDNSException(
-                data['error']['code'], data['error']['message'])
-        elif self.status == httplib.NOT_FOUND:
-            raise GoDaddyDNSException(
-                data['error']['code'], data['error']['message'])
-
-        return self.body
+        raise GoDaddyDNSException(
+            data['code'], data['message'])
 
     def success(self):
         return self.status in self.valid_response_codes
@@ -82,11 +75,8 @@ class GoDaddyDNSConnection(ConnectionKey):
     def __init__(self, key, secret, shopper_id, secure=True, host=None,
                  port=None, url=None, timeout=None,
                  proxy_url=None, backoff=None, retry_delay=None):
-        """
-        Initialize `user_id` and `key`; set `secure` to an ``int`` based on
-        passed value.
-        """
         super(GoDaddyDNSConnection, self).__init__(
+            key,
             secure=secure, host=host,
             port=port, url=url,
             timeout=timeout,
@@ -105,6 +95,13 @@ class GoDaddyDNSConnection(ConnectionKey):
 
 
 class GoDaddyDNSDriver(DNSDriver):
+    """
+    A driver for GoDaddy DNS.
+
+    This is for customers of GoDaddy
+    who wish to purchase, update existing domains
+    and manage records for DNS zones owned by GoDaddy NS servers.
+    """
     type = Provider.GODADDY
     name = 'GoDaddy DNS'
     website = 'https://www.godaddy.com/'
@@ -122,10 +119,22 @@ class GoDaddyDNSDriver(DNSDriver):
 
     def __init__(self, shopper_id, key, secret,
                  secure=True, host=None, port=None):
+        """
+        Instantiate a new `GoDaddyDNSDriver`
+
+        :param  shopper_id: Your customer ID or shopped ID with GoDaddy
+        :type   shopper_id: `str`
+
+        :param  key: Your access key from developer.godaddy.com
+        :type   key: `str`
+
+        :param  secret: Your access key secret
+        :type   secret: `str`
+        """
         super(GoDaddyDNSDriver, self).__init__(key=key, secret=secret,
                                                secure=secure,
                                                host=host, port=port,
-                                               shopper_id=shopper_id)
+                                               shopper_id=str(shopper_id))
 
     def _to_zones(self, items):
         zones = []
@@ -134,13 +143,9 @@ class GoDaddyDNSDriver(DNSDriver):
         return zones
 
     def _to_zone(self, item):
-        extra = {}
-        if 'records' in item:
-            extra['records'] = item['records']
-        if item['type'] == 'NATIVE':
-            item['type'] = 'master'
-        zone = Zone(id=item['id'], domain=item['name'],
-                    type=item['type'], ttl=item['ttl'],
+        extra = {"expires": item['expires']}
+        zone = Zone(id=item['domainId'], domain=item['domain'],
+                    type='master', ttl=None,
                     driver=self, extra=extra)
         return zone
 
@@ -152,41 +157,329 @@ class GoDaddyDNSDriver(DNSDriver):
         return records
 
     def _to_record(self, item, zone=None):
-        extra = {'ttl': item['ttl']}
+        ttl = item['ttl']
         type = self._string_to_record_type(item['type'])
-        name = item['name'][:-len(zone.domain) - 1]
-        record = Record(id=item['id'], name=name,
-                        type=type, data=item['content'],
-                        zone=zone, driver=self, extra=extra)
+        name = item['name']
+        id = '{0}:{1}'.format(name, type)
+        record = Record(id=id, name=name,
+                        type=type, data=item['data'],
+                        zone=zone, driver=self,
+                        extra={'ttl': ttl})
         return record
 
+    def _to_tlds(self, items):
+        tlds = []
+        for item in items:
+            tlds.append(self._to_tld(item))
+        return tlds
+
+    def _to_tld(self, item):
+        return GoDaddyTLD(
+            name=item['name'],
+            tld_type=item['type']
+        )
+
     def list_zones(self):
+        """
+        Return a list of zones (purchased domains)
+
+        :return: ``list`` of :class:`Zone`
+        """
         result = self.connection.request(
             '/v1/domains/').object
         zones = self._to_zones(result)
         return zones
 
     def list_records(self, zone):
+        """
+        Return a list of records for the provided zone.
+
+        :param zone: Zone to list records for.
+        :type zone: :class:`Zone`
+
+        :return: ``list`` of :class:`Record`
+        """
         result = self.connection.request(
-            '/v1/domains/%s/records' % zone.id).object
+            '/v1/domains/%s/records' % zone.domain).object
         records = self._to_records(items=result, zone=zone)
         return records
 
+    def create_record(self, name, zone, type, data, extra=None):
+        """
+        Create a new record.
+
+        :param name: Record name without the domain name (e.g. www).
+                     Note: If you want to create a record for a base domain
+                     name, you should specify empty string ('') for this
+                     argument.
+        :type  name: ``str``
+
+        :param zone: Zone where the requested record is created.
+        :type  zone: :class:`Zone`
+
+        :param type: DNS record type (A, AAAA, ...).
+        :type  type: :class:`RecordType`
+
+        :param data: Data for the record (depends on the record type).
+        :type  data: ``str``
+
+        :param extra: Extra attributes (driver specific). (optional)
+        :type extra: ``dict``
+
+        :rtype: :class:`Record`
+        """
+        if extra is None:
+            extra = {}
+        new_record = {}
+        if type is RecordType.SRV:
+            new_record = {
+                'type': type,
+                'name': name,
+                'data': data,
+                'priority': 1,
+                'ttl': extra['ttl'] if hasattr(extra, 'ttl') else 5,
+                'service': extra['service']
+                if hasattr(extra, 'service') else '',
+                'protocol': extra['protocol']
+                if hasattr(extra, 'protocol') else '',
+                'port': extra['port']
+                if hasattr(extra, 'port') else '',
+                'weight': extra['weight']
+                if hasattr(extra, 'weight') else ''
+            }
+        else:
+            new_record = {
+                'type': type,
+                'name': name,
+                'data': data,
+                'priority': 1,
+                'ttl': extra['ttl'] if hasattr(extra, 'ttl') else 5
+            }
+        self.connection.request(
+            '/v1/domains/{0}/records'.format(
+                zone.domain), method='PATCH',
+            data=[new_record])
+        id = '{0}:{1}'.format(name, type)
+        return Record(
+            id=id, name=name,
+            type=type, data=data,
+            zone=zone, driver=self,
+            extra=extra)
+
+    def update_record(self, record, name, type, data, extra=None):
+        """
+        Update an existing record.
+
+        :param record: Record to update.
+        :type  record: :class:`Record`
+
+        :param name: Record name without the domain name (e.g. www).
+                     Note: If you want to create a record for a base domain
+                     name, you should specify empty string ('') for this
+                     argument.
+        :type  name: ``str``
+
+        :param type: DNS record type (A, AAAA, ...).
+        :type  type: :class:`RecordType`
+
+        :param data: Data for the record (depends on the record type).
+        :type  data: ``str``
+
+        :param extra: (optional) Extra attributes (driver specific).
+        :type  extra: ``dict``
+
+        :rtype: :class:`Record`
+        """
+        if extra is None:
+            extra = {}
+        new_record = {}
+        if type is RecordType.SRV:
+            new_record = {
+                'type': type,
+                'name': name,
+                'data': data,
+                'priority': 1,
+                'ttl': extra['ttl'] if hasattr(extra, 'ttl') else 5,
+                'service': extra['service']
+                if hasattr(extra, 'service') else '',
+                'protocol': extra['protocol']
+                if hasattr(extra, 'protocol') else '',
+                'port': extra['port']
+                if hasattr(extra, 'port') else '',
+                'weight': extra['weight']
+                if hasattr(extra, 'weight') else ''
+            }
+        else:
+            new_record = {
+                'type': type,
+                'name': name,
+                'data': data,
+                'priority': 1,
+                'ttl': extra['ttl'] if hasattr(extra, 'ttl') else 5
+            }
+        self.connection.request(
+            '/v1/domains/{0}/records'.format(
+                record.zone.domain), method='PUT',
+            data=[new_record])
+        id = '{0}:{1}'.format(name, type)
+        return Record(
+            id=id, name=name,
+            type=type, data=data,
+            zone=record.zone, driver=self,
+            extra=extra)
+
+    def get_record(self, zone_id, record_id):
+        """
+        Return a Record instance.
+
+        :param zone_id: ID of the required zone
+        :type  zone_id: ``str``
+
+        :param record_id: ID of the required record
+        :type  record_id: ``str``
+
+        :rtype: :class:`Record`
+        """
+        parts = record_id.split(':')
+        result = self.connection.request(
+            '/v1/domains/{0}/records/{1}/{2}'.format(
+                zone_id,
+                parts[1],
+                parts[0])).object
+        if len(result) is 0:
+            raise GoDaddyDNSException("Could not locate record")
+        return self._to_record(result[0],
+                               self.get_zone(zone_id))
+
     def get_zone(self, zone_id):
+        """
+        Get a zone (by domain)
+
+        :param  zone_id: The domain, not the ID
+        :type   zone_id: `str`
+
+        :rtype:  :class:`Zone`
+        """
         result = self.connection.request(
             '/v1/domains/%s/' % zone_id).object
         zone = self._to_zone(result)
         return zone
 
-    def get_record(self, zone_id, record_type, record_name):
+    def delete_zone(self, zone):
+        """
+        Delete a zone.
+
+        Note: This will CANCEL a purchased domain
+
+        :param zone: Zone to delete.
+        :type  zone: :class:`Zone`
+
+        :rtype: ``bool``
+        """
+        self.connection.request(
+            '/v1/domains/%s' % zone.domain,
+            method='DELETE')
+        # no error means ok
+        return True
+
+    def ex_check_availability(self, domain, for_transfer=False):
+        """
+        Check the availability of the domain
+
+        :param   domain: the domain name e.g. wazzlewobbleflooble.com
+        :type    domain: `str`
+
+        :param   for_transfer: Check if domain is available for transfer
+        :type    for_transfer: `bool`
+
+        :rtype: `list` of :class:`GoDaddyAvailability`
+        """
         result = self.connection.request(
-            '/v1/domains/%s/record/%s/%s' % (zone_id, record_type, 
record_name))
-        record = self._to_record(item=result.object,
-                                 zone=self.get_zone(zone_id))
-        return record
+            '/v1/domains/available',
+            method='GET',
+            params={
+                'domain': domain,
+                'forTransfer': str(for_transfer)
+            }
+        ).object
+        return GoDaddyAvailability(
+            domain=result['domain'],
+            available=result['available'],
+            price=result['price'],
+            currency=result['currency'],
+            period=result['period']
+        )
+
+    def ex_list_tlds(self):
+        """
+        List available TLDs for sale
 
-    def delete_zone(self, zone):
+        :rtype: `list` of `GoDaddyTLD`
+        """
+        result = self.connection.request(
+            '/v1/domains/tlds',
+            method='GET'
+        ).object
+        return self._to_tlds(result)
+
+    def ex_get_purchase_schema(self, tld):
+        """
+        Get the schema that needs completing to purchase a new domain
+        Use this in conjunction with ex_purchase_domain
+
+        :param   tld: The top level domain e.g com, eu, uk
+        :type    tld: `str`
+
+        :rtype: `dict` the JSON Schema
+        """
+        result = self.connection.request(
+            '/v1/domains/purchase/schema/{0}'.format(tld),
+            method='GET'
+        ).object
+        return result
+
+    def ex_purchase_domain(self, purchase_request):
+        """
+        Purchase a domain with GoDaddy
+
+        :param  purchase_request: The completed document
+            from ex_get_purchase_schema
+        :type   purchase_request: `dict`
+
+        :rtype: :class:`GoDaddyDomainPurchaseResponse` Your order
+        """
         result = self.connection.request(
-            '/v1/domains/%s' % zone.id,
-            method='DELETE').object
-        return bool(result)
+            '/v1/domains/purchase',
+            data=purchase_request,
+            method='POST'
+        ).object
+        return GoDaddyDomainPurchaseResponse(
+            order_id=result['orderId'],
+            item_count=result['itemCount'],
+            total=result['total'],
+            currency=result['currency']
+        )
+
+
+class GoDaddyAvailability(object):
+    def __init__(self, domain, available, price, currency, period):
+        self.domain = domain
+        self.available = bool(available)
+        # currency comes in micro-units, convert to dollars.
+        self.price = float(price) / 1000000
+        self.currency = currency
+        self.period = int(period)
+
+
+class GoDaddyTLD(object):
+    def __init__(self, name, tld_type):
+        self.name = name
+        self.type = tld_type
+
+
+class GoDaddyDomainPurchaseResponse(object):
+    def __init__(self, order_id, item_count, total, currency):
+        self.order_id = order_id
+        self.item_count = item_count
+        self.total = total
+        self.current = currency

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/godaddy/v1_domains.json 
b/libcloud/test/dns/fixtures/godaddy/v1_domains.json
new file mode 100644
index 0000000..64fa1ca
--- /dev/null
+++ b/libcloud/test/dns/fixtures/godaddy/v1_domains.json
@@ -0,0 +1,77 @@
+[
+  {
+    "domainId": 177184419,
+    "domain": "aperture-platform.com",
+    "status": "ACTIVE",
+    "expires": "2016-09-25T19:29:32Z",
+    "expirationProtected": false,
+    "holdRegistrar": false,
+    "limited": false,
+    "locked": true,
+    "privacy": false,
+    "renewAuto": true,
+    "renewable": true,
+    "smart": false,
+    "transferProtected": false
+  },
+  {
+    "domainId": 203342445,
+    "domain": "dd-innovation-days.com",
+    "status": "ACTIVE",
+    "expires": "2016-11-15T20:10:17Z",
+    "expirationProtected": false,
+    "holdRegistrar": false,
+    "limited": false,
+    "locked": true,
+    "privacy": false,
+    "renewAuto": true,
+    "renewable": true,
+    "smart": false,
+    "transferProtected": false
+  },
+  {
+    "domainId": 183117834,
+    "domain": "givesourcing.com",
+    "status": "ACTIVE",
+    "expires": "2017-01-01T19:00:40Z",
+    "expirationProtected": false,
+    "holdRegistrar": false,
+    "limited": false,
+    "locked": true,
+    "privacy": false,
+    "renewAuto": true,
+    "renewable": true,
+    "smart": false,
+    "transferProtected": false
+  },
+  {
+    "domainId": 200411864,
+    "domain": "service-layer.com",
+    "status": "ACTIVE",
+    "expires": "2018-09-30T18:22:00Z",
+    "expirationProtected": false,
+    "holdRegistrar": false,
+    "limited": false,
+    "locked": true,
+    "privacy": false,
+    "renewAuto": true,
+    "renewable": true,
+    "smart": false,
+    "transferProtected": false
+  },
+  {
+    "domainId": 183221633,
+    "domain": "sourcewise.org",
+    "status": "ACTIVE",
+    "expires": "2017-01-04T04:02:07Z",
+    "expirationProtected": false,
+    "holdRegistrar": false,
+    "limited": false,
+    "locked": true,
+    "privacy": false,
+    "renewAuto": true,
+    "renewable": true,
+    "smart": false,
+    "transferProtected": false
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com.json 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com.json
new file mode 100644
index 0000000..4e47db6
--- /dev/null
+++ b/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com.json
@@ -0,0 +1,86 @@
+{
+  "domainId": 177184419,
+  "domain": "aperture-platform.com",
+  "status": "ACTIVE",
+  "expires": "2016-09-25T19:29:32Z",
+  "expirationProtected": false,
+  "holdRegistrar": false,
+  "limited": false,
+  "locked": true,
+  "privacy": false,
+  "renewAuto": true,
+  "renewable": true,
+  "renewDeadline": "2016-11-09T19:29:32Z",
+  "smart": false,
+  "transferProtected": false,
+  "createdAt": "2014-09-25T19:29:32Z",
+  "authCode": "nowayamiputtingthishere",
+  "nameServers": [
+    "ns25.domaincontrol.com",
+    "ns26.domaincontrol.com"
+  ],
+  "contactRegistrant": {
+    "nameFirst": "Anthony",
+    "nameLast": "Shaw",
+    "organization": "DIMENSION DATA Cloud Solutions Australia",
+    "email": "[email protected]",
+    "phone": 61.414529701,
+    "fax": "",
+    "addressMailing": {
+      "address1": "3 Richardson Place",
+      "address2": "",
+      "city": "North Ryde",
+      "state": "New South Wales",
+      "postalCode": 2113,
+      "country": "AU"
+    }
+  },
+  "contactBilling": {
+    "nameFirst": "Anthony",
+    "nameLast": "Shaw",
+    "organization": "DIMENSION DATA Cloud Solutions Australia",
+    "email": "[email protected]",
+    "phone": 61.414529701,
+    "fax": "",
+    "addressMailing": {
+      "address1": "3 Richardson Place",
+      "address2": "",
+      "city": "North Ryde",
+      "state": "New South Wales",
+      "postalCode": 2113,
+      "country": "AU"
+    }
+  },
+  "contactAdmin": {
+    "nameFirst": "Anthony",
+    "nameLast": "Shaw",
+    "organization": "DIMENSION DATA Cloud Solutions Australia",
+    "email": "[email protected]",
+    "phone": 61.414529701,
+    "fax": "",
+    "addressMailing": {
+      "address1": "3 Richardson Place",
+      "address2": "",
+      "city": "North Ryde",
+      "state": "New South Wales",
+      "postalCode": 2113,
+      "country": "AU"
+    }
+  },
+  "contactTech": {
+    "nameFirst": "Anthony",
+    "nameLast": "Shaw",
+    "organization": "DIMENSION DATA Cloud Solutions Australia",
+    "email": "[email protected]",
+    "phone": 61.414529701,
+    "fax": "",
+    "addressMailing": {
+      "address1": "3 Richardson Place",
+      "address2": "",
+      "city": "North Ryde",
+      "state": "New South Wales",
+      "postalCode": 2113,
+      "country": "AU"
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records.json
 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records.json
new file mode 100644
index 0000000..65bf86f
--- /dev/null
+++ 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records.json
@@ -0,0 +1,88 @@
+[
+  {
+    "type": "A",
+    "name": "@",
+    "data": "50.63.202.42",
+    "ttl": 600
+  },
+  {
+    "type": "A",
+    "name": "mercury",
+    "data": "197.96.19.72",
+    "ttl": 3600
+  },
+  {
+    "type": "A",
+    "name": "secure",
+    "data": "175.184.215.248",
+    "ttl": 3600
+  },
+  {
+    "type": "A",
+    "name": "manga",
+    "data": "175.184.205.28",
+    "ttl": 3600
+  },
+  {
+    "type": "CNAME",
+    "name": "email",
+    "data": "email.secureserver.net",
+    "ttl": 3600
+  },
+  {
+    "type": "CNAME",
+    "name": "ftp",
+    "data": "@",
+    "ttl": 3600
+  },
+  {
+    "type": "CNAME",
+    "name": "www",
+    "data": "@",
+    "ttl": 3600
+  },
+  {
+    "type": "MX",
+    "name": "@",
+    "data": "mailstore1.secureserver.net",
+    "priority": 10,
+    "ttl": 3600
+  },
+  {
+    "type": "MX",
+    "name": "@",
+    "data": "smtp.secureserver.net",
+    "priority": 0,
+    "ttl": 3600
+  },
+  {
+    "type": "TXT",
+    "name": "@",
+    "data": "MS=ms30628878",
+    "ttl": 3600
+  },
+  {
+    "type": "AAAA",
+    "name": "wazzle",
+    "data": "2607:f480:111:1336:6503:544c:74a6:3a28",
+    "ttl": 3600
+  },
+  {
+    "type": "AAAA",
+    "name": "flerg",
+    "data": "2607:f480:111:1336:6503:544c:74a6:3a28",
+    "ttl": 3600
+  },
+  {
+    "type": "NS",
+    "name": "@",
+    "data": "ns25.domaincontrol.com",
+    "ttl": 3600
+  },
+  {
+    "type": "NS",
+    "name": "@",
+    "data": "ns26.domaincontrol.com",
+    "ttl": 3600
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records_A_www.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records_A_www.json
 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records_A_www.json
new file mode 100644
index 0000000..14d1251
--- /dev/null
+++ 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_aperture_platform_com_records_A_www.json
@@ -0,0 +1,8 @@
+[
+  {
+    "type": "A",
+    "name": "www",
+    "data": "50.63.202.42",
+    "ttl": 600
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains_available.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/godaddy/v1_domains_available.json 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_available.json
new file mode 100644
index 0000000..3210b86
--- /dev/null
+++ b/libcloud/test/dns/fixtures/godaddy/v1_domains_available.json
@@ -0,0 +1,7 @@
+{
+  "price": 14990000,
+  "currency": "USD",
+  "period": 1,
+  "available": true,
+  "domain": "wazzlewobbleflooble.com"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains_purchase_schema_com.json
----------------------------------------------------------------------
diff --git 
a/libcloud/test/dns/fixtures/godaddy/v1_domains_purchase_schema_com.json 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_purchase_schema_com.json
new file mode 100644
index 0000000..91025a6
--- /dev/null
+++ b/libcloud/test/dns/fixtures/godaddy/v1_domains_purchase_schema_com.json
@@ -0,0 +1,432 @@
+{
+  "id": "https://api.godaddy.com/DomainPurchase#";,
+  "additionalProperties": false,
+  "properties": {
+    "domain": {
+      "type": "string",
+      "format": "domain",
+      "pattern": "^[^.]{1,63}(\.[^.]{2,}){1,2}$"
+    },
+    "consent": {
+      "$ref": "https://api.godaddy.com/DomainPurchase#/definitions/Consent";
+    },
+    "period": {
+      "type": "integer",
+      "format": "integer-positive",
+      "defaultValue": 1,
+      "minimum": 1,
+      "maximum": 10,
+      "pattern": "[1]?[0-9]"
+    },
+    "renewAuto": {
+      "type": "boolean",
+      "defaultValue": true
+    },
+    "privacy": {
+      "type": "boolean",
+      "defaultValue": false
+    },
+    "nameServers": {
+      "type": "array",
+      "items": {
+        "format": "host-name",
+        "pattern": "([^.]+\.)*[^.]+\.[^.]+",
+        "type": "string"
+      },
+      "minItems": 0,
+      "maxItems": 13
+    },
+    "contactAdmin": {
+      "$ref": "https://api.godaddy.com/DomainPurchase#/definitions/Contact";
+    },
+    "contactBilling": {
+      "$ref": "https://api.godaddy.com/DomainPurchase#/definitions/Contact";
+    },
+    "contactRegistrant": {
+      "$ref": "https://api.godaddy.com/DomainPurchase#/definitions/Contact";
+    },
+    "contactTech": {
+      "$ref": "https://api.godaddy.com/DomainPurchase#/definitions/Contact";
+    }
+  },
+  "required": [
+    "domain",
+    "consent",
+    "contactAdmin",
+    "contactBilling",
+    "contactRegistrant",
+    "contactTech"
+  ],
+  "$schema": "http://json-schema.org/draft-04/schema#";,
+  "definitions": {
+    "Consent": {
+      "id": "Consent",
+      "additionalProperties": false,
+      "properties": {
+        "agreementKeys": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          },
+          "description": "Unique identifiers of the legal agreements to which 
the end-user has agreed, as returned from the/domains/agreements endpoint"
+        },
+        "agreedBy": {
+          "type": "string",
+          "description": "Originating client IP address of the end-user's 
computer when they consented to these legal agreements"
+        },
+        "agreedAt": {
+          "type": "string",
+          "format": "iso-datetime",
+          "description": "Timestamp indicating when the end-user consented to 
these legal agreements"
+        }
+      },
+      "required": [
+        "agreementKeys",
+        "agreedBy",
+        "agreedAt"
+      ]
+    },
+    "Contact": {
+      "id": "Contact",
+      "additionalProperties": false,
+      "properties": {
+        "nameFirst": {
+          "type": "string",
+          "format": "person-name",
+          "maxLength": 30
+        },
+        "nameMiddle": {
+          "type": "string"
+        },
+        "nameLast": {
+          "type": "string",
+          "format": "person-name",
+          "maxLength": 30
+        },
+        "organization": {
+          "type": "string",
+          "format": "organization-name",
+          "maxLength": 100
+        },
+        "jobTitle": {
+          "type": "string"
+        },
+        "email": {
+          "type": "string",
+          "format": "email",
+          "maxLength": 80
+        },
+        "phone": {
+          "type": "string",
+          "format": "phone",
+          "maxLength": 17
+        },
+        "fax": {
+          "type": "string",
+          "format": "phone",
+          "maxLength": 17
+        },
+        "addressMailing": {
+          "$ref": "https://api.godaddy.com/DomainPurchase#/definitions/Address";
+        }
+      },
+      "required": [
+        "nameFirst",
+        "nameLast",
+        "email",
+        "phone",
+        "addressMailing"
+      ]
+    },
+    "Address": {
+      "id": "Address",
+      "additionalProperties": false,
+      "properties": {
+        "address1": {
+          "type": "string",
+          "format": "street-address",
+          "maxLength": 41
+        },
+        "address2": {
+          "type": "string",
+          "format": "street-address2",
+          "maxLength": 41
+        },
+        "city": {
+          "type": "string",
+          "format": "city-name",
+          "maxLength": 30
+        },
+        "state": {
+          "type": "string",
+          "format": "state-province-territory",
+          "minLength": 2,
+          "maxLength": 30,
+          "description": "State or province or territory"
+        },
+        "postalCode": {
+          "type": "string",
+          "format": "postal-code",
+          "minLength": 2,
+          "maxLength": 10,
+          "description": "Postal or zip code"
+        },
+        "country": {
+          "type": "string",
+          "format": "iso-country-code",
+          "defaultValue": "US",
+          "enum": [
+            "AD",
+            "AE",
+            "AF",
+            "AG",
+            "AI",
+            "AL",
+            "AM",
+            "AO",
+            "AQ",
+            "AR",
+            "AS",
+            "AT",
+            "AU",
+            "AW",
+            "AZ",
+            "BA",
+            "BB",
+            "BD",
+            "BE",
+            "BF",
+            "BG",
+            "BH",
+            "BI",
+            "BJ",
+            "BM",
+            "BN",
+            "BO",
+            "BQ",
+            "BR",
+            "BS",
+            "BT",
+            "BV",
+            "BW",
+            "BY",
+            "BZ",
+            "CA",
+            "CC",
+            "CD",
+            "CF",
+            "CG",
+            "CH",
+            "CI",
+            "CK",
+            "CL",
+            "CM",
+            "CN",
+            "CO",
+            "CR",
+            "CV",
+            "CW",
+            "CX",
+            "CY",
+            "CZ",
+            "DE",
+            "DJ",
+            "DK",
+            "DM",
+            "DO",
+            "DZ",
+            "EC",
+            "EE",
+            "EG",
+            "EH",
+            "ER",
+            "ES",
+            "ET",
+            "FI",
+            "FJ",
+            "FK",
+            "FM",
+            "FO",
+            "FR",
+            "GA",
+            "GB",
+            "GD",
+            "GE",
+            "GF",
+            "GG",
+            "GH",
+            "GI",
+            "GL",
+            "GM",
+            "GN",
+            "GP",
+            "GQ",
+            "GR",
+            "GS",
+            "GT",
+            "GU",
+            "GW",
+            "GY",
+            "HK",
+            "HM",
+            "HN",
+            "HR",
+            "HT",
+            "HU",
+            "ID",
+            "IE",
+            "IL",
+            "IM",
+            "IN",
+            "IO",
+            "IQ",
+            "IS",
+            "IT",
+            "JE",
+            "JM",
+            "JO",
+            "JP",
+            "KE",
+            "KG",
+            "KH",
+            "KI",
+            "KM",
+            "KN",
+            "KR",
+            "KW",
+            "KY",
+            "KZ",
+            "LA",
+            "LB",
+            "LC",
+            "LI",
+            "LK",
+            "LR",
+            "LS",
+            "LT",
+            "LU",
+            "LV",
+            "LY",
+            "MA",
+            "MC",
+            "MD",
+            "ME",
+            "MG",
+            "MH",
+            "MK",
+            "ML",
+            "MM",
+            "MN",
+            "MO",
+            "MP",
+            "MQ",
+            "MR",
+            "MS",
+            "MT",
+            "MU",
+            "MV",
+            "MW",
+            "MX",
+            "MY",
+            "MZ",
+            "NA",
+            "NC",
+            "NE",
+            "NF",
+            "NG",
+            "NI",
+            "NL",
+            "NO",
+            "NP",
+            "NR",
+            "NU",
+            "NZ",
+            "OM",
+            "PA",
+            "PE",
+            "PF",
+            "PG",
+            "PH",
+            "PK",
+            "PL",
+            "PM",
+            "PN",
+            "PR",
+            "PS",
+            "PT",
+            "PW",
+            "PY",
+            "QA",
+            "RE",
+            "RO",
+            "RS",
+            "RU",
+            "RW",
+            "SA",
+            "SB",
+            "SC",
+            "SE",
+            "SG",
+            "SH",
+            "SI",
+            "SJ",
+            "SK",
+            "SL",
+            "SM",
+            "SN",
+            "SO",
+            "SR",
+            "ST",
+            "SV",
+            "SX",
+            "SZ",
+            "TC",
+            "TD",
+            "TF",
+            "TG",
+            "TH",
+            "TJ",
+            "TK",
+            "TL",
+            "TM",
+            "TN",
+            "TO",
+            "TR",
+            "TT",
+            "TV",
+            "TW",
+            "TZ",
+            "UA",
+            "UG",
+            "UM",
+            "US",
+            "UY",
+            "UZ",
+            "VA",
+            "VC",
+            "VE",
+            "VG",
+            "VI",
+            "VN",
+            "VU",
+            "WF",
+            "WS",
+            "YE",
+            "YT",
+            "ZA",
+            "ZM",
+            "ZW"
+          ],
+          "description": "Two-letter ISO country code to be used as a hint for 
target region<br/><br/>\nNOTE: These are sample values, there are many\n<a 
href='http://www.iso.org/iso/country_codes.htm'>more</a>"
+        }
+      },
+      "required": [
+        "address1",
+        "city",
+        "state",
+        "postalCode",
+        "country"
+      ]
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/fixtures/godaddy/v1_domains_tlds.json
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/fixtures/godaddy/v1_domains_tlds.json 
b/libcloud/test/dns/fixtures/godaddy/v1_domains_tlds.json
new file mode 100644
index 0000000..ae1d554
--- /dev/null
+++ b/libcloud/test/dns/fixtures/godaddy/v1_domains_tlds.json
@@ -0,0 +1,1326 @@
+[
+  {
+    "name": "academy",
+    "type": "GENERIC"
+  },
+  {
+    "name": "accountants",
+    "type": "GENERIC"
+  },
+  {
+    "name": "actor",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ag",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "agency",
+    "type": "GENERIC"
+  },
+  {
+    "name": "airforce",
+    "type": "GENERIC"
+  },
+  {
+    "name": "am",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "apartments",
+    "type": "GENERIC"
+  },
+  {
+    "name": "archi",
+    "type": "GENERIC"
+  },
+  {
+    "name": "army",
+    "type": "GENERIC"
+  },
+  {
+    "name": "associates",
+    "type": "GENERIC"
+  },
+  {
+    "name": "at",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "auction",
+    "type": "GENERIC"
+  },
+  {
+    "name": "audio",
+    "type": "GENERIC"
+  },
+  {
+    "name": "band",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bar",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bargains",
+    "type": "GENERIC"
+  },
+  {
+    "name": "be",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "beer",
+    "type": "GENERIC"
+  },
+  {
+    "name": "best",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bid",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bike",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bingo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bio",
+    "type": "GENERIC"
+  },
+  {
+    "name": "biz",
+    "type": "GENERIC"
+  },
+  {
+    "name": "black",
+    "type": "GENERIC"
+  },
+  {
+    "name": "blackfriday",
+    "type": "GENERIC"
+  },
+  {
+    "name": "blue",
+    "type": "GENERIC"
+  },
+  {
+    "name": "boutique",
+    "type": "GENERIC"
+  },
+  {
+    "name": "build",
+    "type": "GENERIC"
+  },
+  {
+    "name": "builders",
+    "type": "GENERIC"
+  },
+  {
+    "name": "business",
+    "type": "GENERIC"
+  },
+  {
+    "name": "buzz",
+    "type": "GENERIC"
+  },
+  {
+    "name": "bz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "ca",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "cab",
+    "type": "GENERIC"
+  },
+  {
+    "name": "camera",
+    "type": "GENERIC"
+  },
+  {
+    "name": "camp",
+    "type": "GENERIC"
+  },
+  {
+    "name": "capital",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cards",
+    "type": "GENERIC"
+  },
+  {
+    "name": "care",
+    "type": "GENERIC"
+  },
+  {
+    "name": "careers",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cash",
+    "type": "GENERIC"
+  },
+  {
+    "name": "catering",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cc",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "center",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ch",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "chat",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cheap",
+    "type": "GENERIC"
+  },
+  {
+    "name": "christmas",
+    "type": "GENERIC"
+  },
+  {
+    "name": "church",
+    "type": "GENERIC"
+  },
+  {
+    "name": "city",
+    "type": "GENERIC"
+  },
+  {
+    "name": "claims",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cleaning",
+    "type": "GENERIC"
+  },
+  {
+    "name": "click",
+    "type": "GENERIC"
+  },
+  {
+    "name": "clinic",
+    "type": "GENERIC"
+  },
+  {
+    "name": "clothing",
+    "type": "GENERIC"
+  },
+  {
+    "name": "club",
+    "type": "GENERIC"
+  },
+  {
+    "name": "co",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "co.in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "co.nz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "co.uk",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "coach",
+    "type": "GENERIC"
+  },
+  {
+    "name": "codes",
+    "type": "GENERIC"
+  },
+  {
+    "name": "coffee",
+    "type": "GENERIC"
+  },
+  {
+    "name": "com",
+    "type": "GENERIC"
+  },
+  {
+    "name": "com.ag",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "com.bz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "com.co",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "com.es",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "com.mx",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "com.ph",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "com.tw",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "community",
+    "type": "GENERIC"
+  },
+  {
+    "name": "company",
+    "type": "GENERIC"
+  },
+  {
+    "name": "computer",
+    "type": "GENERIC"
+  },
+  {
+    "name": "condos",
+    "type": "GENERIC"
+  },
+  {
+    "name": "construction",
+    "type": "GENERIC"
+  },
+  {
+    "name": "consulting",
+    "type": "GENERIC"
+  },
+  {
+    "name": "contractors",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cooking",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cool",
+    "type": "GENERIC"
+  },
+  {
+    "name": "country",
+    "type": "GENERIC"
+  },
+  {
+    "name": "credit",
+    "type": "GENERIC"
+  },
+  {
+    "name": "creditcard",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cruises",
+    "type": "GENERIC"
+  },
+  {
+    "name": "cz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "dance",
+    "type": "GENERIC"
+  },
+  {
+    "name": "dating",
+    "type": "GENERIC"
+  },
+  {
+    "name": "de",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "deals",
+    "type": "GENERIC"
+  },
+  {
+    "name": "degree",
+    "type": "GENERIC"
+  },
+  {
+    "name": "delivery",
+    "type": "GENERIC"
+  },
+  {
+    "name": "democrat",
+    "type": "GENERIC"
+  },
+  {
+    "name": "dental",
+    "type": "GENERIC"
+  },
+  {
+    "name": "desi",
+    "type": "GENERIC"
+  },
+  {
+    "name": "design",
+    "type": "GENERIC"
+  },
+  {
+    "name": "diamonds",
+    "type": "GENERIC"
+  },
+  {
+    "name": "diet",
+    "type": "GENERIC"
+  },
+  {
+    "name": "digital",
+    "type": "GENERIC"
+  },
+  {
+    "name": "direct",
+    "type": "GENERIC"
+  },
+  {
+    "name": "directory",
+    "type": "GENERIC"
+  },
+  {
+    "name": "discount",
+    "type": "GENERIC"
+  },
+  {
+    "name": "domains",
+    "type": "GENERIC"
+  },
+  {
+    "name": "education",
+    "type": "GENERIC"
+  },
+  {
+    "name": "email",
+    "type": "GENERIC"
+  },
+  {
+    "name": "energy",
+    "type": "GENERIC"
+  },
+  {
+    "name": "engineer",
+    "type": "GENERIC"
+  },
+  {
+    "name": "engineering",
+    "type": "GENERIC"
+  },
+  {
+    "name": "enterprises",
+    "type": "GENERIC"
+  },
+  {
+    "name": "equipment",
+    "type": "GENERIC"
+  },
+  {
+    "name": "es",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "estate",
+    "type": "GENERIC"
+  },
+  {
+    "name": "eu",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "events",
+    "type": "GENERIC"
+  },
+  {
+    "name": "exchange",
+    "type": "GENERIC"
+  },
+  {
+    "name": "expert",
+    "type": "GENERIC"
+  },
+  {
+    "name": "exposed",
+    "type": "GENERIC"
+  },
+  {
+    "name": "fail",
+    "type": "GENERIC"
+  },
+  {
+    "name": "farm",
+    "type": "GENERIC"
+  },
+  {
+    "name": "finance",
+    "type": "GENERIC"
+  },
+  {
+    "name": "financial",
+    "type": "GENERIC"
+  },
+  {
+    "name": "firm.in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "fish",
+    "type": "GENERIC"
+  },
+  {
+    "name": "fishing",
+    "type": "GENERIC"
+  },
+  {
+    "name": "fitness",
+    "type": "GENERIC"
+  },
+  {
+    "name": "flights",
+    "type": "GENERIC"
+  },
+  {
+    "name": "florist",
+    "type": "GENERIC"
+  },
+  {
+    "name": "flowers",
+    "type": "GENERIC"
+  },
+  {
+    "name": "fm",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "forsale",
+    "type": "GENERIC"
+  },
+  {
+    "name": "foundation",
+    "type": "GENERIC"
+  },
+  {
+    "name": "fund",
+    "type": "GENERIC"
+  },
+  {
+    "name": "furniture",
+    "type": "GENERIC"
+  },
+  {
+    "name": "futbol",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gallery",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gen.in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "gift",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gifts",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gives",
+    "type": "GENERIC"
+  },
+  {
+    "name": "glass",
+    "type": "GENERIC"
+  },
+  {
+    "name": "global",
+    "type": "GENERIC"
+  },
+  {
+    "name": "graphics",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gratis",
+    "type": "GENERIC"
+  },
+  {
+    "name": "green",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gripe",
+    "type": "GENERIC"
+  },
+  {
+    "name": "gs",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "guide",
+    "type": "GENERIC"
+  },
+  {
+    "name": "guitars",
+    "type": "GENERIC"
+  },
+  {
+    "name": "guru",
+    "type": "GENERIC"
+  },
+  {
+    "name": "haus",
+    "type": "GENERIC"
+  },
+  {
+    "name": "healthcare",
+    "type": "GENERIC"
+  },
+  {
+    "name": "help",
+    "type": "GENERIC"
+  },
+  {
+    "name": "hiphop",
+    "type": "GENERIC"
+  },
+  {
+    "name": "hiv",
+    "type": "GENERIC"
+  },
+  {
+    "name": "holdings",
+    "type": "GENERIC"
+  },
+  {
+    "name": "holiday",
+    "type": "GENERIC"
+  },
+  {
+    "name": "horse",
+    "type": "GENERIC"
+  },
+  {
+    "name": "host",
+    "type": "GENERIC"
+  },
+  {
+    "name": "hosting",
+    "type": "GENERIC"
+  },
+  {
+    "name": "house",
+    "type": "GENERIC"
+  },
+  {
+    "name": "idv.tw",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "immo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "immobilien",
+    "type": "GENERIC"
+  },
+  {
+    "name": "in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "ind.in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "industries",
+    "type": "GENERIC"
+  },
+  {
+    "name": "info",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ink",
+    "type": "GENERIC"
+  },
+  {
+    "name": "institute",
+    "type": "GENERIC"
+  },
+  {
+    "name": "insure",
+    "type": "GENERIC"
+  },
+  {
+    "name": "international",
+    "type": "GENERIC"
+  },
+  {
+    "name": "investments",
+    "type": "GENERIC"
+  },
+  {
+    "name": "juegos",
+    "type": "GENERIC"
+  },
+  {
+    "name": "kaufen",
+    "type": "GENERIC"
+  },
+  {
+    "name": "kim",
+    "type": "GENERIC"
+  },
+  {
+    "name": "kitchen",
+    "type": "GENERIC"
+  },
+  {
+    "name": "kiwi",
+    "type": "GENERIC"
+  },
+  {
+    "name": "la",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "land",
+    "type": "GENERIC"
+  },
+  {
+    "name": "lease",
+    "type": "GENERIC"
+  },
+  {
+    "name": "legal",
+    "type": "GENERIC"
+  },
+  {
+    "name": "lgbt",
+    "type": "GENERIC"
+  },
+  {
+    "name": "life",
+    "type": "GENERIC"
+  },
+  {
+    "name": "lighting",
+    "type": "GENERIC"
+  },
+  {
+    "name": "limited",
+    "type": "GENERIC"
+  },
+  {
+    "name": "limo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "link",
+    "type": "GENERIC"
+  },
+  {
+    "name": "loans",
+    "type": "GENERIC"
+  },
+  {
+    "name": "london",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ltda",
+    "type": "GENERIC"
+  },
+  {
+    "name": "maison",
+    "type": "GENERIC"
+  },
+  {
+    "name": "management",
+    "type": "GENERIC"
+  },
+  {
+    "name": "market",
+    "type": "GENERIC"
+  },
+  {
+    "name": "marketing",
+    "type": "GENERIC"
+  },
+  {
+    "name": "me",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "me.uk",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "media",
+    "type": "GENERIC"
+  },
+  {
+    "name": "memorial",
+    "type": "GENERIC"
+  },
+  {
+    "name": "menu",
+    "type": "GENERIC"
+  },
+  {
+    "name": "mobi",
+    "type": "GENERIC"
+  },
+  {
+    "name": "moda",
+    "type": "GENERIC"
+  },
+  {
+    "name": "moe",
+    "type": "GENERIC"
+  },
+  {
+    "name": "money",
+    "type": "GENERIC"
+  },
+  {
+    "name": "mortgage",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ms",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "mx",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "nagoya",
+    "type": "GENERIC"
+  },
+  {
+    "name": "navy",
+    "type": "GENERIC"
+  },
+  {
+    "name": "net",
+    "type": "GENERIC"
+  },
+  {
+    "name": "net.ag",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "net.bz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "net.co",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "net.in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "net.nz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "net.ph",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "network",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ninja",
+    "type": "GENERIC"
+  },
+  {
+    "name": "nl",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "nom.co",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "nom.es",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "nyc",
+    "type": "GENERIC"
+  },
+  {
+    "name": "okinawa",
+    "type": "GENERIC"
+  },
+  {
+    "name": "onl",
+    "type": "GENERIC"
+  },
+  {
+    "name": "org",
+    "type": "GENERIC"
+  },
+  {
+    "name": "org.ag",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "org.es",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "org.in",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "org.nz",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "org.ph",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "org.tw",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "org.uk",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "partners",
+    "type": "GENERIC"
+  },
+  {
+    "name": "parts",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ph",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "photo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "photography",
+    "type": "GENERIC"
+  },
+  {
+    "name": "photos",
+    "type": "GENERIC"
+  },
+  {
+    "name": "pics",
+    "type": "GENERIC"
+  },
+  {
+    "name": "pictures",
+    "type": "GENERIC"
+  },
+  {
+    "name": "pink",
+    "type": "GENERIC"
+  },
+  {
+    "name": "pizza",
+    "type": "GENERIC"
+  },
+  {
+    "name": "place",
+    "type": "GENERIC"
+  },
+  {
+    "name": "plumbing",
+    "type": "GENERIC"
+  },
+  {
+    "name": "poker",
+    "type": "GENERIC"
+  },
+  {
+    "name": "press",
+    "type": "GENERIC"
+  },
+  {
+    "name": "productions",
+    "type": "GENERIC"
+  },
+  {
+    "name": "properties",
+    "type": "GENERIC"
+  },
+  {
+    "name": "property",
+    "type": "GENERIC"
+  },
+  {
+    "name": "pub",
+    "type": "GENERIC"
+  },
+  {
+    "name": "qpon",
+    "type": "GENERIC"
+  },
+  {
+    "name": "recipes",
+    "type": "GENERIC"
+  },
+  {
+    "name": "red",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rehab",
+    "type": "GENERIC"
+  },
+  {
+    "name": "reisen",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rentals",
+    "type": "GENERIC"
+  },
+  {
+    "name": "repair",
+    "type": "GENERIC"
+  },
+  {
+    "name": "report",
+    "type": "GENERIC"
+  },
+  {
+    "name": "republican",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rest",
+    "type": "GENERIC"
+  },
+  {
+    "name": "restaurant",
+    "type": "GENERIC"
+  },
+  {
+    "name": "reviews",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rich",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rip",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rocks",
+    "type": "GENERIC"
+  },
+  {
+    "name": "rodeo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ryukyu",
+    "type": "GENERIC"
+  },
+  {
+    "name": "sale",
+    "type": "GENERIC"
+  },
+  {
+    "name": "sarl",
+    "type": "GENERIC"
+  },
+  {
+    "name": "schule",
+    "type": "GENERIC"
+  },
+  {
+    "name": "services",
+    "type": "GENERIC"
+  },
+  {
+    "name": "sexy",
+    "type": "GENERIC"
+  },
+  {
+    "name": "shiksha",
+    "type": "GENERIC"
+  },
+  {
+    "name": "shoes",
+    "type": "GENERIC"
+  },
+  {
+    "name": "singles",
+    "type": "GENERIC"
+  },
+  {
+    "name": "site",
+    "type": "GENERIC"
+  },
+  {
+    "name": "social",
+    "type": "GENERIC"
+  },
+  {
+    "name": "software",
+    "type": "GENERIC"
+  },
+  {
+    "name": "solar",
+    "type": "GENERIC"
+  },
+  {
+    "name": "solutions",
+    "type": "GENERIC"
+  },
+  {
+    "name": "space",
+    "type": "GENERIC"
+  },
+  {
+    "name": "style",
+    "type": "GENERIC"
+  },
+  {
+    "name": "supplies",
+    "type": "GENERIC"
+  },
+  {
+    "name": "supply",
+    "type": "GENERIC"
+  },
+  {
+    "name": "support",
+    "type": "GENERIC"
+  },
+  {
+    "name": "surf",
+    "type": "GENERIC"
+  },
+  {
+    "name": "surgery",
+    "type": "GENERIC"
+  },
+  {
+    "name": "systems",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tattoo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tax",
+    "type": "GENERIC"
+  },
+  {
+    "name": "technology",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tennis",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tienda",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tips",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tires",
+    "type": "GENERIC"
+  },
+  {
+    "name": "today",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tokyo",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tools",
+    "type": "GENERIC"
+  },
+  {
+    "name": "town",
+    "type": "GENERIC"
+  },
+  {
+    "name": "toys",
+    "type": "GENERIC"
+  },
+  {
+    "name": "trade",
+    "type": "GENERIC"
+  },
+  {
+    "name": "training",
+    "type": "GENERIC"
+  },
+  {
+    "name": "tv",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "tw",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "university",
+    "type": "GENERIC"
+  },
+  {
+    "name": "uno",
+    "type": "GENERIC"
+  },
+  {
+    "name": "us",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "vacations",
+    "type": "GENERIC"
+  },
+  {
+    "name": "vegas",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ventures",
+    "type": "GENERIC"
+  },
+  {
+    "name": "vet",
+    "type": "GENERIC"
+  },
+  {
+    "name": "viajes",
+    "type": "GENERIC"
+  },
+  {
+    "name": "video",
+    "type": "GENERIC"
+  },
+  {
+    "name": "villas",
+    "type": "GENERIC"
+  },
+  {
+    "name": "vision",
+    "type": "GENERIC"
+  },
+  {
+    "name": "vodka",
+    "type": "GENERIC"
+  },
+  {
+    "name": "vote",
+    "type": "GENERIC"
+  },
+  {
+    "name": "voto",
+    "type": "GENERIC"
+  },
+  {
+    "name": "voyage",
+    "type": "GENERIC"
+  },
+  {
+    "name": "watch",
+    "type": "GENERIC"
+  },
+  {
+    "name": "webcam",
+    "type": "GENERIC"
+  },
+  {
+    "name": "website",
+    "type": "GENERIC"
+  },
+  {
+    "name": "wiki",
+    "type": "GENERIC"
+  },
+  {
+    "name": "works",
+    "type": "GENERIC"
+  },
+  {
+    "name": "world",
+    "type": "GENERIC"
+  },
+  {
+    "name": "ws",
+    "type": "COUNTRY_CODE"
+  },
+  {
+    "name": "wtf",
+    "type": "GENERIC"
+  },
+  {
+    "name": "xxx",
+    "type": "GENERIC"
+  },
+  {
+    "name": "xyz",
+    "type": "GENERIC"
+  },
+  {
+    "name": "yokohama",
+    "type": "GENERIC"
+  },
+  {
+    "name": "zone",
+    "type": "GENERIC"
+  }
+]
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/dns/test_godaddy.py
----------------------------------------------------------------------
diff --git a/libcloud/test/dns/test_godaddy.py 
b/libcloud/test/dns/test_godaddy.py
new file mode 100644
index 0000000..5aaa648
--- /dev/null
+++ b/libcloud/test/dns/test_godaddy.py
@@ -0,0 +1,139 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+import unittest
+
+from libcloud.utils.py3 import httplib
+from libcloud.dns.drivers.godaddy import GoDaddyDNSDriver
+from libcloud.test import MockHttp
+from libcloud.test.file_fixtures import DNSFileFixtures
+from libcloud.test.secrets import DNS_PARAMS_GODADDY
+from libcloud.dns.base import Zone, RecordType
+
+
+class GoDaddyTests(unittest.TestCase):
+
+    def setUp(self):
+        GoDaddyMockHttp.type = None
+        GoDaddyDNSDriver.connectionCls.conn_classes = (
+            None, GoDaddyMockHttp)
+        self.driver = GoDaddyDNSDriver(*DNS_PARAMS_GODADDY)
+
+    def assertHasKeys(self, dictionary, keys):
+        for key in keys:
+            self.assertTrue(key in dictionary, 'key "%s" not in dictionary' %
+                            (key))
+
+    def test_list_zones(self):
+        zones = self.driver.list_zones()
+        self.assertEqual(len(zones), 5)
+        self.assertEqual(zones[0].id, '177184419')
+        self.assertEqual(zones[0].domain, 'aperture-platform.com')
+
+    def test_ex_check_availability(self):
+        check = self.driver.ex_check_availability("wazzlewobbleflooble.com")
+        self.assertEqual(check.available, True)
+        self.assertEqual(check.price, 14.99)
+
+    def test_ex_list_tlds(self):
+        tlds = self.driver.ex_list_tlds()
+        self.assertEqual(len(tlds), 331)
+        self.assertEqual(tlds[0].name, 'academy')
+        self.assertEqual(tlds[0].type, 'GENERIC')
+
+    def test_ex_get_purchase_schema(self):
+        schema = self.driver.ex_get_purchase_schema('com')
+        self.assertEqual(schema['id'],
+                         'https://api.godaddy.com/DomainPurchase#')
+
+    def test_list_records(self):
+        zone = Zone(id='177184419',
+                    domain='aperture-platform.com',
+                    type='master',
+                    ttl=None,
+                    driver=self.driver)
+        records = self.driver.list_records(zone)
+        self.assertEqual(len(records), 14)
+        self.assertEqual(records[0].type, RecordType.A)
+        self.assertEqual(records[0].name, '@')
+        self.assertEqual(records[0].data, '50.63.202.42')
+        self.assertEqual(records[0].id, '@:A')
+
+    def test_get_record(self):
+        record = self.driver.get_record(
+            'aperture-platform.com',
+            'www:A')
+        self.assertEqual(record.id, 'www:A')
+        self.assertEqual(record.name, 'www')
+        self.assertEqual(record.type, RecordType.A)
+        self.assertEqual(record.data, '50.63.202.42')
+
+    def test_create_record(self):
+        zone = Zone(id='177184419',
+                    domain='aperture-platform.com',
+                    type='master',
+                    ttl=None,
+                    driver=self.driver)
+        record = self.driver.create_record(
+            zone=zone,
+            name='www',
+            type=RecordType.A,
+            data='50.63.202.42'
+        )
+        self.assertEqual(record.id, 'www:A')
+        self.assertEqual(record.name, 'www')
+        self.assertEqual(record.type, RecordType.A)
+        self.assertEqual(record.data, '50.63.202.42')
+
+    def test_get_zone(self):
+        zone = self.driver.get_zone('aperture-platform.com')
+        self.assertEqual(zone.id, '177184419')
+        self.assertEqual(zone.domain, 'aperture-platform.com')
+
+
+class GoDaddyMockHttp(MockHttp):
+    fixtures = DNSFileFixtures('godaddy')
+
+    def _v1_domains(self, method, url, body, headers):
+        body = self.fixtures.load('v1_domains.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_aperture_platform_com(self, method, url, body, headers):
+        body = self.fixtures.load('v1_domains_aperture_platform_com.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_aperture_platform_com_records(self, method, url, body, 
headers):
+        body = 
self.fixtures.load('v1_domains_aperture_platform_com_records.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_available(self, method, url, body, headers):
+        body = self.fixtures.load('v1_domains_available.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_tlds(self, method, url, body, headers):
+        body = self.fixtures.load('v1_domains_tlds.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_aperture_platform_com_records_A_www(self, method, url, 
body, headers):
+        body = 
self.fixtures.load('v1_domains_aperture_platform_com_records_A_www.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+    def _v1_domains_purchase_schema_com(self, method, url, body, headers):
+        body = self.fixtures.load('v1_domains_purchase_schema_com.json')
+        return (httplib.OK, body, {}, httplib.responses[httplib.OK])
+
+if __name__ == '__main__':
+    sys.exit(unittest.main())

http://git-wip-us.apache.org/repos/asf/libcloud/blob/a3d1103c/libcloud/test/secrets.py-dist
----------------------------------------------------------------------
diff --git a/libcloud/test/secrets.py-dist b/libcloud/test/secrets.py-dist
index 9048837..98b5512 100644
--- a/libcloud/test/secrets.py-dist
+++ b/libcloud/test/secrets.py-dist
@@ -79,3 +79,4 @@ DNS_PARAMS_POINTDNS = ('user', 'key')
 DNS_PARAMS_LIQUIDWEB = ('user', 'key')
 DNS_PARAMS_ZONOMI = ('key')
 DNS_PARAMS_DURABLEDNS = ('api_user', 'api_key')
+DNS_PARAMS_GODADDY = ('customer-id', 'api_user', 'api_key')

Reply via email to