#!/usr/bin/env python # -*- coding: utf-8 -*- """ This script is used to test Salt from a Jenkins server, specifically jenkins.saltstack.com. This script is intended to be shell-centric!! """ # Import python libs from __future__ import absolute_import, print_function import glob import optparse import os import random import re import shutil import subprocess import sys import time # Import Salt libs import salt.utils.files import salt.utils.json import salt.utils.stringutils import salt.utils.yaml try: from salt.utils.nb_popen import NonBlockingPopen except ImportError: # Salt not installed, or nb_popen was not yet shipped with it SALT_LIB = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) if SALT_LIB not in sys.path: sys.path.insert(0, SALT_LIB) try: # Let's try using the current checked out code from salt.utils.nb_popen import NonBlockingPopen except ImportError: # Still an ImportError??? Let's use some "brute-force" sys.path.insert(0, os.path.join(SALT_LIB, "salt", "utils")) from nb_popen import NonBlockingPopen # Import 3rd-party libs try: import requests HAS_REQUESTS = True except ImportError: HAS_REQUESTS = False SALT_GIT_URL = "https://github.com/saltstack/salt.git" def build_pillar_data(options): """ Build a YAML formatted string to properly pass pillar data """ pillar = { "test_transport": options.test_transport, "cloud_only": options.cloud_only, "with_coverage": options.test_without_coverage is False, } if options.test_git_commit is not None: pillar["test_git_commit"] = options.test_git_commit if options.test_git_url is not None: pillar["test_git_url"] = options.test_git_url if options.bootstrap_salt_url is not None: pillar["bootstrap_salt_url"] = options.bootstrap_salt_url if options.bootstrap_salt_commit is not None: pillar["bootstrap_salt_commit"] = options.bootstrap_salt_commit if options.package_source_dir: pillar["package_source_dir"] = options.package_source_dir if options.package_build_dir: pillar["package_build_dir"] = options.package_build_dir if options.package_artifact_dir: pillar["package_artifact_dir"] = options.package_artifact_dir if options.pillar: pillar.update(dict(options.pillar)) return salt.utils.yaml.safe_dump( pillar, default_flow_style=True, indent=0, width=sys.maxint ).rstrip() def build_minion_target(options, vm_name): target = vm_name for grain in options.grain_target: target += " and G@{0}".format(grain) if options.grain_target: return '"{0}"'.format(target) return target def generate_vm_name(options): """ Generate a random enough vm name """ if "BUILD_NUMBER" in os.environ: random_part = "BUILD{0:0>6}".format(os.environ.get("BUILD_NUMBER")) else: random_part = os.urandom(3).encode("hex") return "{0}-{1}-{2}".format(options.vm_prefix, options.platform, random_part) def delete_vm(options): """ Stop a VM """ cmd = "salt-cloud -d {0} -y".format(options.delete_vm) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() def echo_parseable_environment(options, parser): """ Echo NAME=VAL parseable output """ output = [] if options.platform: name = generate_vm_name(options) output.extend( [ "JENKINS_SALTCLOUD_VM_PLATFORM={0}".format(options.platform), "JENKINS_SALTCLOUD_VM_NAME={0}".format(name), ] ) if options.provider: output.append("JENKINS_SALTCLOUD_VM_PROVIDER={0}".format(options.provider)) if options.pull_request: # This is a Jenkins triggered Pull Request # We need some more data about the Pull Request available to the # environment if HAS_REQUESTS is False: parser.error("The python 'requests' library needs to be installed") headers = {} url = "https://api.github.com/repos/saltstack/salt/pulls/{0}".format( options.pull_request ) github_access_token_path = os.path.join( os.environ.get("JENKINS_HOME", os.path.expanduser("~")), ".github_token" ) if os.path.isfile(github_access_token_path): with salt.utils.files.fopen(github_access_token_path) as rfh: headers = {"Authorization": "token {0}".format(rfh.read().strip())} http_req = requests.get(url, headers=headers) if http_req.status_code != 200: parser.error( "Unable to get the pull request: {0[message]}".format(http_req.json()) ) pr_details = http_req.json() output.extend( [ "SALT_PR_GIT_URL={0}".format(pr_details["head"]["repo"]["clone_url"]), "SALT_PR_GIT_BRANCH={0}".format(pr_details["head"]["ref"]), "SALT_PR_GIT_COMMIT={0}".format(pr_details["head"]["sha"]), "SALT_PR_GIT_BASE_BRANCH={0}".format(pr_details["base"]["ref"]), ] ) sys.stdout.write("\n\n{0}\n\n".format("\n".join(output))) sys.stdout.flush() def download_unittest_reports(options): print("Downloading remote unittest reports...") sys.stdout.flush() workspace = options.workspace xml_reports_path = os.path.join(workspace, "xml-test-reports") if os.path.isdir(xml_reports_path): shutil.rmtree(xml_reports_path) os.makedirs(xml_reports_path) cmds = ( "salt {0} archive.tar zcvf /tmp/xml-test-reports.tar.gz '*.xml' cwd=/tmp/xml-unittests-output/", "salt {0} cp.push /tmp/xml-test-reports.tar.gz", "mv -f /var/cache/salt/master/minions/{1}/files/tmp/xml-test-reports.tar.gz {2} && " "tar zxvf {2}/xml-test-reports.tar.gz -C {2}/xml-test-reports && " "rm -f {2}/xml-test-reports.tar.gz", ) vm_name = options.download_unittest_reports for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def download_coverage_report(options): print("Downloading remote coverage report...") sys.stdout.flush() workspace = options.workspace vm_name = options.download_coverage_report if os.path.isfile(os.path.join(workspace, "coverage.xml")): os.unlink(os.path.join(workspace, "coverage.xml")) cmds = ( "salt {0} archive.gzip /tmp/coverage.xml", "salt {0} cp.push /tmp/coverage.xml.gz", "gunzip /var/cache/salt/master/minions/{1}/files/tmp/coverage.xml.gz", "mv /var/cache/salt/master/minions/{1}/files/tmp/coverage.xml {2}", ) for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def download_remote_logs(options): print("Downloading remote logs...") sys.stdout.flush() workspace = options.workspace vm_name = options.download_remote_logs for fname in ("salt-runtests.log", "minion.log"): if os.path.isfile(os.path.join(workspace, fname)): os.unlink(os.path.join(workspace, fname)) if not options.remote_log_path: options.remote_log_path = ["/tmp/salt-runtests.log", "/var/log/salt/minion"] cmds = [] for remote_log in options.remote_log_path: cmds.extend( [ "salt {{0}} archive.gzip {0}".format(remote_log), "salt {{0}} cp.push {0}.gz".format(remote_log), "gunzip /var/cache/salt/master/minions/{{1}}/files{0}.gz".format( remote_log ), "mv /var/cache/salt/master/minions/{{1}}/files{0} {{2}}/{1}".format( remote_log, "{0}{1}".format( os.path.basename(remote_log), "" if remote_log.endswith(".log") else ".log", ), ), ] ) for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def download_packages(options): print("Downloading packages...") sys.stdout.flush() workspace = options.workspace vm_name = options.download_packages for fglob in ("salt-*.rpm", "salt-*.deb", "salt-*.pkg.xz", "salt-buildpackage.log"): for fname in glob.glob(os.path.join(workspace, fglob)): if os.path.isfile(fname): os.unlink(fname) cmds = [ ( "salt {{0}} archive.tar czf {0}.tar.gz sources='*.*' cwd={0}".format( options.package_artifact_dir ) ), "salt {{0}} cp.push {0}.tar.gz".format(options.package_artifact_dir), ( "tar -C {{2}} -xzf /var/cache/salt/master/minions/{{1}}/files{0}.tar.gz".format( options.package_artifact_dir ) ), ] for cmd in cmds: cmd = cmd.format(build_minion_target(options, vm_name), vm_name, workspace) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() if proc.returncode != 0: print("\nFailed to execute command. Exit code: {0}".format(proc.returncode)) time.sleep(0.25) def run(opts): """ RUN! """ vm_name = os.environ.get("JENKINS_SALTCLOUD_VM_NAME", generate_vm_name(opts)) if opts.download_remote_reports: if opts.test_without_coverage is False: opts.download_coverage_report = vm_name opts.download_unittest_reports = vm_name opts.download_packages = vm_name if opts.bootstrap_salt_commit is not None: if opts.bootstrap_salt_url is None: opts.bootstrap_salt_url = "https://github.com/saltstack/salt.git" cmd = ( "salt-cloud -l debug" ' --script-args "-D -g {bootstrap_salt_url} -n git {1}"' " -p {provider}_{platform} {0}".format( vm_name, os.environ.get( "SALT_MINION_BOOTSTRAP_RELEASE", opts.bootstrap_salt_commit ), **opts.__dict__ ) ) else: cmd = ( "salt-cloud -l debug" ' --script-args "-D -n git {1}" -p {provider}_{platform} {0}'.format( vm_name, os.environ.get( "SALT_MINION_BOOTSTRAP_RELEASE", opts.bootstrap_salt_commit ), **opts.__dict__ ) ) if opts.splay is not None: # Sleep a random number of seconds cloud_downtime = random.randint(0, opts.splay) print( "Sleeping random period before calling salt-cloud: {0}".format( cloud_downtime ) ) time.sleep(cloud_downtime) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = NonBlockingPopen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stream_stds=True, ) proc.poll_and_read_until_finish(interval=0.5) proc.communicate() retcode = proc.returncode if retcode != 0: print("Failed to bootstrap VM. Exit code: {0}".format(retcode)) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) print("VM Bootstrapped. Exit code: {0}".format(retcode)) sys.stdout.flush() # Sleep a random number of seconds bootstrap_downtime = random.randint(0, opts.splay) print( "Sleeping for {0} seconds to allow the minion to breathe a little".format( bootstrap_downtime ) ) sys.stdout.flush() time.sleep(bootstrap_downtime) if opts.bootstrap_salt_commit is not None: # Let's find out if the installed version matches the passed in pillar # information print("Grabbing bootstrapped minion version information ... ") cmd = "salt -t 100 {0} --out json test.version".format( build_minion_target(opts, vm_name) ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, _ = proc.communicate() retcode = proc.returncode if retcode != 0: print( "Failed to get the bootstrapped minion version. Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) outstr = salt.utils.stringutils.to_str(stdout).strip() if not outstr: print( "Failed to get the bootstrapped minion version(no output). Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) try: version_info = salt.utils.json.loads(outstr) bootstrap_minion_version = os.environ.get( "SALT_MINION_BOOTSTRAP_RELEASE", opts.bootstrap_salt_commit[:7] ) print("Minion reported salt version: {0}".format(version_info)) if bootstrap_minion_version not in version_info[vm_name]: print("\n\nATTENTION!!!!\n") print( "The boostrapped minion version commit does not contain the desired commit:" ) print( " '{0}' does not contain '{1}'".format( version_info[vm_name], bootstrap_minion_version ) ) print("\n\n") sys.stdout.flush() # if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ: # delete_vm(opts) # sys.exit(retcode) else: print("matches!") except ValueError: print("Failed to load any JSON from '{0}'".format(outstr)) if opts.cloud_only: # Run Cloud Provider tests preparation SLS cloud_provider_downtime = random.randint(3, opts.splay) time.sleep(cloud_provider_downtime) cmd = ( 'salt -t 900 {target} state.sls {cloud_prep_sls} pillar="{pillar}" ' "--no-color".format( target=build_minion_target(opts, vm_name), cloud_prep_sls="cloud-only", pillar=build_pillar_data(opts), ) ) else: # Run standard preparation SLS standard_sls_downtime = random.randint(3, opts.splay) time.sleep(standard_sls_downtime) cmd = ( 'salt -t 1800 {target} state.sls {prep_sls} pillar="{pillar}" ' "--no-color".format( target=build_minion_target(opts, vm_name), prep_sls=opts.prep_sls, pillar=build_pillar_data(opts), ) ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() if stdout: print(salt.utils.stringutils.to_str(stdout)) if stderr: print(salt.utils.stringutils.to_str(stderr)) sys.stdout.flush() retcode = proc.returncode if retcode != 0: print( "Failed to execute the preparation SLS file. Exit code: {0}".format(retcode) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) if opts.cloud_only: cloud_provider_pillar = random.randint(3, opts.splay) time.sleep(cloud_provider_pillar) # Run Cloud Provider tests pillar preparation SLS cmd = ( 'salt -t 600 {target} state.sls {cloud_prep_sls} pillar="{pillar}" ' "--no-color".format( target=build_minion_target(opts, vm_name), cloud_prep_sls="cloud-test-configs", pillar=build_pillar_data(opts), ) ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout, stderr = proc.communicate() if stdout: # DO NOT print the state return here! print("Cloud configuration files provisioned via pillar.") if stderr: print(salt.utils.stringutils.to_str(stderr)) sys.stdout.flush() retcode = proc.returncode if retcode != 0: print( "Failed to execute the preparation SLS file. Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) if opts.prep_sls_2 is not None: sls_2_downtime = random.randint(3, opts.splay) time.sleep(sls_2_downtime) # Run the 2nd preparation SLS cmd = ( 'salt -t 30 {target} state.sls {prep_sls_2} pillar="{pillar}" ' "--no-color".format( prep_sls_2=opts.prep_sls_2, pillar=build_pillar_data(opts), target=build_minion_target(opts, vm_name), ) ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) stdout, stderr = proc.communicate() if stdout: print(salt.utils.stringutils.to_str(stdout)) if stderr: print(salt.utils.stringutils.to_str(stderr)) sys.stdout.flush() retcode = proc.returncode if retcode != 0: print( "Failed to execute the 2nd preparation SLS file. Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) # Run remote checks if opts.test_git_url is not None: test_git_downtime = random.randint(1, opts.splay) time.sleep(test_git_downtime) # Let's find out if the cloned repository if checked out from the # desired repository print("Grabbing the cloned repository remotes information ... ") cmd = "salt -t 100 {0} --out json git.remote_get /testing".format( build_minion_target(opts, vm_name) ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, _ = proc.communicate() retcode = proc.returncode if retcode != 0: print( "Failed to get the cloned repository remote. Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) if not stdout: print( "Failed to get the cloned repository remote(no output). Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) try: remotes_info = salt.utils.json.loads(stdout.strip()) if ( remotes_info is None or remotes_info[vm_name] is None or opts.test_git_url not in remotes_info[vm_name] ): print("The cloned repository remote is not the desired one:") print(" '{0}' is not in {1}".format(opts.test_git_url, remotes_info)) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) print("matches!") except ValueError: print( "Failed to load any JSON from '{0}'".format( salt.utils.stringutils.to_str(stdout).strip() ) ) if opts.test_git_commit is not None: test_git_commit_downtime = random.randint(1, opts.splay) time.sleep(test_git_commit_downtime) # Let's find out if the cloned repository is checked out at the desired # commit print("Grabbing the cloned repository commit information ... ") cmd = "salt -t 100 {0} --out json git.revision /testing".format( build_minion_target(opts, vm_name) ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, _ = proc.communicate() sys.stdout.flush() retcode = proc.returncode if retcode != 0: print( "Failed to get the cloned repository revision. Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) if not stdout: print( "Failed to get the cloned repository revision(no output). Exit code: {0}".format( retcode ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) try: revision_info = salt.utils.json.loads(stdout.strip()) if revision_info[vm_name][7:] != opts.test_git_commit[7:]: print("The cloned repository commit is not the desired one:") print( " '{0}' != '{1}'".format( revision_info[vm_name][:7], opts.test_git_commit[:7] ) ) sys.stdout.flush() if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) sys.exit(retcode) print("matches!") except ValueError: print( "Failed to load any JSON from '{0}'".format( salt.utils.stringutils.to_str(stdout).strip() ) ) # Run tests here test_begin_downtime = random.randint(3, opts.splay) time.sleep(test_begin_downtime) cmd = 'salt -t 1800 {target} state.sls {sls} pillar="{pillar}" --no-color'.format( sls=opts.sls, pillar=build_pillar_data(opts), target=build_minion_target(opts, vm_name), ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() outstr = salt.utils.stringutils.to_str(stdout) if outstr: print(outstr) if stderr: print(salt.utils.stringutils.to_str(stderr)) sys.stdout.flush() try: match = re.search(r"Test Suite Exit Code: (?P[\d]+)", outstr) retcode = int(match.group("exitcode")) except AttributeError: # No regex matching retcode = 1 except ValueError: # Not a number!? retcode = 1 except TypeError: # No output!? retcode = 1 if outstr: # Anything else, raise the exception raise if retcode == 0: # Build packages time.sleep(3) cmd = 'salt -t 1800 {target} state.sls buildpackage pillar="{pillar}" --no-color'.format( pillar=build_pillar_data(opts), target=build_minion_target(opts, vm_name), ) print("Running CMD: {0}".format(cmd)) sys.stdout.flush() proc = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) stdout, stderr = proc.communicate() if stdout: print(salt.utils.stringutils.to_str(stdout)) if stderr: print(salt.utils.stringutils.to_str(stderr)) sys.stdout.flush() # Grab packages and log file (or just log file if build failed) download_packages(opts) if opts.download_remote_reports: # Download unittest reports download_unittest_reports(opts) # Download coverage report if opts.test_without_coverage is False: download_coverage_report(opts) if opts.clean and "JENKINS_SALTCLOUD_VM_NAME" not in os.environ: delete_vm(opts) return retcode def parse(): """ Parse the CLI options """ parser = optparse.OptionParser() parser.add_option( "--vm-prefix", default=os.environ.get("JENKINS_VM_NAME_PREFIX", "ZJENKINS"), help="The bootstrapped machine name prefix", ) parser.add_option( "-w", "--workspace", default=os.path.abspath( os.environ.get("WORKSPACE", os.path.dirname(os.path.dirname(__file__))) ), help="Path the execution workspace", ) parser.add_option( "--platform", default=os.environ.get("JENKINS_SALTCLOUD_VM_PLATFORM", None), help="The target platform, choose from:\ncent6\ncent5\nubuntu12.04", ) parser.add_option( "--provider", default=os.environ.get("JENKINS_SALTCLOUD_VM_PROVIDER", None), help="The vm provider", ) parser.add_option( "--bootstrap-salt-url", default=None, help="The salt git repository url used to boostrap a minion", ) parser.add_option( "--bootstrap-salt-commit", default=None, help="The salt git commit used to boostrap a minion", ) parser.add_option( "--test-git-url", default=None, help="The testing git repository url" ) parser.add_option( "--test-git-commit", default=None, help="The testing git commit to track" ) parser.add_option( "--test-transport", default="zeromq", choices=("zeromq", "tcp"), help=( "Select which transport to run the integration tests with, " "zeromq or tcp. Default: %default" ), ) parser.add_option( "--test-without-coverage", default=False, action="store_true", help="Do not generate coverage reports", ) parser.add_option( "--prep-sls", default="git.salt", help="The sls file to execute to prepare the system", ) parser.add_option( "--prep-sls-2", default=None, help="An optional 2nd system preparation SLS" ) parser.add_option( "--sls", default="testrun-no-deps", help="The final sls file to execute" ) parser.add_option( "--pillar", action="append", nargs=2, help="Pillar (key, value)s to pass to the sls file. " "Example: '--pillar pillar_key pillar_value'", ) parser.add_option( "--no-clean", dest="clean", default=True, action="store_false", help="Clean up the built vm", ) parser.add_option( "--echo-parseable-environment", default=False, action="store_true", help="Print a parseable KEY=VAL output", ) parser.add_option("--pull-request", type=int, help="Include the PR info only") parser.add_option("--delete-vm", default=None, help="Delete a running VM") parser.add_option( "--download-remote-reports", default=False, action="store_true", help="Download remote reports when running remote 'testrun' state", ) parser.add_option( "--download-unittest-reports", default=None, help="Download the XML unittest results", ) parser.add_option( "--download-coverage-report", default=None, help="Download the XML coverage reports", ) parser.add_option( "--remote-log-path", action="append", default=[], help="Provide additional log paths to download from remote minion", ) parser.add_option( "--download-remote-logs", default=None, help="Download remote minion and runtests log files", ) parser.add_option( "--grain-target", action="append", default=[], help="Match minions using compound matchers, the minion ID, plus the passed grain.", ) parser.add_option( "--cloud-only", default=False, action="store_true", help="Run the cloud provider tests only.", ) parser.add_option( "--build-packages", default=True, action="store_true", help="Run buildpackage.py to create packages off of the git build.", ) # These next three options are ignored if --build-packages is False parser.add_option( "--package-source-dir", default="/testing", help="Directory where the salt source code checkout is found " "(default: %default)", ) parser.add_option( "--package-build-dir", default="/tmp/salt-buildpackage", help="Build root for automated package builds (default: %default)", ) parser.add_option( "--package-artifact-dir", default="/tmp/salt-packages", help="Location on the minion from which packages should be " "retrieved (default: %default)", ) parser.add_option( "--splay", default="10", help="The number of seconds across which calls to provisioning components should be made", ) options, args = parser.parse_args() if options.delete_vm is not None and not options.test_git_commit: delete_vm(options) parser.exit(0) if options.download_unittest_reports is not None and not options.test_git_commit: download_unittest_reports(options) parser.exit(0) if options.test_without_coverage is False: if options.download_coverage_report is not None and not options.test_git_commit: download_coverage_report(options) parser.exit(0) if options.download_remote_logs is not None and not options.test_git_commit: download_remote_logs(options) parser.exit(0) if not options.platform and not options.pull_request: parser.exit("--platform or --pull-request is required") if not options.provider and not options.pull_request: parser.exit("--provider or --pull-request is required") if options.echo_parseable_environment: echo_parseable_environment(options, parser) parser.exit(0) if not options.test_git_commit and not options.pull_request: parser.exit("--commit or --pull-request is required") return options if __name__ == "__main__": exit_code = run(parse()) print("Exit Code: {0}".format(exit_code)) sys.exit(exit_code)