12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- #!/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<exitcode>[\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)
|