From 37274de7a22db9355dff4d94c2f61d95807445ac Mon Sep 17 00:00:00 2001 From: Matt Clay Date: Thu, 15 Nov 2018 10:32:42 -0800 Subject: [PATCH] [stable-2.5] Backport test infra bug fixes. (#48700) * Download hello package from S3 for apt test. (cherry picked from commit 83fd82ca7e7635f6c16801c88a5fe81a377f86ca) * Fix passing of env vars to Shippable. (cherry picked from commit 9979a32e5c6d55345a8f87115ed86a94d86d8398) * Use correct interpreter for ansible-test injector. (cherry picked from commit eec21a3d12dcab755e019f7875cf357f64bd3104) * Correct ansible-test injector python behavior. Inject a symlink to the correct python into the copied injector directory instead of altering the shebang of the injector. This has the side-effect of also intercepting `python` for integration tests which simplifies cases where it needs to be directly invoked without collecting code coverage. (cherry picked from commit d6bf45cd9d62e59923df13ffb71900f7dde92497) * Fix ansible-test merge change detection. (cherry picked from commit aa7fe919d319fe45e068e94f6e2de7162d8921fe) * Fix ansible-test interpreter tracking. Track the interpreter for each copy of the injector by the interpreter path instead of the interpreter version. This avoids the possibility of mixing different interpreters with the same version. (cherry picked from commit fa53b4805bcf3573dee7d94ed6f6918d7183c92e) * Use `state: latest` for `dpkg_selections` test. We don't need to test with `upgrade: dist`, since we're not trying to test the `apt` module. We just need to make sure the hold set by the `dpkg_selections` module is working. This change will avoid updating all the packages on the system, which is slow, unnecessary, and can cause the installed python to be changed. (cherry picked from commit 136a2cca2fcea71d86ab317c476e9bb4b2dd34ac) --- .../integration/targets/apt/tasks/upgrade.yml | 2 +- .../tasks/dpkg_selections.yaml | 7 ++-- test/runner/lib/changes.py | 15 +++------ test/runner/lib/executor.py | 4 +-- test/runner/lib/git.py | 18 +++++++++++ test/runner/lib/util.py | 32 +++++++++++++------ test/utils/shippable/tools/run.py | 6 ++-- 7 files changed, 56 insertions(+), 28 deletions(-) diff --git a/test/integration/targets/apt/tasks/upgrade.yml b/test/integration/targets/apt/tasks/upgrade.yml index 1fec0cc79d7..1762ff0bd07 100644 --- a/test/integration/targets/apt/tasks/upgrade.yml +++ b/test/integration/targets/apt/tasks/upgrade.yml @@ -1,7 +1,7 @@ --- #### Tests for upgrade/download functions in modules/packaging/os/apt.py #### - name: download and install old version of hello - apt: "deb=https://launchpad.net/ubuntu/+archive/primary/+files/hello_{{ hello_old_version }}_amd64.deb" + apt: "deb=https://s3.amazonaws.com/ansible-ci-files/test/integration/targets/apt/hello_{{ hello_old_version }}_amd64.deb" - name: check hello version shell: dpkg -s hello | grep Version | awk '{print $2}' diff --git a/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml b/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml index c263743f0de..f38e1f84aa9 100644 --- a/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml +++ b/test/integration/targets/dpkg_selections/tasks/dpkg_selections.yaml @@ -19,7 +19,9 @@ - name: attempt to upgrade hello apt: - upgrade: dist + name: hello + state: latest + ignore_errors: yes - name: check hello version shell: dpkg -s hello | grep Version | awk '{print $2}' @@ -37,7 +39,8 @@ - name: upgrade hello apt: - upgrade: dist + name: hello + state: latest - name: check hello version shell: dpkg -s hello | grep Version | awk '{print $2}' diff --git a/test/runner/lib/changes.py b/test/runner/lib/changes.py index e4e223b3e72..ca31219b9c2 100644 --- a/test/runner/lib/changes.py +++ b/test/runner/lib/changes.py @@ -106,17 +106,10 @@ class ShippableChanges(object): display.warning('Unable to find project. Cannot determine changes. All tests will be executed.') return None - merge_runs = sorted(merge_runs, key=lambda r: r['createdAt']) - known_commits = set() - last_successful_commit = None - - for merge_run in merge_runs: - commit_sha = merge_run['commitSha'] - if commit_sha not in known_commits: - known_commits.add(commit_sha) - if merge_run['statusCode'] == 30: - if git.is_valid_ref(commit_sha): - last_successful_commit = commit_sha + successful_commits = set(run['commitSha'] for run in merge_runs if run['statusCode'] == 30) + commit_history = git.get_rev_list(max_count=100) + ordered_successful_commits = [commit for commit in commit_history if commit in successful_commits] + last_successful_commit = ordered_successful_commits[0] if ordered_successful_commits else None if last_successful_commit is None: display.warning('No successful commit found. All tests will be executed.') diff --git a/test/runner/lib/executor.py b/test/runner/lib/executor.py index 36f500ef2e4..7045d478c90 100644 --- a/test/runner/lib/executor.py +++ b/test/runner/lib/executor.py @@ -346,7 +346,7 @@ def command_network_integration(args): instances = [] # type: list [lib.thread.WrappedThread] if args.platform: - get_coverage_path(args) # initialize before starting threads + get_coverage_path(args, args.python_executable) # initialize before starting threads configs = dict((config['platform_version'], config) for config in args.metadata.instance_config) @@ -514,7 +514,7 @@ def command_windows_integration(args): httptester_id = None if args.windows: - get_coverage_path(args) # initialize before starting threads + get_coverage_path(args, args.python_executable) # initialize before starting threads configs = dict((config['platform_version'], config) for config in args.metadata.instance_config) diff --git a/test/runner/lib/git.py b/test/runner/lib/git.py index 387a87f4d2d..a06e4c8162b 100644 --- a/test/runner/lib/git.py +++ b/test/runner/lib/git.py @@ -59,6 +59,24 @@ class Git(object): cmd = ['symbolic-ref', '--short', 'HEAD'] return self.run_git(cmd).strip() + def get_rev_list(self, commits=None, max_count=None): + """ + :type commits: list[str] | None + :type max_count: int | None + :rtype: list[str] + """ + cmd = ['rev-list'] + + if commits: + cmd += commits + else: + cmd += ['HEAD'] + + if max_count: + cmd += ['--max-count', '%s' % max_count] + + return self.run_git_split(cmd) + def get_branch_fork_point(self, branch): """ :type branch: str diff --git a/test/runner/lib/util.py b/test/runner/lib/util.py index fbc648f6659..f04bcc5212d 100644 --- a/test/runner/lib/util.py +++ b/test/runner/lib/util.py @@ -39,8 +39,7 @@ except ImportError: from configparser import ConfigParser DOCKER_COMPLETION = {} - -coverage_path = '' # pylint: disable=locally-disabled, invalid-name +COVERAGE_PATHS = {} # type: dict[str, str] def get_docker_completion(): @@ -196,10 +195,10 @@ def intercept_command(args, cmd, target_name, capture=False, env=None, data=None env = common_environment() cmd = list(cmd) - inject_path = get_coverage_path(args) - config_path = os.path.join(inject_path, 'injector.json') version = python_version or args.python_version interpreter = find_python(version, path) + inject_path = get_coverage_path(args, interpreter) + config_path = os.path.join(inject_path, 'injector.json') coverage_file = os.path.abspath(os.path.join(inject_path, '..', 'output', '%s=%s=%s=%s=coverage' % ( args.command, target_name, args.coverage_label or 'local-%s' % version, 'python-%s' % version))) @@ -219,12 +218,13 @@ def intercept_command(args, cmd, target_name, capture=False, env=None, data=None return run_command(args, cmd, capture=capture, env=env, data=data, cwd=cwd) -def get_coverage_path(args): +def get_coverage_path(args, interpreter): """ :type args: TestConfig + :type interpreter: str :rtype: str """ - global coverage_path # pylint: disable=locally-disabled, global-statement, invalid-name + coverage_path = COVERAGE_PATHS.get(interpreter) if coverage_path: return os.path.join(coverage_path, 'coverage') @@ -251,13 +251,27 @@ def get_coverage_path(args): os.mkdir(os.path.join(coverage_path, directory)) os.chmod(os.path.join(coverage_path, directory), stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) - atexit.register(cleanup_coverage_dir) + os.symlink(interpreter, os.path.join(coverage_path, 'coverage', 'python')) + + if not COVERAGE_PATHS: + atexit.register(cleanup_coverage_dirs) + + COVERAGE_PATHS[interpreter] = coverage_path return os.path.join(coverage_path, 'coverage') -def cleanup_coverage_dir(): - """Copy over coverage data from temporary directory and purge temporary directory.""" +def cleanup_coverage_dirs(): + """Clean up all coverage directories.""" + for path in COVERAGE_PATHS.values(): + display.info('Cleaning up coverage directory: %s' % path, verbosity=2) + cleanup_coverage_dir(path) + + +def cleanup_coverage_dir(coverage_path): + """Copy over coverage data from temporary directory and purge temporary directory. + :type coverage_path: str + """ output_dir = os.path.join(coverage_path, 'output') for filename in os.listdir(output_dir): diff --git a/test/utils/shippable/tools/run.py b/test/utils/shippable/tools/run.py index 7eb60bcda12..4a74076d535 100755 --- a/test/utils/shippable/tools/run.py +++ b/test/utils/shippable/tools/run.py @@ -99,7 +99,7 @@ def main(): # new build data = dict( - globalEnv=['%s=%s' % (kp[0], kp[1]) for kp in args.env or []] + globalEnv=dict((kp[0], kp[1]) for kp in args.env or []) ) if args.branch: @@ -108,10 +108,10 @@ def main(): data['runId'] = args.run url = 'https://api.shippable.com/projects/%s/newBuild' % project_id - response = requests.post(url, data, headers=headers) + response = requests.post(url, json=data, headers=headers) if response.status_code != 200: - raise Exception(response.content) + raise Exception("HTTP %s: %s\n%s" % (response.status_code, response.reason, response.content)) print(json.dumps(response.json(), indent=4, sort_keys=True))