From f3f76705f6c2964ebf9d0130cdd4d86956ebbbdc Mon Sep 17 00:00:00 2001 From: elibixby Date: Fri, 17 Jul 2015 15:28:27 -0700 Subject: [PATCH 1/9] Initial commit for adding CRM samples --- resourcemanager/__init__.py | 0 resourcemanager/samples/create_project.py | 42 +++++++++++++++++++++++ resourcemanager/samples/delete_project.py | 23 +++++++++++++ resourcemanager/samples/utils.py | 17 +++++++++ 4 files changed, 82 insertions(+) create mode 100644 resourcemanager/__init__.py create mode 100644 resourcemanager/samples/create_project.py create mode 100644 resourcemanager/samples/delete_project.py create mode 100644 resourcemanager/samples/utils.py diff --git a/resourcemanager/__init__.py b/resourcemanager/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/resourcemanager/samples/create_project.py b/resourcemanager/samples/create_project.py new file mode 100644 index 000000000000..558d78faef40 --- /dev/null +++ b/resourcemanager/samples/create_project.py @@ -0,0 +1,42 @@ +from utils import build_client, wait_for_active +from googleapiclient.errors import HttpError +import httplib2 +import random +import argparse +import json + +def create_project(name, id, **labels): + return client.projects().create(body={ + 'projectId': pid_candidate, + 'name': name + 'labels': labels + }).execute() + + +def run(name, id=None, **labels): + project = None + if id is None: + while project is None: + id = "{}-{}-{}".format(*rw.random_words(count=2), random.randint(100, 999))[:30] + try: + project = create_project(client, name, id, **labels) + except HttpError as e: + code, uri, reason = str(e).parse('') + if not reason=="Requested entity already exists": + raise e + else: + project = create_project(client, name, id, **labels) + + return wait_for_active(project) + +parser = argparse.ArgumentParser(description='Create a Google Cloud Project') +parser.add_argument('--name', type=str, help='Human readable name of the project', required=True) +parser.add_argument('--id', type=str, help='Unique ID of the project. Max 30 Characters. Only hyphens, digits, and lower case letters. Leave blank to have a memorable string generated for you') +parser.add_argument('--labels', type=json.loads, help='Json formatted dictionary of labels to apply to the project') + +if __name__=='__main__': + args = parser.parse_args() + if args.labels: + run(args.name, id=args.id, **args.labels) + else: + run(args.name, id=args.id) diff --git a/resourcemanager/samples/delete_project.py b/resourcemanager/samples/delete_project.py new file mode 100644 index 000000000000..78f42967b555 --- /dev/null +++ b/resourcemanager/samples/delete_project.py @@ -0,0 +1,23 @@ +from utils import build_client +import argparse + + +def run(id): + client = build_client() + project = client.projects().delete(projectId=id) + if project['lifecycleState'] == 'DELETE_REQUESTED': + print("Project {} successfully deleted".format(id)) + else: + print("Project {} was not scheduled for deletion: \n + either the project is associated with a billing account, + or is not currently active".format(id)) + + + + +parser = argparse.ArgumentParser(description='Delete a Google Cloud Project') +parser.add_argument('--id', type=str, required=True, help='Unique Id of the project to delete') + +if __name__ == '__main__': + args = parser.parse_args() + run(args.id) diff --git a/resourcemanager/samples/utils.py b/resourcemanager/samples/utils.py new file mode 100644 index 000000000000..ecc457027aaa --- /dev/null +++ b/resourcemanager/samples/utils.py @@ -0,0 +1,17 @@ +from googleapiclient.discovery import build +from oauth2client.client import GoogleCredentials +import httplib2 + +def build_client(): + return build('cloudresourcemanager', + 'v1beta1', + credentials=GoogleCredentials.get_application_default() + http=httplib2.Http(timeout=90) #Long timeout for create requests + +def wait_for_active(project): + timeout=1 + while project['lifecycleState'] != 'ACTIVE': + sleep(timeout) + timeout=timeout*2 + project = client.projects().get(projectId=project_id).execute() + return project From 251f1c18769bfc0d72ebbe43f8badce3f640d62a Mon Sep 17 00:00:00 2001 From: elibixby Date: Fri, 17 Jul 2015 16:20:21 -0700 Subject: [PATCH 2/9] Added tests --- resourcemanager/samples/utils.py | 3 ++- .../tests/test_resource_manager.py | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 resourcemanager/tests/test_resource_manager.py diff --git a/resourcemanager/samples/utils.py b/resourcemanager/samples/utils.py index ecc457027aaa..9f00537a3f46 100644 --- a/resourcemanager/samples/utils.py +++ b/resourcemanager/samples/utils.py @@ -1,12 +1,13 @@ from googleapiclient.discovery import build from oauth2client.client import GoogleCredentials import httplib2 +from time import sleep def build_client(): return build('cloudresourcemanager', 'v1beta1', credentials=GoogleCredentials.get_application_default() - http=httplib2.Http(timeout=90) #Long timeout for create requests + http=httplib2.Http(timeout=90)) #Long timeout for create requests def wait_for_active(project): timeout=1 diff --git a/resourcemanager/tests/test_resource_manager.py b/resourcemanager/tests/test_resource_manager.py new file mode 100644 index 000000000000..92271417823c --- /dev/null +++ b/resourcemanager/tests/test_resource_manager.py @@ -0,0 +1,21 @@ +# Copyright 2015, 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. +# +from resourcemanager.samples import create_project, delete_project +from tests import CloudBaseTest + + +class TestResourceManager(CloudBaseTest): + def test_main(self): + project = create_project.run('Python Docs Samples Test Project') + delete_project.run(project['projectId']) From 2b7a43bfcbdeb9e21723ac131f3849ca7df57782 Mon Sep 17 00:00:00 2001 From: elibixby Date: Fri, 17 Jul 2015 16:26:18 -0700 Subject: [PATCH 3/9] Fixed packaging --- resourcemanager/{samples => }/create_project.py | 0 resourcemanager/{samples => }/delete_project.py | 0 resourcemanager/tests/test_resource_manager.py | 2 +- resourcemanager/{samples => }/utils.py | 0 4 files changed, 1 insertion(+), 1 deletion(-) rename resourcemanager/{samples => }/create_project.py (100%) rename resourcemanager/{samples => }/delete_project.py (100%) rename resourcemanager/{samples => }/utils.py (100%) diff --git a/resourcemanager/samples/create_project.py b/resourcemanager/create_project.py similarity index 100% rename from resourcemanager/samples/create_project.py rename to resourcemanager/create_project.py diff --git a/resourcemanager/samples/delete_project.py b/resourcemanager/delete_project.py similarity index 100% rename from resourcemanager/samples/delete_project.py rename to resourcemanager/delete_project.py diff --git a/resourcemanager/tests/test_resource_manager.py b/resourcemanager/tests/test_resource_manager.py index 92271417823c..6366da5dd051 100644 --- a/resourcemanager/tests/test_resource_manager.py +++ b/resourcemanager/tests/test_resource_manager.py @@ -11,7 +11,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from resourcemanager.samples import create_project, delete_project +from resourcemanager import create_project, delete_project from tests import CloudBaseTest diff --git a/resourcemanager/samples/utils.py b/resourcemanager/utils.py similarity index 100% rename from resourcemanager/samples/utils.py rename to resourcemanager/utils.py From 02d76f8d01e928445b92b7d5f02968c690267380 Mon Sep 17 00:00:00 2001 From: elibixby Date: Fri, 17 Jul 2015 16:34:39 -0700 Subject: [PATCH 4/9] Style and syntax fixes --- resourcemanager/create_project.py | 33 +++++++++++++++++++++---------- resourcemanager/delete_project.py | 11 ++++++----- resourcemanager/utils.py | 11 +++++++---- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/resourcemanager/create_project.py b/resourcemanager/create_project.py index 558d78faef40..e2b7551c289c 100644 --- a/resourcemanager/create_project.py +++ b/resourcemanager/create_project.py @@ -5,6 +5,7 @@ import argparse import json + def create_project(name, id, **labels): return client.projects().create(body={ 'projectId': pid_candidate, @@ -13,29 +14,41 @@ def create_project(name, id, **labels): }).execute() -def run(name, id=None, **labels): - project = None + def run(name, id=None, **labels): + project = None if id is None: while project is None: - id = "{}-{}-{}".format(*rw.random_words(count=2), random.randint(100, 999))[:30] + id = "{}-{}-{}".format(*rw.random_words(count=2), + random.randint(100, 999))[:30] try: project = create_project(client, name, id, **labels) except HttpError as e: - code, uri, reason = str(e).parse('') - if not reason=="Requested entity already exists": + code, uri, reason = str(e).parse( + '') + if not reason == "Requested entity already exists": raise e else: project = create_project(client, name, id, **labels) return wait_for_active(project) -parser = argparse.ArgumentParser(description='Create a Google Cloud Project') -parser.add_argument('--name', type=str, help='Human readable name of the project', required=True) -parser.add_argument('--id', type=str, help='Unique ID of the project. Max 30 Characters. Only hyphens, digits, and lower case letters. Leave blank to have a memorable string generated for you') -parser.add_argument('--labels', type=json.loads, help='Json formatted dictionary of labels to apply to the project') + +parser = argparse.ArgumentParser(description = 'Create a Google Cloud Project') +parser.add_argument('--name', + type=str, + help='Human readable name of the project', + required=True) +parser.add_argument('--id', + type=str, + help="""Unique ID of the project. Max 30 Characters. + Only hyphens, digits, and lower case letters. + Leave blank to use a generated string""") +parser.add_argument('--labels', + type=json.loads, + help='Json formatted dictionary of labels') if __name__=='__main__': - args = parser.parse_args() + args = parser.parse_args() if args.labels: run(args.name, id=args.id, **args.labels) else: diff --git a/resourcemanager/delete_project.py b/resourcemanager/delete_project.py index 78f42967b555..828e665c0ca7 100644 --- a/resourcemanager/delete_project.py +++ b/resourcemanager/delete_project.py @@ -8,15 +8,16 @@ def run(id): if project['lifecycleState'] == 'DELETE_REQUESTED': print("Project {} successfully deleted".format(id)) else: - print("Project {} was not scheduled for deletion: \n + print("""Project {} was not scheduled for deletion: either the project is associated with a billing account, - or is not currently active".format(id)) - - + or is not currently active""".format(id)) parser = argparse.ArgumentParser(description='Delete a Google Cloud Project') -parser.add_argument('--id', type=str, required=True, help='Unique Id of the project to delete') +parser.add_argument('--id', + type=str, + required=True, + help='Unique Id of the project to delete') if __name__ == '__main__': args = parser.parse_args() diff --git a/resourcemanager/utils.py b/resourcemanager/utils.py index 9f00537a3f46..9d315d3aee8d 100644 --- a/resourcemanager/utils.py +++ b/resourcemanager/utils.py @@ -3,16 +3,19 @@ import httplib2 from time import sleep + def build_client(): return build('cloudresourcemanager', 'v1beta1', - credentials=GoogleCredentials.get_application_default() - http=httplib2.Http(timeout=90)) #Long timeout for create requests + credentials=GoogleCredentials.get_application_default(), + # Use long timeout for create requests + http=httplib2.Http(timeout=90)) + def wait_for_active(project): - timeout=1 + timeout = 1 while project['lifecycleState'] != 'ACTIVE': sleep(timeout) - timeout=timeout*2 + timeout = timeout*2 project = client.projects().get(projectId=project_id).execute() return project From 416320d3994e598e099d06f88aac4a2cb54611fd Mon Sep 17 00:00:00 2001 From: elibixby Date: Mon, 20 Jul 2015 11:19:46 -0700 Subject: [PATCH 5/9] Added requirements, fixed syntax --- resourcemanager/create_project.py | 49 ++++++++++++++++++------------- resourcemanager/delete_project.py | 3 +- resourcemanager/requirements.txt | 2 ++ resourcemanager/utils.py | 11 ++++--- 4 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 resourcemanager/requirements.txt diff --git a/resourcemanager/create_project.py b/resourcemanager/create_project.py index e2b7551c289c..153dd4cd8e72 100644 --- a/resourcemanager/create_project.py +++ b/resourcemanager/create_project.py @@ -1,39 +1,48 @@ -from utils import build_client, wait_for_active -from googleapiclient.errors import HttpError -import httplib2 -import random import argparse import json +import random + +from googleapiclient.errors import HttpError +from random_words import RandomWords +from utils import build_client, wait_for_active + +rw = RandomWords() -def create_project(name, id, **labels): - return client.projects().create(body={ - 'projectId': pid_candidate, - 'name': name + +def create_project(client, name, id, **labels): + return client.projects().create( + body={ + 'projectId': id, + 'name': name, 'labels': labels - }).execute() + } + ).execute() - def run(name, id=None, **labels): - project = None +def run(name, id=None, **labels): + client = build_client() + project = None if id is None: while project is None: - id = "{}-{}-{}".format(*rw.random_words(count=2), + words = rw.random_words(count=2) + id = "{}-{}-{}".format(words[0], + words[1], random.randint(100, 999))[:30] try: project = create_project(client, name, id, **labels) except HttpError as e: code, uri, reason = str(e).parse( - '') + '') if not reason == "Requested entity already exists": raise e else: project = create_project(client, name, id, **labels) - return wait_for_active(project) + return wait_for_active(client, project) -parser = argparse.ArgumentParser(description = 'Create a Google Cloud Project') +parser = argparse.ArgumentParser(description='Create a Google Cloud Project') parser.add_argument('--name', type=str, help='Human readable name of the project', @@ -47,9 +56,9 @@ def run(name, id=None, **labels): type=json.loads, help='Json formatted dictionary of labels') -if __name__=='__main__': +if __name__ == '__main__': args = parser.parse_args() - if args.labels: - run(args.name, id=args.id, **args.labels) - else: - run(args.name, id=args.id) + if args.labels: + run(args.name, id=args.id, **args.labels) + else: + run(args.name, id=args.id) diff --git a/resourcemanager/delete_project.py b/resourcemanager/delete_project.py index 828e665c0ca7..4a399b3244c6 100644 --- a/resourcemanager/delete_project.py +++ b/resourcemanager/delete_project.py @@ -1,6 +1,7 @@ -from utils import build_client import argparse +from utils import build_client + def run(id): client = build_client() diff --git a/resourcemanager/requirements.txt b/resourcemanager/requirements.txt new file mode 100644 index 000000000000..9a75b98bc439 --- /dev/null +++ b/resourcemanager/requirements.txt @@ -0,0 +1,2 @@ +random_words +googleapiclient diff --git a/resourcemanager/utils.py b/resourcemanager/utils.py index 9d315d3aee8d..5e2a2f30d99e 100644 --- a/resourcemanager/utils.py +++ b/resourcemanager/utils.py @@ -1,7 +1,8 @@ +from time import sleep + from googleapiclient.discovery import build -from oauth2client.client import GoogleCredentials import httplib2 -from time import sleep +from oauth2client.client import GoogleCredentials def build_client(): @@ -12,10 +13,12 @@ def build_client(): http=httplib2.Http(timeout=90)) -def wait_for_active(project): +def wait_for_active(client, project): timeout = 1 while project['lifecycleState'] != 'ACTIVE': sleep(timeout) timeout = timeout*2 - project = client.projects().get(projectId=project_id).execute() + project = client.projects().get( + projectId=project['projectId'] + ).execute() return project From a493d95d620073b7a34bc7d124b4d9d406d92c13 Mon Sep 17 00:00:00 2001 From: elibixby Date: Mon, 20 Jul 2015 11:24:05 -0700 Subject: [PATCH 6/9] Fixed module name --- resourcemanager/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resourcemanager/requirements.txt b/resourcemanager/requirements.txt index 9a75b98bc439..0a96f734aa16 100644 --- a/resourcemanager/requirements.txt +++ b/resourcemanager/requirements.txt @@ -1,2 +1,2 @@ -random_words +RandomWords googleapiclient From 4e93c86a142cb06fcdf7d4a125a4d97149c27c38 Mon Sep 17 00:00:00 2001 From: elibixby Date: Mon, 20 Jul 2015 11:41:18 -0700 Subject: [PATCH 7/9] Fixed module import --- resourcemanager/create_project.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resourcemanager/create_project.py b/resourcemanager/create_project.py index 153dd4cd8e72..91a44d847d2a 100644 --- a/resourcemanager/create_project.py +++ b/resourcemanager/create_project.py @@ -3,7 +3,9 @@ import random from googleapiclient.errors import HttpError -from random_words import RandomWords + +import RandomWords + from utils import build_client, wait_for_active From 7fd887b7053260927db613aed9aecfc0a9f467fc Mon Sep 17 00:00:00 2001 From: elibixby Date: Mon, 20 Jul 2015 11:58:43 -0700 Subject: [PATCH 8/9] Moved to Global requirements.txt --- requirements.txt | 6 ++++++ resourcemanager/create_project.py | 4 +--- resourcemanager/requirements.txt | 2 -- tox.ini | 13 +++++-------- 4 files changed, 12 insertions(+), 13 deletions(-) create mode 100644 requirements.txt delete mode 100644 resourcemanager/requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000000..dab79330e2af --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +google-api-python-client +flask +mock +nose +nosegae +RandomWords diff --git a/resourcemanager/create_project.py b/resourcemanager/create_project.py index 91a44d847d2a..153dd4cd8e72 100644 --- a/resourcemanager/create_project.py +++ b/resourcemanager/create_project.py @@ -3,9 +3,7 @@ import random from googleapiclient.errors import HttpError - -import RandomWords - +from random_words import RandomWords from utils import build_client, wait_for_active diff --git a/resourcemanager/requirements.txt b/resourcemanager/requirements.txt deleted file mode 100644 index 0a96f734aa16..000000000000 --- a/resourcemanager/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -RandomWords -googleapiclient diff --git a/tox.ini b/tox.ini index deab332300aa..0a7bcc4ed2a7 100644 --- a/tox.ini +++ b/tox.ini @@ -6,25 +6,22 @@ envlist = py27, pep8, cover passenv = PYTHONPATH GOOGLE_* GCLOUD_* TEST_* TRAVIS* basepython = python2.7 + [testenv:py27] deps = - google-api-python-client - flask - mock - nose - nosegae -commands = + -rrequirements.txt +commands = nosetests --with-gae {posargs} [testenv:pep8] -deps = +deps = flake8 flake8-import-order commands = flake8 --max-complexity=10 --import-order-style=google {posargs} [testenv:cover] -deps = +deps = {[testenv:py27]deps} coverage coveralls From 8db9c6426a25bd6f33d96d483eb1e214a96860ac Mon Sep 17 00:00:00 2001 From: elibixby Date: Mon, 20 Jul 2015 13:47:59 -0700 Subject: [PATCH 9/9] Fixed parsing of HttpError str --- requirements.txt | 1 + resourcemanager/create_project.py | 13 ++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/requirements.txt b/requirements.txt index dab79330e2af..3eddbdfbe9f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ mock nose nosegae RandomWords +parse diff --git a/resourcemanager/create_project.py b/resourcemanager/create_project.py index 153dd4cd8e72..287cdf55fdd6 100644 --- a/resourcemanager/create_project.py +++ b/resourcemanager/create_project.py @@ -1,12 +1,13 @@ import argparse import json +import logging import random from googleapiclient.errors import HttpError +from parse import * from random_words import RandomWords from utils import build_client, wait_for_active - rw = RandomWords() @@ -20,6 +21,7 @@ def create_project(client, name, id, **labels): ).execute() + def run(name, id=None, **labels): client = build_client() project = None @@ -32,11 +34,12 @@ def run(name, id=None, **labels): try: project = create_project(client, name, id, **labels) except HttpError as e: - code, uri, reason = str(e).parse( - '') - if not reason == "Requested entity already exists": + code, uri, reason = parse('', + str(e)) + if reason == "Requested entity already exists": + logging.info("Project ID {} is taken".format(id)) + else: raise e - else: project = create_project(client, name, id, **labels) return wait_for_active(client, project)