summaryrefslogtreecommitdiff
path: root/lorrycontroller/gitano.py
blob: c1347d53d625c76cddcf03c345840c17080ab8eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# 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]

        exit, stdout, stderr = cliapp.runcmd_unchecked(
            ['ssh', 'git@%s' % self.trovehost] + 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()