diff --git a/gcloud/dns/__init__.py b/gcloud/dns/__init__.py new file mode 100644 index 000000000000..315d6f1a3fe8 --- /dev/null +++ b/gcloud/dns/__init__.py @@ -0,0 +1,26 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed 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. + +"""Google Cloud DNS API wrapper. + +The main concepts with this API are: + +- :class:`gcloud.DNS.zone.ManagedZone` represents an collection of tables. +""" + +from gcloud.dns.client import Client +from gcloud.dns.connection import Connection + + +SCOPE = Connection.SCOPE diff --git a/gcloud/dns/client.py b/gcloud/dns/client.py new file mode 100644 index 000000000000..40b84acdaf1c --- /dev/null +++ b/gcloud/dns/client.py @@ -0,0 +1,58 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed 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. + +"""gcloud dns client for interacting with API.""" + + +from gcloud.client import JSONClient +from gcloud.dns.connection import Connection + + +class Client(JSONClient): + """Client to bundle configuration needed for API requests. + + :type project: string + :param project: the project which the client acts on behalf of. Will be + passed when creating a dataset / job. If not passed, + falls back to the default inferred from the environment. + + :type credentials: :class:`oauth2client.client.OAuth2Credentials` or + :class:`NoneType` + :param credentials: The OAuth2 Credentials to use for the connection + owned by this client. If not passed (and if no ``http`` + object is passed), falls back to the default inferred + from the environment. + + :type http: :class:`httplib2.Http` or class that defines ``request()``. + :param http: An optional HTTP object to make requests. If not passed, an + ``http`` object is created that is bound to the + ``credentials`` for the current object. + """ + + _connection_class = Connection + + def quotas(self): + """Return DNS quots for the project associated with this client. + + See: + https://cloud.google.com/dns/api/v1/projects/get + + :rtype: mapping + :returns: keys for the mapping correspond to those of the ``quota`` + sub-mapping of the project resource. + """ + path = '/projects/%s' % (self.project,) + resp = self.connection.api_request(method='GET', path=path) + return dict([(key, int(value)) + for key, value in resp['quota'].items()]) diff --git a/gcloud/dns/connection.py b/gcloud/dns/connection.py new file mode 100644 index 000000000000..e2b382fd9daa --- /dev/null +++ b/gcloud/dns/connection.py @@ -0,0 +1,33 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed 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. + +"""Create / interact with gcloud dns connections.""" + +from gcloud import connection as base_connection + + +class Connection(base_connection.JSONConnection): + """A connection to Google Cloud DNS via the JSON REST API.""" + + API_BASE_URL = 'https://www.googleapis.com' + """The base of the API call URL.""" + + API_VERSION = 'v1' + """The version of the API, used in building the API call's URL.""" + + API_URL_TEMPLATE = '{api_base_url}/dns/{api_version}{path}' + """A template for the URL of a particular API call.""" + + SCOPE = ('https://www.googleapis.com/auth/ndev.clouddns.readwrite',) + """The scopes required for authenticating as a Cloud DNS consumer.""" diff --git a/gcloud/dns/test_client.py b/gcloud/dns/test_client.py new file mode 100644 index 000000000000..fdba2c7cbf82 --- /dev/null +++ b/gcloud/dns/test_client.py @@ -0,0 +1,94 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed 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 unittest2 + + +class TestClient(unittest2.TestCase): + + def _getTargetClass(self): + from gcloud.dns.client import Client + return Client + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test_ctor(self): + from gcloud.dns.connection import Connection + PROJECT = 'PROJECT' + creds = _Credentials() + http = object() + client = self._makeOne(project=PROJECT, credentials=creds, http=http) + self.assertTrue(isinstance(client.connection, Connection)) + self.assertTrue(client.connection.credentials is creds) + self.assertTrue(client.connection.http is http) + + def test_quotas_defaults(self): + PROJECT = 'PROJECT' + PATH = 'projects/%s' % PROJECT + MANAGED_ZONES = 1234 + RRS_PER_RRSET = 23 + RRSETS_PER_ZONE = 345 + RRSET_ADDITIONS = 456 + RRSET_DELETIONS = 567 + TOTAL_SIZE = 67890 + DATA = { + 'quota': { + 'managedZones': str(MANAGED_ZONES), + 'resourceRecordsPerRrset': str(RRS_PER_RRSET), + 'rrsetsPerManagedZone': str(RRSETS_PER_ZONE), + 'rrsetAdditionsPerChange': str(RRSET_ADDITIONS), + 'rrsetDeletionsPerChange': str(RRSET_DELETIONS), + 'totalRrdataSizePerChange': str(TOTAL_SIZE), + } + } + CONVERTED = dict([(key, int(value)) + for key, value in DATA['quota'].items()]) + creds = _Credentials() + client = self._makeOne(PROJECT, creds) + conn = client.connection = _Connection(DATA) + + quotas = client.quotas() + + self.assertEqual(quotas, CONVERTED) + + self.assertEqual(len(conn._requested), 1) + req = conn._requested[0] + self.assertEqual(req['method'], 'GET') + self.assertEqual(req['path'], '/%s' % PATH) + + +class _Credentials(object): + + _scopes = None + + @staticmethod + def create_scoped_required(): + return True + + def create_scoped(self, scope): + self._scopes = scope + return self + + +class _Connection(object): + + def __init__(self, *responses): + self._responses = responses + self._requested = [] + + def api_request(self, **kw): + self._requested.append(kw) + response, self._responses = self._responses[0], self._responses[1:] + return response diff --git a/gcloud/dns/test_connection.py b/gcloud/dns/test_connection.py new file mode 100644 index 000000000000..1a3f777399f5 --- /dev/null +++ b/gcloud/dns/test_connection.py @@ -0,0 +1,47 @@ +# Copyright 2015 Google Inc. All rights reserved. +# +# Licensed 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 unittest2 + + +class TestConnection(unittest2.TestCase): + + def _getTargetClass(self): + from gcloud.dns.connection import Connection + return Connection + + def _makeOne(self, *args, **kw): + return self._getTargetClass()(*args, **kw) + + def test_build_api_url_no_extra_query_params(self): + conn = self._makeOne() + URI = '/'.join([ + conn.API_BASE_URL, + 'dns', + conn.API_VERSION, + 'foo', + ]) + self.assertEqual(conn.build_api_url('/foo'), URI) + + def test_build_api_url_w_extra_query_params(self): + from six.moves.urllib.parse import parse_qsl + from six.moves.urllib.parse import urlsplit + conn = self._makeOne() + uri = conn.build_api_url('/foo', {'bar': 'baz'}) + scheme, netloc, path, qs, _ = urlsplit(uri) + self.assertEqual('%s://%s' % (scheme, netloc), conn.API_BASE_URL) + self.assertEqual(path, + '/'.join(['', 'dns', conn.API_VERSION, 'foo'])) + parms = dict(parse_qsl(qs)) + self.assertEqual(parms['bar'], 'baz')