From 36cf89491a48e20002898a07b348e6f8c6064f54 Mon Sep 17 00:00:00 2001 From: Sam Thursfield Date: Wed, 10 Dec 2014 18:05:24 +0000 Subject: Add initial support for mirroring to a Gerrit instance --- lorry-controller-webapp | 5 +++++ lorrycontroller/__init__.py | 1 + lorrycontroller/gerrit.py | 53 ++++++++++++++++++++++++++++++++++++++++++++ lorrycontroller/givemejob.py | 37 +++++++++++++++++++++++++------ 4 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 lorrycontroller/gerrit.py diff --git a/lorry-controller-webapp b/lorry-controller-webapp index faabb2d..2ed601c 100755 --- a/lorry-controller-webapp +++ b/lorry-controller-webapp @@ -129,6 +129,11 @@ class WEBAPP(cliapp.Application): 'ghosts and be removed from STATEDB (in seconds)', default=10*ONE_MINUTE) + self.settings.choice( + ['git-server-type'], + ['gitano', 'gerrit'], + 'what API the local Git server speaks') + def find_routes(self): '''Return all classes that are API routes. diff --git a/lorrycontroller/__init__.py b/lorrycontroller/__init__.py index a65ff02..9d46441 100644 --- a/lorrycontroller/__init__.py +++ b/lorrycontroller/__init__.py @@ -44,6 +44,7 @@ from gitano import ( new_gitano_command) from static import StaticFile from proxy import setup_proxy +from gerrit import Gerrit __all__ = locals() diff --git a/lorrycontroller/gerrit.py b/lorrycontroller/gerrit.py new file mode 100644 index 0000000..042a621 --- /dev/null +++ b/lorrycontroller/gerrit.py @@ -0,0 +1,53 @@ +# Copyright (C) 2015 Codethink Limited +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; version 2 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + + +import cliapp + + +class Gerrit(object): + + '''Run commands on a Gerrit instance. + + This uses the SSH API to Gerrit. The REST API is actually much nicer to + use, but it requires that the account doing stuff has set an HTTP password. + Use of the SSH API requires only the SSH key that we also use for push + access. + + ''' + + def __init__(self, host, user, port=29418): + self._ssh_command_args = [ + 'ssh', '-oStrictHostKeyChecking=no', '-oBatchMode=yes', '-p%i' % port, + '%s@%s' % (user, host)] + + def _ssh_command(self, command): + return cliapp.runcmd(self._ssh_command_args + command) + + def has_project(self, name): + # There's no 'does this project exist' command in Gerrit 2.9.4; 'list + # all projects with this prefix' is as close we can get. + + output = self._ssh_command([ + 'gerrit', 'ls-projects', '--type=ALL', '--prefix=%s' % name]) + projects = output.strip().split('\n') + + if name in projects: + return True + else: + return False + + def create_project(self, name): + self._ssh_command(['gerrit', 'create-project', name]) diff --git a/lorrycontroller/givemejob.py b/lorrycontroller/givemejob.py index 755def0..0d9e6ab 100644 --- a/lorrycontroller/givemejob.py +++ b/lorrycontroller/givemejob.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Codethink Limited +# Copyright (C) 2014-2015 Codethink Limited # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,13 +14,9 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -import collections import logging -import re -import time import bottle -import cliapp import lorrycontroller @@ -40,8 +36,7 @@ class GiveMeJob(lorrycontroller.LorryControllerRoute): now = statedb.get_current_time() for lorry_info in lorry_infos: if self.ready_to_run(lorry_info, now): - self.create_repository_in_local_trove( - statedb, lorry_info) + self.create_repository(statedb, lorry_info) if lorry_info['from_trovehost']: self.copy_repository_metadata(statedb, lorry_info) self.give_job_to_minion(statedb, lorry_info, now) @@ -67,6 +62,13 @@ class GiveMeJob(lorrycontroller.LorryControllerRoute): due = lorry_info['last_run'] + lorry_info['interval'] return (lorry_info['running_job'] is None and due <= now) + def create_repository(self, statedb, lorry_info): + api = self.app_settings['git-server-type'] + if api == 'gitano': + self.create_repository_in_local_trove(statedb, lorry_info) + elif api == 'gerrit': + self.create_gerrit_project(statedb, lorry_info) + def create_repository_in_local_trove(self, statedb, lorry_info): # Create repository on local Trove. If it fails, assume # it failed because the repository already existed, and @@ -82,12 +84,33 @@ class GiveMeJob(lorrycontroller.LorryControllerRoute): else: logging.info('Created %s on local repo', lorry_info['path']) + def create_gerrit_project(self, statedb, lorry_info): + '''Create a project in the local Gerrit server. + + The 'lorry' user must have createProject capability in the Gerrit. + + ''' + gerrit = lorrycontroller.Gerrit( + host='localhost', user='lorry') + project_name = lorry_info['path'] + + if gerrit.has_project(project_name): + logging.info('Project %s exists in local Gerrit already.', + project_name) + else: + gerrit.create_project(project_name) + logging.info('Created %s project in local Gerrit.', project_name) + def copy_repository_metadata(self, statedb, lorry_info): '''Copy project.head and project.description to the local Trove.''' assert lorry_info['from_trovehost'] assert lorry_info['from_path'] + if self.app_settings['git-server-type'] != 'gitano': + # FIXME: would be good to have this info in Gerrit too + return + remote = lorrycontroller.new_gitano_command(statedb, lorry_info['from_trovehost']) local = lorrycontroller.LocalTroveGitanoCommand() -- cgit v1.2.1