123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008 |
- #!/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 os
- import re
- import sys
- import time
- import shutil
- import optparse
- import subprocess
- import random
- # 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', 'raet', 'tcp'),
- help=('Select which transport to run the integration tests with, '
- 'zeromq, raet, 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)
|