diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000000..3eddbdfbe9f0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +google-api-python-client +flask +mock +nose +nosegae +RandomWords +parse diff --git a/resourcemanager/__init__.py b/resourcemanager/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/resourcemanager/create_project.py b/resourcemanager/create_project.py new file mode 100644 index 000000000000..287cdf55fdd6 --- /dev/null +++ b/resourcemanager/create_project.py @@ -0,0 +1,67 @@ +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() + + +def create_project(client, name, id, **labels): + return client.projects().create( + body={ + 'projectId': id, + 'name': name, + 'labels': labels + } + ).execute() + + + +def run(name, id=None, **labels): + client = build_client() + project = None + if id is None: + while project is None: + 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 = parse('<HttpError {} when requesting {} returned "{}">', + str(e)) + if reason == "Requested entity already exists": + logging.info("Project ID {} is taken".format(id)) + else: + raise e + project = create_project(client, name, id, **labels) + + return wait_for_active(client, 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() + 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 new file mode 100644 index 000000000000..4a399b3244c6 --- /dev/null +++ b/resourcemanager/delete_project.py @@ -0,0 +1,25 @@ +import argparse + +from utils import build_client + + +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: + 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/tests/test_resource_manager.py b/resourcemanager/tests/test_resource_manager.py new file mode 100644 index 000000000000..6366da5dd051 --- /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 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']) diff --git a/resourcemanager/utils.py b/resourcemanager/utils.py new file mode 100644 index 000000000000..5e2a2f30d99e --- /dev/null +++ b/resourcemanager/utils.py @@ -0,0 +1,24 @@ +from time import sleep + +from googleapiclient.discovery import build +import httplib2 +from oauth2client.client import GoogleCredentials + + +def build_client(): + return build('cloudresourcemanager', + 'v1beta1', + credentials=GoogleCredentials.get_application_default(), + # Use long timeout for create requests + http=httplib2.Http(timeout=90)) + + +def wait_for_active(client, project): + timeout = 1 + while project['lifecycleState'] != 'ACTIVE': + sleep(timeout) + timeout = timeout*2 + project = client.projects().get( + projectId=project['projectId'] + ).execute() + return project 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