# Copyright (C) 2020 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 abc import shlex import urllib.parse import cliapp class DownstreamHost(abc.ABC): @staticmethod def add_app_settings(app_settings): '''Add any application settings that are specific to this Downstream Host type. ''' pass @staticmethod def check_app_settings(app_settings): '''Validate any fields in the application settings that are specific to this Downstream Host type. ''' pass @abc.abstractmethod def __init__(self, app_settings): '''Construct a Downstream Host connector from the application settings. ''' pass @abc.abstractmethod def prepare_repo(self, repo_path, metadata): '''Prepare a repository on the Host. If the repository does not exist, this method must create it. It should also set any given metadata on the repository, whether or not it already exists. repo_path is the path that the repository should appear at within the Host. metadata is a dictionary with the following (optional) keys defined: - head: Name of the default branch (a.k.a. HEAD) - description: Short string describing the repository ''' pass class UpstreamHost(abc.ABC): @staticmethod def check_host_type_params(validator, section): '''Validate any type-specific fields in a CONFGIT host section. validator is an instance of LorryControllerConfValidator that may be used to check the types of configuration fields. section is the dictionary of fields for the section. Returns None if the configuration is valid; raises an exception on error. ''' pass @staticmethod def get_host_type_params(section): '''Convert any type-specific fields in a CONFGIT host section into a dictionary that will be stored in STATEDB. section is the dictionary of fields for the section. Returns a dictionary, which may be empty. This will be stored in STATEDB as the type_params of the host. ''' return {} @abc.abstractmethod def __init__(self, host_info): '''Construct an Upstream Host connector from the given host_info. The host_info comes directly from STATEDB. ''' pass @abc.abstractmethod def list_repos(self): '''List all visible repositories on the Host. Returns a list of path strings. ''' pass @abc.abstractmethod def get_repo_url(self, repo_path): '''Get URL for a repository. repo_path is the path to the repository within the Host. Returns a URL string suitable for passing to git clone. ''' pass @abc.abstractmethod def get_repo_metadata(self, repo_path): '''Get metadata for a repository. repo_path is the path to the repository within the Host. Returns a dictionary of metadata suitable for passing to DownstreamHost.prepare_repo. ''' pass class SshCommand: def __init__(self, urlstring, **options): try: url = urllib.parse.urlsplit(urlstring, allow_fragments=False) url.port except ValueError: raise cliapp.AppException('Invalid URL: %s' % urlstring) if url.scheme != 'ssh': raise cliapp.AppException('Not an SSH URL: %s' % urlstring) if url.path not in ['', '/']: raise cliapp.AppException('Unexpected path part in SSH URL') if url.query != '': raise cliapp.AppException('Unexpected query part in SSH URL') if url.password is not None: raise cliapp.AppException('Unexpected password in SSH URL') self._ssh_args = ['ssh', '-oBatchMode=yes'] for key, value in options.items(): self._ssh_args.append('-o%s=%s' % (key, value)) if url.username is not None: self._ssh_args.append('-oUser=%s' % url.username) if url.port is not None: self._ssh_args.append('-p%i' % url.port) self._ssh_args.append(url.hostname) def run(self, args): quoted_args = [shlex.quote(arg) for arg in args] stdout = cliapp.runcmd(self._ssh_args + quoted_args) if isinstance(stdout, bytes): stdout = stdout.decode('utf-8', errors='replace') return stdout