0a42768997
The lack of 'rmfile' steps is OK because all of the temporary paths are created within swarming's sandbox; swarming will clean everything up after the recipe has quit. R=borenet@google.com Change-Id: Ibb8dc612ec8eb98fe966c9caa90ba59db48dbfba Reviewed-on: https://skia-review.googlesource.com/c/skia/+/239797 Auto-Submit: Robbie Iannucci <iannucci@google.com> Commit-Queue: Eric Boren <borenet@google.com> Reviewed-by: Eric Boren <borenet@google.com>
131 lines
5.5 KiB
Python
131 lines
5.5 KiB
Python
# Copyright 2019 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
|
|
from recipe_engine import recipe_api
|
|
|
|
import default
|
|
import ntpath
|
|
import ssh
|
|
import subprocess # TODO(borenet): No! Remove this.
|
|
|
|
|
|
"""Win SSH flavor, used for running code on Windows via an SSH connection.
|
|
|
|
Copied from chromebook.py and modified for Windows.
|
|
"""
|
|
|
|
|
|
class WinSSHFlavor(ssh.SSHFlavor):
|
|
|
|
def __init__(self, m):
|
|
super(WinSSHFlavor, self).__init__(m)
|
|
self.remote_homedir = 'C:\\Users\\chrome-bot\\botdata\\'
|
|
self.device_dirs = default.DeviceDirs(
|
|
bin_dir = self.device_path_join(self.remote_homedir, 'bin'),
|
|
dm_dir = self.device_path_join(self.remote_homedir, 'dm_out'),
|
|
perf_data_dir = self.device_path_join(self.remote_homedir, 'perf'),
|
|
resource_dir = self.device_path_join(self.remote_homedir, 'resources'),
|
|
images_dir = self.device_path_join(self.remote_homedir, 'images'),
|
|
lotties_dir = self.device_path_join(self.remote_homedir, 'lotties'),
|
|
skp_dir = self.device_path_join(self.remote_homedir, 'skps'),
|
|
svg_dir = self.device_path_join(self.remote_homedir, 'svgs'),
|
|
mskp_dir = self.device_path_join(self.remote_homedir, 'mskp'),
|
|
tmp_dir = self.remote_homedir)
|
|
self._empty_dir = self.device_path_join(self.remote_homedir, 'empty')
|
|
|
|
|
|
def _cmd(self, title, cmd, infra_step=True, fail_errorlevel=1, **kwargs):
|
|
return self.m.run(self.m.python, title,
|
|
script=self.module.resource('win_ssh_cmd.py'),
|
|
args=[self.user_ip, cmd, fail_errorlevel],
|
|
infra_step=infra_step, **kwargs)
|
|
|
|
def ensure_device_dir(self, path):
|
|
self._cmd('mkdir %s' % path, 'if not exist "%s" md "%s"' % (path, path))
|
|
|
|
def _rmdir(self, path):
|
|
self._cmd('rmdir %s' % path, 'if exist "%s" rd "%s"' % (path, path))
|
|
|
|
def device_path_join(self, *args):
|
|
return ntpath.join(*args)
|
|
|
|
def install(self):
|
|
super(WinSSHFlavor, self).install()
|
|
|
|
# Ensure that our empty dir is actually empty.
|
|
self._rmdir(self._empty_dir)
|
|
self.ensure_device_dir(self._empty_dir)
|
|
|
|
self.create_clean_device_dir(self.device_dirs.bin_dir)
|
|
|
|
def create_clean_device_dir(self, path):
|
|
# Based on https://stackoverflow.com/a/98069 and
|
|
# https://superuser.com/a/346112.
|
|
self._cmd('clean %s' % path,
|
|
'robocopy /mir "%s" "%s"' % (self._empty_dir, path),
|
|
fail_errorlevel=8)
|
|
|
|
def read_file_on_device(self, path, **kwargs):
|
|
with self.m.step.nest('read %s' % path):
|
|
tmp = self.m.path.mkdtemp('read_file_on_device')
|
|
host_path = tmp.join(ntpath.basename(path))
|
|
device_path = self.scp_device_path(path)
|
|
ok = self._run('scp %s %s' % (device_path, host_path),
|
|
cmd=['scp', device_path, host_path],
|
|
infra_step=True, **kwargs)
|
|
# TODO(dogben): Should readfile respect fail_build_on_failure and
|
|
# abort_on_failure?
|
|
if ok:
|
|
return self.m.run.readfile(host_path)
|
|
|
|
def remove_file_on_device(self, path):
|
|
self._cmd('rm %s' % path, 'if exist "%s" del "%s"' % (path, path))
|
|
|
|
def _copy_dir(self, src, dest):
|
|
self._run('scp -r %s %s' % (src, dest),
|
|
cmd=['scp', '-r', src, dest], infra_step=True)
|
|
|
|
def copy_directory_contents_to_device(self, host_path, device_path):
|
|
# Callers expect that the destination directory is replaced, which is not
|
|
# how scp works when the destination is a directory. Instead scp to tmp_dir
|
|
# and then robocopy to the correct destination.
|
|
# Other flavors use a glob and subprocess with shell=True to copy the
|
|
# contents of host_path; however, there are a lot of ways POSIX shell
|
|
# interpretation could mess up Windows path names.
|
|
with self.m.step.nest('copy %s to device' % host_path):
|
|
tmp_pardir = self.device_path_join(
|
|
self.device_dirs.tmp_dir,
|
|
'tmp_copy_directory_contents_to_device')
|
|
self.create_clean_device_dir(tmp_pardir)
|
|
tmpdir = self.device_path_join(tmp_pardir,
|
|
self.m.path.basename(host_path))
|
|
self._copy_dir(host_path, self.scp_device_path(tmpdir))
|
|
self._cmd('copy %s to %s' % (tmpdir, device_path),
|
|
'robocopy /mir "%s" "%s"' % (tmpdir, device_path),
|
|
fail_errorlevel=8)
|
|
|
|
def copy_directory_contents_to_host(self, device_path, host_path):
|
|
# Note that the glob in src is interpreted by the remote shell.
|
|
src = self.scp_device_path(self.device_path_join(device_path, '*'))
|
|
self._copy_dir(src, host_path)
|
|
|
|
def step(self, name, cmd, infra_step=False, **kwargs):
|
|
# There may be DLLs in the same dir as the executable that must be loaded
|
|
# (yes, Windows allows overriding system DLLs with files in the local
|
|
# directory). For simplicity, just copy the entire dir to the device.
|
|
self.copy_directory_contents_to_device(self.host_dirs.bin_dir,
|
|
self.device_dirs.bin_dir)
|
|
device_bin = self.device_path_join(self.device_dirs.bin_dir, cmd[0])
|
|
|
|
# Copy PowerShell script to device.
|
|
ps = 'win_run_and_check_log.ps1'
|
|
device_ps = self.device_path_join(self.device_dirs.bin_dir, ps)
|
|
self.copy_file_to_device(self.module.resource(ps), device_ps)
|
|
|
|
cmd = ['powershell', '-ExecutionPolicy', 'Unrestricted', '-File', device_ps,
|
|
device_bin] + cmd[1:]
|
|
self._cmd(name, subprocess.list2cmdline(map(str, cmd)),
|
|
infra_step=infra_step, **kwargs)
|