Skip to content

Commit

Permalink
Fix googleapis#77 - Initialized gcloud dns.
Browse files Browse the repository at this point in the history
Cleaned up dns.

Addressed comments on dns.

Added function to create changes.

Changed project_id to project and addressed comments.

Made some changes.

Streamlined dns to reflect first draft of design and updated doc strings.

Initialized more doc strings.

Edited some doc strings.

Changed project_name to project.

Updated docstrings and fixed some errors.

Fixed a bug involving changes.
  • Loading branch information
kleyow committed May 24, 2014
1 parent af92e74 commit 6828c0c
Show file tree
Hide file tree
Showing 14 changed files with 743 additions and 0 deletions.
84 changes: 84 additions & 0 deletions gcloud/connection.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import httplib2
import json
import urllib

from gcloud import exceptions


class Connection(object):
Expand Down Expand Up @@ -42,3 +46,83 @@ def http(self):
self._http = self._credentials.authorize(self._http)
return self._http


class JsonConnection(Connection):

API_BASE_URL = 'https://www.googleapis.com'
"""The base of the API call URL."""

_EMPTY = object()
"""A pointer to represent an empty value for default arguments."""

def __init__(self, project=None, *args, **kwargs):

super(JsonConnection, self).__init__(*args, **kwargs)

self.project = project

def build_api_url(self, path, query_params=None, api_base_url=None,
api_version=None):

url = self.API_URL_TEMPLATE.format(
api_base_url=(api_base_url or self.API_BASE_URL),
api_version=(api_version or self.API_VERSION),
path=path)

query_params = query_params or {}
query_params.update({'project': self.project})
url += '?' + urllib.urlencode(query_params)

return url

def make_request(self, method, url, data=None, content_type=None,
headers=None):

headers = headers or {}
headers['Accept-Encoding'] = 'gzip'

if data:
content_length = len(str(data))
else:
content_length = 0

headers['Content-Length'] = content_length

if content_type:
headers['Content-Type'] = content_type

return self.http.request(uri=url, method=method, headers=headers,
body=data)

def api_request(self, method, path=None, query_params=None,
data=None, content_type=None,
api_base_url=None, api_version=None,
expect_json=True):

url = self.build_api_url(path=path, query_params=query_params,
api_base_url=api_base_url,
api_version=api_version)

# Making the executive decision that any dictionary
# data will be sent properly as JSON.
if data and isinstance(data, dict):
data = json.dumps(data)
content_type = 'application/json'

response, content = self.make_request(
method=method, url=url, data=data, content_type=content_type)

# TODO: Add better error handling.
if response.status == 404:
raise exceptions.NotFoundError(response, content)
elif not 200 <= response.status < 300:
raise exceptions.ConnectionError(response, content)

if content and expect_json:
# TODO: Better checking on this header for JSON.
content_type = response.get('content-type', '')
if not content_type.startswith('application/json'):
raise TypeError('Expected JSON, got %s' % content_type)
return json.loads(content)

return content
92 changes: 92 additions & 0 deletions gcloud/dns/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
"""Shortcut methods for getting set up with Google Cloud DNS.
You'll typically use these to get started with the API:
>>> import gcloud.dns
>>> zone = gcloud.dns.get_zone('zone-name-here',
'[email protected]',
'/path/to/private.key')
The main concepts with this API are:
- :class:`gcloud.dns.connection.Connection`
which represents a connection between your machine
and the Cloud DNS API.
- :class:`gcloud.dns.zone.Zone`
which represents a particular zone.
"""


__version__ = '0.1'

# TODO: Allow specific scopes and authorization levels.
SCOPE = ('https://www.googleapis.com/auth/cloud-platform',
'https://www.googleapis.com/auth/ndev.clouddns.readonly',
'https://www.googleapis.com/auth/ndev.clouddns.readwrite')
"""The scope required for authenticating as a Cloud DNS consumer."""


def get_connection(project, client_email, private_key_path):
"""Shortcut method to establish a connection to Cloud DNS.
Use this if you are going to access several zones
with the same set of credentials:
>>> from gcloud import dns
>>> connection = dns.get_connection(project, email, key_path)
>>> zone1 = connection.get_zone('zone1')
>>> zone2 = connection.get_zone('zone2')
:type project: string
:param project: The name of the project to connect to.
:type client_email: string
:param client_email: The e-mail attached to the service account.
:type private_key_path: string
:param private_key_path: The path to a private key file (this file was
given to you when you created the service
account).
:rtype: :class:`gcloud.dns.connection.Connection`
:returns: A connection defined with the proper credentials.
"""

from gcloud.credentials import Credentials
from gcloud.dns.connection import Connection

credentials = Credentials.get_for_service_account(
client_email, private_key_path, scope=SCOPE)
return Connection(project=project, credentials=credentials)


def get_zone(zone, project, client_email, private_key_path):
"""Shortcut method to establish a connection to a particular zone.
You'll generally use this as the first call to working with the API:
>>> from gcloud import dns
>>> zone = dns.get_zone(zone, project, email, key_path)
:type zone: string
:param zone: The id of the zone you want to use.
This is akin to a disk name on a file system.
:type project: string
:param project: The name of the project to connect to.
:type client_email: string
:param client_email: The e-mail attached to the service account.
:type private_key_path: string
:param private_key_path: The path to a private key file (this file was
given to you when you created the service
account).
:rtype: :class:`gcloud.dns.zone.Zone`
:returns: A zone with a connection using the provided credentials.
"""

connection = get_connection(project, client_email, private_key_path)
return connection.get_zone(zone)
21 changes: 21 additions & 0 deletions gcloud/dns/change.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Change(object):
"""A class representing a Change on Cloud DNS.
:type additions: list
:param name: A list of records slated to be added to a zone.
:type deletions: list
:param data: A list of records slated to be deleted to a zone.
"""

def __init__(self, additions=None, deletions=None):
self.additions = additions
self.deletions = deletions

def to_dict(self):
"""Format the change into a dict compatible with Cloud DNS.
:rtype: dict
:returns: A Cloud DNS dict representation of a change.
"""
return {'additions': self.additions, 'deletions': self.deletions}
145 changes: 145 additions & 0 deletions gcloud/dns/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
from gcloud import connection
from gcloud.dns.record import Record
from gcloud.dns.zone import Zone


class Connection(connection.JsonConnection):
"""A connection to Google Cloud DNS via the JSON REST API.
See :class:`gcloud.connection.JsonConnection` for a full list of parameters.
:class:`Connection` differs only in needing a project name
(which you specify when creating a project in the Cloud Console).
"""

API_VERSION = 'v1beta1'
"""The version of the API, used in building the API call's URL."""

API_URL_TEMPLATE = ('{api_base_url}/dns/{api_version}/projects/{path}')
"""A template used to craft the URL pointing toward a particular API call."""

_EMPTY = object()
"""A pointer to represent an empty value for default arguments."""

def __init__(self, project=None, *args, **kwargs):
"""
:type project: string
:param project: The project name to connect to.
"""

super(Connection, self).__init__(*args, **kwargs)

self.project = project

def new_zone(self, zone):
"""Factory method for creating a new (unsaved) zone object.
:type zone: string or :class:`gcloud.dns.zone.Zone`
:param zone: A name of a zone or an existing Zone object.
"""

if isinstance(zone, Zone):
return zone

# Support Python 2 and 3.
try:
string_type = basestring
except NameError:
string_type = str

if isinstance(zone, string_type):
return Zone(connection=self, name=zone)

def create_zone(self, zone, dns_name, description):
"""Create a new zone.
:type zone: string or :class:`gcloud.dns.zone.Zone`
:param zone: The zone name (or zone object) to create.
:rtype: :class:`gcloud.dns.zone.Zone`
:returns: The newly created zone.
"""

zone = self.new_zone(zone)
response = self.api_request(method='POST', path=zone.path,
data={'name': zone.name, 'dnsName': dns_name,
'description': description})
return Zone.from_dict(response, connection=self)

def delete_zone(self, zone, force=False):
"""Delete a zone.
You can use this method to delete a zone by name,
or to delete a zone object::
>>> from gcloud import dns
>>> connection = dns.get_connection(project, email, key_path)
>>> connection.delete_zone('my-zone')
True
You can also delete pass in the zone object::
>>> zone = connection.get_zone('other-zone')
>>> connection.delete_zone(zone)
True
:type zone: string or :class:`gcloud.dns.zone.Zone`
:param zone: The zone name (or zone object) to create.
:type force: bool
:param full: If True, deletes the zones's recordss then deletes it.
:rtype: bool
:returns: True if the zone was deleted.
"""

zone = self.new_zone(zone)

if force:
rrsets = self.get_records(zone)
for rrset in rrsets['rrsets']:
record = Record.from_dict(rrset)
if record.type != 'NS' and record.type != 'SOA':
zone.remove_record(record)
zone.save()

self.api_request(method='DELETE', path=zone.path + zone.name)
return True

def get_zone(self, zone):
"""Get a zone by name.
:type zone: string
:param zone: The name of the zone to get.
:rtype: :class:`gcloud.dns.zone.Zone`
:returns: The zone matching the name provided.
"""

zone = self.new_zone(zone)
response = self.api_request(method='GET', path=zone.path)
return Zone.from_dict(response['managedZones'][0], connection=self)

def get_records(self, zone):
"""Get a list of resource records on a zone.
:type zone: string or :class:`gcloud.dns.zone.Zone`
:param zone: The zone name (or zone object) to get records from.
"""

zone = self.new_zone(zone)
return self.api_request(method='GET', path=zone.path + zone.name +
'/rrsets')

def save_change(self, zone, change):
"""Save a set of changes to a zone.
:type zone: string or :class:`gcloud.dns.zone.Zone`
:param zone: The zone name (or zone object) to save to.
:type change: dict
:param dict: A dict with the addition and deletion lists of records.
"""

zone = self.new_zone(zone)
return self.api_request(method='POST', path=zone.path + zone.name +
'/changes', data=change)
19 changes: 19 additions & 0 deletions gcloud/dns/demo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import os
from gcloud import dns


__all__ = ['get_connection', 'get_zone' 'CLIENT_EMAIL', 'PRIVATE_KEY_PATH',
'PROJECT']


CLIENT_EMAIL = '524635209885-rda26ks46309o10e0nc8rb7d33rn0hlm@developer.gserviceaccount.com'
PRIVATE_KEY_PATH = os.path.join(os.path.dirname(__file__), 'demo.p12')
PROJECT = 'gceremote'


def get_connection():
return dns.get_connection(PROJECT, CLIENT_EMAIL, PRIVATE_KEY_PATH)


def get_zone(zone):
return dns.get_zone(zone, PROJECT, CLIENT_EMAIL, PRIVATE_KEY_PATH)
5 changes: 5 additions & 0 deletions gcloud/dns/demo/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from gcloud import demo
from gcloud import dns


demo.DemoRunner.from_module(dns).run()
Binary file added gcloud/dns/demo/demo.p12
Binary file not shown.
Loading

0 comments on commit 6828c0c

Please sign in to comment.