tools/dns-sync/dns_sync/api.py (72 lines of code) (raw):

# Copyright 2017 Google Inc. # # 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 logging import threading from google.cloud import datastore from google.cloud import resource_manager from googleapiclient import discovery from googleapiclient import errors import httplib2 from oauth2client import client import webapp2 def resource_iterator(next_page_function): """Loop through resources from a Google API. An iterator that returns all of the resources from a Google API 'list' operation paging through each set. Args: next_page_function: A function that when called will return the next page of results. Yields: A list if resources, which are typically dictionaries. """ next_page_token = None more_results = True while more_results: resource_response = None try: resource_response = next_page_function(next_page_token).execute() except errors.HttpError: # Some projects throw a 403. (compute engine isn't enabled) # just ignore those resources. logging.debug('skipping resources.', exc_info=True) return for items_field in ['items', 'rrsets', 'managedZones']: items = resource_response.get(items_field, {}) if items and (type(items) == dict): for item in items.iteritems(): yield item if items and (type(items) == list): for item in items: yield item if 'nextPageToken' in resource_response: next_page_token = resource_response['nextPageToken'] else: more_results = False class ThreadsafeClientLocal(object): """A thread local Google API client descriptor. Httplib2 is not threadsafe so each request thread requires it's own threadlocal client object which this creates. Attributes: service: String name of the API to create the client for. version: String version of the API client. """ _class_thread_local = threading.local() def __init__(self, service, version): """Create a thread local API client. Will create the underlying httplib2.Http object on construction, but the underlying API client is lazy constructed. Args: service: Name of API. version: Version of the api. """ self.service = service self.version = version self.http = httplib2.Http(timeout=60) self.cache_discovery = True def __get__(self, instance, instance_type): """Construct the API client.""" if instance is None: return self thread_local = None try: app = webapp2.get_app() # Python Google API clients aren't threadsafe as they use httplib2 # which isn't threadsafe. thread_local = app.registry.get(self) if thread_local is None: thread_local = threading.local() app.registry[self] = thread_local except AssertionError: # When not in a request context, use class thread local. thread_local = ThreadsafeClientLocal._class_thread_local cached_client = getattr(thread_local, 'api', None) if cached_client is None: credentials = client.GoogleCredentials.get_application_default() if credentials.create_scoped_required(): credentials = credentials.create_scoped( 'https://www.googleapis.com/auth/cloud-platform') cached_client = discovery.build( self.service, self.version, http=credentials.authorize(self.http), cache_discovery=self.cache_discovery) thread_local.api = cached_client return cached_client class Clients(object): """Holds API clients. For Google API clients, we use thread local descriptors which creates the client on first access. The "google.cloud" clients are threadsafe and are simple properties. """ metrics = ThreadsafeClientLocal('monitoring', 'v3') compute = ThreadsafeClientLocal('compute', 'v1') dns = ThreadsafeClientLocal('dns', 'v1') iam = ThreadsafeClientLocal('cloudresourcemanager', 'v1') def __init__(self): self.datastore = datastore.Client() self.crm = resource_manager.Client() CLIENTS = Clients()