diff options
Diffstat (limited to 'lorrycontroller/gitano.py')
-rw-r--r-- | lorrycontroller/gitano.py | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/lorrycontroller/gitano.py b/lorrycontroller/gitano.py new file mode 100644 index 0000000..b2c9123 --- /dev/null +++ b/lorrycontroller/gitano.py @@ -0,0 +1,130 @@ +# Copyright (C) 2014 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 collections +import logging +import re +import urllib2 +import urlparse + +import cliapp + +import lorrycontroller + + +class GitanoCommandFailure(Exception): + + def __init__(self, trovehost, command, stderr): + Exception.__init__( + self, + 'Failed to run "%s" on Gitano on %s\n%s' % + (command, trovehost, stderr)) + + +class GitanoCommand(object): + + '''Run a Gitano command on a Trove.''' + + def __init__(self, trovehost, protocol, username, password): + self.trovehost = trovehost + self.protocol = protocol + self.username = username + self.password = password + + if protocol == 'ssh': + self._command = self._ssh_command + elif protocol in ('http', 'https'): + self._command = self._http_command + else: + raise GitanoCommandFailure( + self.trovehost, '__init__', 'unknown protocol %s' % protocol) + + def whoami(self): + return self._command(['whoami']) + + def create(self, repo_path): + self._command(['create', repo_path]) + + def get_gitano_config(self, repo_path): + stdout = self._command(['config', repo_path, 'show']) + + # "config REPO show" outputs a sequence of lines of the form "key: value". + # Extract those into a collections.defaultdict. + + result = collections.defaultdict(str) + for line in stdout.splitlines(): + m = re.match(r'^([^:])+:\s*(.*)$', line) + if m: + result[m.group(0)] = m.group(1).strip() + + return result + + def set_gitano_config(self, path, key, value): + self._command(['config', path, 'set', key, value]) + + def ls(self): + return self._command(['ls']) + + def _ssh_command(self, gitano_args): + quoted_args = [cliapp.shell_quote(x) for x in gitano_args] + + base_argv = [ + 'ssh', + '-oStrictHostKeyChecking=no', + '-oBatchMode=yes', + 'git@%s' % self.trovehost, + ] + + exit, stdout, stderr = cliapp.runcmd_unchecked( + base_argv + quoted_args) + + if exit != 0: + logging.error( + 'Failed to run "%s" for %s:\n%s', + self.trovehost, stdout + stderr) + raise GitanoCommandFailure( + self.trovehost, + ' '.join(gitano_args), + stdout + stderr) + + return stdout + + def _http_command(self, gitano_args): + quoted_args = urllib2.quote(' '.join(gitano_args)) + url = urlparse.urlunsplit(( + self.protocol, + self.trovehost, + '/gitano-command.cgi', + 'cmd=%s' % quoted_args, + '')) + logging.debug('url=%r', url) + + try: + request = urllib2.Request(url, None, {}) + logging.debug('request=%r', request.get_full_url()) + if self.username and self.password: + password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm() + password_mgr.add_password(None, url, self.username, self.password) + auth_handler = urllib2.HTTPBasicAuthHandler(password_mgr) + opener = urllib2.build_opener(auth_handler) + response = opener.open(url) + else: + response = urllib2.urlopen(request) + except urllib2.URLError as e: + raise GitanoCommandFailure( + self.trovehost, ' '.join(gitano_args), str(e)) + + return response.read() |