From e6ecac7ab041b173b28c7aca58a2544c1a72ba3a Mon Sep 17 00:00:00 2001 From: Ed Cranford Date: Wed, 4 Mar 2015 17:40:42 -0600 Subject: [PATCH] Adding git key and trigger tools to app-create Changed BaseException to CommandException for python34 test compatibility Change-Id: Id5db4950a06a0875dc6485758318f0bc9090f6f2 --- solumclient/common/github.py | 123 +++++++++++++++++++++++++++++++++++ solumclient/solum.py | 17 +++++ 2 files changed, 140 insertions(+) create mode 100644 solumclient/common/github.py diff --git a/solumclient/common/github.py b/solumclient/common/github.py new file mode 100644 index 0000000..7c59903 --- /dev/null +++ b/solumclient/common/github.py @@ -0,0 +1,123 @@ +# Copyright (c) 2015 Rackspace +# +# 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. + +# Tools for interacting with Github. + +import base64 +import getpass +import json +import random +import re +import string + +import httplib2 + + +class GitHubAuth(object): + username = None + password = None + full_repo_name = None + git_url = None + _token = None + + _auth_url = 'https://api.github.com/authorizations' + + def __init__(self, git_url): + self.git_url = git_url + + user_org_name, repo = '', '' + + github_regex = r'github\.com[:/](.+?)/(.+?)($|/$|\.git$|\.git/$)' + repo_pat = re.compile(github_regex) + match = repo_pat.search(self.git_url) + if match: + user_org_name, repo = match.group(1), match.group(2) + else: + raise ValueError("Failed to parse %s." % git_url) + self.full_repo_name = '/'.join([user_org_name, repo]) + prompt = ("Username for repo '%s' [%s]:" % + (self.full_repo_name, user_org_name)) + username = raw_input(prompt) or user_org_name + password = getpass.getpass("Password: ") + self.username = username + self.password = password + + @property + def _auth_header(self): + authstring = '%s:%s' % (self.username, self.password) + auth = base64.encodestring(authstring) + return { + 'Authorization': 'Basic %s' % auth, + 'Content-Type': 'application/json', + } + + @property + def token(self): + if self._token is None: + self._get_repo_token() + return self._token + + def _get_repo_token(self): + note = ''.join(random.sample(string.lowercase, 5)) + auth_info = { + 'scopes': 'repo', + 'note': 'Solum-status-%s' % note, + } + resp, content = httplib2.Http().request( + self._auth_url, + 'POST', + headers=self._auth_header, + body=json.dumps(auth_info)) + if resp.get('status') in ['200', '201']: + response_body = json.loads(content) + self._token = response_body.get('token') + + def create_webhook(self, trigger_uri): + hook_url = ('https://api.github.com/repos/%s/hooks' % + self.full_repo_name) + hook_info = { + 'name': 'web', + 'events': ['pull_request', 'commit_comment'], + 'config': { + 'content_type': 'json', + 'url': trigger_uri, + } + } + resp, content = httplib2.Http().request( + hook_url, + 'POST', + headers=self._auth_header, + body=json.dumps(hook_info)) + if resp.get('status') not in ['200', '201']: + pass + + def add_ssh_key(self, public_key=None, is_private=False): + if not public_key: + return + api_url = ('https://api.github.com/repos/%s/keys' % + self.full_repo_name) + if is_private: + api_url = 'https://api.github.com/user/keys' + key_info = { + 'title': 'devops@Solum', + 'key': public_key, + } + resp, content = httplib2.Http().request( + api_url, + 'POST', + headers=self._auth_header, + body=json.dumps(key_info)) + if resp.get('status') not in ['200', '201']: + pass diff --git a/solumclient/solum.py b/solumclient/solum.py index f15e9b2..901893c 100644 --- a/solumclient/solum.py +++ b/solumclient/solum.py @@ -47,6 +47,7 @@ import sys from solumclient.common import cli_utils from solumclient.common import exc +from solumclient.common import github from solumclient.common import yamlutils from solumclient.openstack.common.apiclient import exceptions from solumclient.openstack.common import cliutils @@ -515,6 +516,7 @@ Available commands: solum app create [--plan-file ] [--git-url ] [--langpack ] [--run-cmd ] [--name ] [--desc ] + [--setup-triggers] Register a new application with Solum. solum app deploy @@ -618,6 +620,10 @@ Available commands: help="A yaml file containing custom" " parameters to be used in the" " application") + self.parser.add_argument('--setup-triggers', + action='store_true', + dest='setup_triggers', + help="Set up app triggers on git repo") args = self.parser.parse_args() @@ -671,6 +677,7 @@ Available commands: git_url = None if args.git_url is not None: + git_url = args.git_url plan_definition['artifacts'][0]['content']['href'] = args.git_url if plan_definition['artifacts'][0]['content'].get('href') is None: git_url = raw_input("Please specify a git repository URL for " @@ -727,6 +734,16 @@ Available commands: cliutils.print_dict(data, wrap=72) self._show_public_keys(artifacts) + if artifacts and args.setup_triggers: + gha = github.GitHubAuth(git_url) + content = artifacts[0].content + public_key = content.get('public_key', '') + private_repo = content.get('private', False) + gha.add_ssh_key(public_key=public_key, is_private=private_repo) + trigger_url = vars(plan).get('trigger_url', '') + if trigger_url: + gha.create_webhook(trigger_url) + def deploy(self): """Deploy an application, building any applicable artifacts first.""" # This is just "assembly create" with a little bit of introspection.