From 6ca4ea0c1f20ff23f231bbe14a80fd3eafc87087 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 13 Aug 2018 09:27:59 +1000 Subject: [PATCH] add support for opening shell on remote Windows host (#43919) * add support for opening shell on remote Windows host * added arg completion and fix sanity check * remove uneeded arg --- test/runner/lib/delegation.py | 45 ++++++++++++++++++++--------------- test/runner/lib/manage_ci.py | 34 ++++++++++++++++++++++++++ test/runner/test.py | 20 +++++++++++++++- 3 files changed, 79 insertions(+), 20 deletions(-) diff --git a/test/runner/lib/delegation.py b/test/runner/lib/delegation.py index d086e991e32..7a1769b67f2 100644 --- a/test/runner/lib/delegation.py +++ b/test/runner/lib/delegation.py @@ -33,6 +33,7 @@ from lib.core_ci import ( from lib.manage_ci import ( ManagePosixCI, + ManageWindowsCI, ) from lib.util import ( @@ -321,30 +322,35 @@ def delegate_remote(args, exclude, require, integration_targets): core_ci.wait() - options = { - '--remote': 1, - } + if platform == 'windows': + # Windows doesn't need the ansible-test fluff, just run the SSH command + manage = ManageWindowsCI(core_ci) + cmd = ['powershell.exe'] + else: + options = { + '--remote': 1, + } - cmd = generate_command(args, 'ansible/test/runner/test.py', options, exclude, require) + cmd = generate_command(args, 'ansible/test/runner/test.py', options, exclude, require) - if httptester_id: - cmd += ['--inject-httptester'] + if httptester_id: + cmd += ['--inject-httptester'] - if isinstance(args, TestConfig): - if args.coverage and not args.coverage_label: - cmd += ['--coverage-label', 'remote-%s-%s' % (platform, version)] + if isinstance(args, TestConfig): + if args.coverage and not args.coverage_label: + cmd += ['--coverage-label', 'remote-%s-%s' % (platform, version)] - if isinstance(args, IntegrationConfig): - if not args.allow_destructive: - cmd.append('--allow-destructive') + if isinstance(args, IntegrationConfig): + if not args.allow_destructive: + cmd.append('--allow-destructive') - # remote instances are only expected to have a single python version available - if isinstance(args, UnitsConfig) and not args.python: - cmd += ['--python', 'default'] + # remote instances are only expected to have a single python version available + if isinstance(args, UnitsConfig) and not args.python: + cmd += ['--python', 'default'] - manage = ManagePosixCI(core_ci) - manage.setup() + manage = ManagePosixCI(core_ci) + manage.setup() if isinstance(args, IntegrationConfig): cloud_platforms = get_cloud_providers(args) @@ -355,8 +361,9 @@ def delegate_remote(args, exclude, require, integration_targets): manage.ssh(cmd, ssh_options) success = True finally: - manage.ssh('rm -rf /tmp/results && cp -a ansible/test/results /tmp/results && chmod -R a+r /tmp/results') - manage.download('/tmp/results', 'test') + if platform != 'windows': + manage.ssh('rm -rf /tmp/results && cp -a ansible/test/results /tmp/results && chmod -R a+r /tmp/results') + manage.download('/tmp/results', 'test') finally: if args.remote_terminate == 'always' or (args.remote_terminate == 'success' and success): core_ci.stop() diff --git a/test/runner/lib/manage_ci.py b/test/runner/lib/manage_ci.py index 015bb8fdc7c..b01688af0d9 100644 --- a/test/runner/lib/manage_ci.py +++ b/test/runner/lib/manage_ci.py @@ -32,6 +32,22 @@ class ManageWindowsCI(object): :type core_ci: AnsibleCoreCI """ self.core_ci = core_ci + self.ssh_args = ['-i', self.core_ci.ssh_key.key] + + ssh_options = dict( + BatchMode='yes', + StrictHostKeyChecking='no', + UserKnownHostsFile='/dev/null', + ServerAliveInterval=15, + ServerAliveCountMax=4, + ) + + for ssh_option in sorted(ssh_options): + self.ssh_args += ['-o', '%s=%s' % (ssh_option, ssh_options[ssh_option])] + + def setup(self): + """Used in delegate_remote to setup the host, no action is required for Windows.""" + pass def wait(self): """Wait for instance to respond to ansible ping.""" @@ -59,6 +75,24 @@ class ManageWindowsCI(object): raise ApplicationError('Timeout waiting for %s/%s instance %s.' % (self.core_ci.platform, self.core_ci.version, self.core_ci.instance_id)) + def ssh(self, command, options=None): + """ + :type command: str | list[str] + :type options: list[str] | None + """ + if not options: + options = [] + + if isinstance(command, list): + command = ' '.join(pipes.quote(c) for c in command) + + run_command(self.core_ci.args, + ['ssh', '-tt', '-q'] + self.ssh_args + + options + + ['-p', '22', + '%s@%s' % (self.core_ci.connection.username, self.core_ci.connection.hostname)] + + [command]) + class ManageNetworkCI(object): """Manage access to a network instance provided by Ansible Core CI.""" diff --git a/test/runner/test.py b/test/runner/test.py index 95bb33f3a12..9fe0588c4e1 100755 --- a/test/runner/test.py +++ b/test/runner/test.py @@ -564,7 +564,7 @@ def add_environments(parser, tox_version=False, tox_only=False): environments.add_argument('--remote', metavar='PLATFORM', default=None, - help='run from a remote instance').completer = complete_remote + help='run from a remote instance').completer = complete_remote_shell if parser.prog.endswith(' shell') else complete_remote remote = parser.add_argument_group(title='remote arguments') @@ -685,6 +685,24 @@ def complete_remote(prefix, parsed_args, **_): return [i for i in images if i.startswith(prefix)] +def complete_remote_shell(prefix, parsed_args, **_): + """ + :type prefix: unicode + :type parsed_args: any + :rtype: list[str] + """ + del parsed_args + + with open('test/runner/completion/remote.txt', 'r') as completion_fd: + images = completion_fd.read().splitlines() + + # 2008 doesn't support SSH so we do not add to the list of valid images + with open('test/runner/completion/windows.txt', 'r') as completion_fd: + images.extend(["windows/%s" % i for i in completion_fd.read().splitlines() if i != '2008']) + + return [i for i in images if i.startswith(prefix)] + + def complete_docker(prefix, parsed_args, **_): """ :type prefix: unicode