salt/tests/jenkins.py

983 lines
31 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
#!/usr/bin/env python
'''
This script is used to test Salt from a Jenkins server, specifically
jenkins.saltstack.com.
This script is intended to be shell-centric!!
'''
2013-08-10 07:23:32 +00:00
# Import python libs
2014-02-03 11:07:11 +00:00
from __future__ import print_function
import glob
import os
import re
2013-08-10 07:23:32 +00:00
import sys
import json
import time
import shutil
import optparse
import subprocess
# Import Salt libs
try:
from salt.utils.nb_popen import NonBlockingPopen
except ImportError:
# Salt not installed, or nb_popen was not yet shipped with it
2013-09-06 11:14:27 +00:00
SALT_LIB = os.path.abspath(
os.path.dirname(os.path.dirname(__file__))
)
2013-09-06 11:14:27 +00:00
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,
2013-09-06 11:14:27 +00:00
os.path.join(SALT_LIB, 'salt', 'utils')
)
from nb_popen import NonBlockingPopen
# Import 3rd-party libs
import yaml
try:
import requests
HAS_REQUESTS = True
except ImportError:
HAS_REQUESTS = False
SALT_GIT_URL = 'https://github.com/saltstack/salt.git'
2014-01-10 17:28:07 +00:00
def build_pillar_data(options):
'''
Build a YAML formatted string to properly pass pillar data
'''
pillar = {'test_transport': options.test_transport,
2014-12-08 17:32:28 +00:00
'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))
2014-04-13 12:16:22 +00:00
return yaml.dump(pillar, default_flow_style=True, indent=0, width=sys.maxint).rstrip()
def build_minion_target(options, vm_name):
2014-04-15 11:28:01 +00:00
target = vm_name
for grain in options.grain_target:
target += ' and G@{0}'.format(grain)
2014-04-15 11:35:48 +00:00
if options.grain_target:
return '"{0}"'.format(target)
2014-04-15 11:28:01 +00:00
return target
def generate_vm_name(options):
2013-09-06 11:14:27 +00:00
'''
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)
2013-08-17 06:59:13 +00:00
2013-09-06 11:14:27 +00:00
def delete_vm(options):
2013-09-06 11:14:27 +00:00
'''
Stop a VM
'''
cmd = 'salt-cloud -d {0} -y'.format(options.delete_vm)
2013-08-17 06:59:13 +00:00
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = NonBlockingPopen(
2013-08-17 06:59:13 +00:00
cmd,
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
stderr=subprocess.PIPE,
stream_stds=True
2013-08-17 06:59:13 +00:00
)
proc.poll_and_read_until_finish(interval=0.5)
proc.communicate()
2013-08-17 06:59:13 +00:00
2014-11-24 18:58:24 +00:00
def echo_parseable_environment(options, parser):
2013-09-06 11:14:27 +00:00
'''
Echo NAME=VAL parseable output
'''
2014-01-10 17:28:07 +00:00
output = []
if options.platform:
name = generate_vm_name(options)
2014-01-10 17:28:07 +00:00
output.extend([
'JENKINS_SALTCLOUD_VM_PLATFORM={0}'.format(options.platform),
2014-01-10 19:24:23 +00:00
'JENKINS_SALTCLOUD_VM_NAME={0}'.format(name)
2014-01-10 17:28:07 +00:00
])
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 = {}
2014-11-22 02:11:19 +00:00
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):
headers = {
'Authorization': 'token {0}'.format(
open(github_access_token_path).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']),
2014-11-22 02:11:19 +00:00
'SALT_PR_GIT_COMMIT={0}'.format(pr_details['head']['sha']),
'SALT_PR_GIT_BASE_BRANCH={0}'.format(pr_details['base']['ref']),
])
2014-01-10 19:37:22 +00:00
sys.stdout.write('\n\n{0}\n\n'.format('\n'.join(output)))
2013-09-06 11:14:27 +00:00
sys.stdout.flush()
def download_unittest_reports(options):
print('Downloading remote unittest reports...')
sys.stdout.flush()
2014-01-05 00:10:24 +00:00
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)
2014-01-05 00:10:24 +00:00
os.makedirs(xml_reports_path)
cmds = (
2014-12-15 19:27:25 +00:00
'salt {0} archive.tar zcvf /tmp/xml-test-reports.tar.gz \'*.xml\' cwd=/tmp/xml-unittests-output/',
2013-09-09 20:52:58 +00:00
'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} && '
2014-08-20 16:07:12 +00:00
'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
)
)
2013-10-03 14:08:50 +00:00
time.sleep(0.25)
2013-09-08 17:18:21 +00:00
def download_coverage_report(options):
print('Downloading remote coverage report...')
sys.stdout.flush()
2013-09-09 20:52:58 +00:00
workspace = options.workspace
2013-09-10 11:15:19 +00:00
vm_name = options.download_coverage_report
2013-09-09 20:52:58 +00:00
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
)
)
2013-10-03 14:08:50 +00:00
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),
2014-04-27 12:56:17 +00:00
'' 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
)
)
2013-10-03 14:08:50 +00:00
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),
2014-09-11 17:56:45 +00:00
('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)
2013-09-06 11:14:27 +00:00
def run(opts):
'''
RUN!
'''
2013-09-06 11:14:27 +00:00
vm_name = os.environ.get(
'JENKINS_SALTCLOUD_VM_NAME',
generate_vm_name(opts)
2013-09-06 11:14:27 +00:00
)
2013-09-10 11:21:41 +00:00
if opts.download_remote_reports:
2014-12-08 17:32:28 +00:00
if opts.test_without_coverage is False:
opts.download_coverage_report = vm_name
2013-09-10 11:21:41 +00:00
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'
2014-04-13 10:40:58 +00:00
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__
)
2014-04-13 10:40:58 +00:00
)
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__
)
2014-04-13 10:40:58 +00:00
)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = NonBlockingPopen(
cmd,
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
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()
print('Sleeping for 5 seconds to allow the minion to breathe a little')
sys.stdout.flush()
time.sleep(5)
if opts.bootstrap_salt_commit is not None:
# Let's find out if the installed version matches the passed in pillar
# information
2014-04-15 11:40:05 +00:00
print('Grabbing bootstrapped minion version information ... ')
2014-04-15 11:18:45 +00:00
cmd = 'salt -t 100 {0} --out json test.version'.format(build_minion_target(opts, vm_name))
print('Running CMD: {0}'.format(cmd))
2014-04-15 11:40:05 +00:00
sys.stdout.flush()
proc = subprocess.Popen(
2014-04-15 11:18:45 +00:00
cmd,
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
stderr=subprocess.PIPE,
)
stdout, _ = proc.communicate()
retcode = proc.returncode
if retcode != 0:
2014-04-15 11:40:05 +00:00
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)
2014-04-29 23:41:18 +00:00
if not stdout.strip():
2014-04-15 11:40:05 +00:00
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)
2014-04-29 23:43:41 +00:00
try:
version_info = json.loads(stdout.strip())
bootstrap_minion_version = os.environ.get(
'SALT_MINION_BOOTSTRAP_RELEASE',
opts.bootstrap_salt_commit[:7]
2014-06-07 12:53:02 +00:00
)
if bootstrap_minion_version not in version_info[vm_name]:
2014-04-29 23:43:41 +00:00
print('\n\nATTENTION!!!!\n')
print('The boostrapped minion version commit does not contain the desired commit:')
print(' {0!r} does not contain {1!r}'.format(version_info[vm_name], bootstrap_minion_version))
2014-04-29 23:43:41 +00:00
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!r}'.format(stdout.strip()))
2014-08-31 02:18:12 +00:00
if opts.cloud_only:
# Run Cloud Provider tests preparation SLS
time.sleep(3)
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
time.sleep(3)
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()
2014-04-07 22:14:27 +00:00
proc = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
stderr=subprocess.PIPE,
)
2014-06-06 11:25:01 +00:00
stdout, stderr = proc.communicate()
2014-04-07 22:14:27 +00:00
if stdout:
print(stdout)
2014-04-07 20:16:36 +00:00
sys.stdout.flush()
2014-06-06 11:25:01 +00:00
if stderr:
print(stderr)
sys.stderr.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)
2014-08-31 02:18:12 +00:00
if opts.cloud_only:
time.sleep(3)
# Run Cloud Provider tests pillar preparation SLS
cmd = (
2014-08-31 15:31:12 +00:00
'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.')
sys.stdout.flush()
if stderr:
print(stderr)
sys.stderr.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:
time.sleep(3)
# 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,
)
2014-06-06 11:25:01 +00:00
stdout, stderr = proc.communicate()
if stdout:
print(stdout)
sys.stdout.flush()
2014-06-06 11:25:01 +00:00
if stderr:
print(stderr)
sys.stderr.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)
2014-04-14 10:08:24 +00:00
# Run remote checks
if opts.test_git_url is not None:
2014-04-14 10:08:24 +00:00
time.sleep(1)
2014-04-07 20:16:36 +00:00
# Let's find out if the cloned repository if checked out from the
# desired repository
2014-04-15 11:40:05 +00:00
print('Grabbing the cloned repository remotes information ... ')
2014-04-15 11:18:45 +00:00
cmd = 'salt -t 100 {0} --out json git.remote_get /testing'.format(build_minion_target(opts, vm_name))
print('Running CMD: {0}'.format(cmd))
2014-04-15 11:40:05 +00:00
sys.stdout.flush()
proc = subprocess.Popen(
2014-04-15 11:18:45 +00:00
cmd,
2014-04-07 20:16:36 +00:00
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
stderr=subprocess.PIPE,
2014-04-07 20:16:36 +00:00
)
2014-06-06 11:25:01 +00:00
proc.communicate()
2014-04-07 20:16:36 +00:00
retcode = proc.returncode
if retcode != 0:
2014-04-15 11:40:05 +00:00
print('Failed to get the cloned repository remote. Exit code: {0}'.format(retcode))
2014-04-07 20:16:36 +00:00
sys.stdout.flush()
if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ:
delete_vm(opts)
2014-04-07 20:16:36 +00:00
sys.exit(retcode)
if not stdout:
2014-04-15 11:40:05 +00:00
print('Failed to get the cloned repository remote(no output). Exit code: {0}'.format(retcode))
2014-04-07 20:16:36 +00:00
sys.stdout.flush()
if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ:
delete_vm(opts)
2014-04-07 20:16:36 +00:00
sys.exit(retcode)
2014-04-30 07:39:49 +00:00
try:
remotes_info = 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!r} 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!r}'.format(stdout.strip()))
2014-04-07 20:16:36 +00:00
if opts.test_git_commit is not None:
2014-04-14 10:08:24 +00:00
time.sleep(1)
# Let's find out if the cloned repository is checked out at the desired
# commit
2014-04-15 11:40:05 +00:00
print('Grabbing the cloned repository commit information ... ')
2014-04-15 11:18:45 +00:00
cmd = 'salt -t 100 {0} --out json git.revision /testing'.format(build_minion_target(opts, vm_name))
print('Running CMD: {0}'.format(cmd))
2014-04-15 11:40:05 +00:00
sys.stdout.flush()
proc = subprocess.Popen(
2014-04-15 11:18:45 +00:00
cmd,
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
stderr=subprocess.PIPE,
)
stdout, _ = proc.communicate()
2014-04-07 20:16:36 +00:00
sys.stdout.flush()
retcode = proc.returncode
if retcode != 0:
2014-04-15 11:40:05 +00:00
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:
2014-04-15 11:40:05 +00:00
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)
2014-04-30 07:39:49 +00:00
try:
revision_info = 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!r} != {1!r}'.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!r}'.format(stdout.strip()))
# Run tests here
2014-04-14 10:08:24 +00:00
time.sleep(3)
cmd = (
'salt -t 1800 {target} state.sls {sls} pillar="{pillar}" --no-color'.format(
2013-09-06 11:14:27 +00:00
sls=opts.sls,
pillar=build_pillar_data(opts),
target=build_minion_target(opts, vm_name),
2013-09-06 11:14:27 +00:00
)
)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = subprocess.Popen(
cmd,
shell=True,
stdout=subprocess.PIPE,
2014-04-30 07:39:49 +00:00
stderr=subprocess.PIPE,
)
2014-06-06 11:25:01 +00:00
stdout, stderr = proc.communicate()
if stdout:
print(stdout)
sys.stdout.flush()
2014-06-06 11:25:01 +00:00
if stderr:
print(stderr)
sys.stderr.flush()
try:
match = re.search(r'Test Suite Exit Code: (?P<exitcode>[\d]+)', stdout)
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 stdout:
# 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(stdout)
sys.stdout.flush()
if stderr:
print(stderr)
sys.stderr.flush()
# Grab packages and log file (or just log file if build failed)
download_packages(opts)
2013-09-10 11:21:41 +00:00
if opts.download_remote_reports:
# Download unittest reports
2013-09-10 11:21:41 +00:00
download_unittest_reports(opts)
# Download coverage report
2014-12-08 18:07:04 +00:00
if opts.test_without_coverage is False:
2014-12-08 17:32:28 +00:00
download_coverage_report(opts)
if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ:
delete_vm(opts)
return retcode
2013-08-10 07:23:32 +00:00
def parse():
'''
Parse the CLI options
2013-08-10 07:23:32 +00:00
'''
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'
)
2013-09-08 22:41:38 +00:00
parser.add_option(
'--platform',
default=os.environ.get('JENKINS_SALTCLOUD_VM_PLATFORM', None),
2013-09-06 11:14:27 +00:00
help='The target platform, choose from:\ncent6\ncent5\nubuntu12.04')
2013-09-08 22:41:38 +00:00
parser.add_option(
'--provider',
default=os.environ.get('JENKINS_SALTCLOUD_VM_PROVIDER', None),
2013-09-06 11:14:27 +00:00
help='The vm provider')
2014-01-02 21:43:44 +00:00
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',
2014-04-13 10:40:58 +00:00
default=None,
help='The testing git repository url')
2013-09-08 22:41:38 +00:00
parser.add_option(
'--test-git-commit',
2014-04-13 10:40:58 +00:00
default=None,
help='The testing git commit to track')
parser.add_option(
'--test-transport',
default='zeromq',
choices=('zeromq', 'raet'),
help='Set to raet to run integration tests with raet transport. Default: %default')
2014-12-08 17:32:28 +00:00
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')
2013-09-08 22:41:38 +00:00
parser.add_option(
'--sls',
default='testrun-no-deps',
help='The final sls file to execute')
2013-09-08 22:41:38 +00:00
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',
2013-09-06 11:14:27 +00:00
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'
)
2013-09-06 11:14:27 +00:00
parser.add_option(
'--delete-vm',
default=None,
help='Delete a running VM'
2013-09-06 11:14:27 +00:00
)
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)',
)
2013-09-06 11:14:27 +00:00
2013-08-10 07:23:32 +00:00
options, args = parser.parse_args()
2013-09-06 11:14:27 +00:00
if options.delete_vm is not None and not options.test_git_commit:
delete_vm(options)
2013-09-06 11:14:27 +00:00
parser.exit(0)
if options.download_unittest_reports is not None and not options.test_git_commit:
download_unittest_reports(options)
parser.exit(0)
2014-12-08 17:32:28 +00:00
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)
2014-01-10 18:32:05 +00:00
if not options.platform and not options.pull_request:
parser.exit('--platform or --pull-request is required')
2013-09-06 11:14:27 +00:00
2014-01-10 18:32:05 +00:00
if not options.provider and not options.pull_request:
parser.exit('--provider or --pull-request is required')
2013-09-06 11:14:27 +00:00
if options.echo_parseable_environment:
2014-11-24 18:58:24 +00:00
echo_parseable_environment(options, parser)
2013-09-06 11:14:27 +00:00
parser.exit(0)
if not options.test_git_commit and not options.pull_request:
parser.exit('--commit or --pull-request is required')
2013-09-10 11:15:19 +00:00
2013-09-06 11:14:27 +00:00
return options
2013-08-10 07:23:32 +00:00
if __name__ == '__main__':
2013-09-06 11:14:27 +00:00
exit_code = run(parse())
print('Exit Code: {0}'.format(exit_code))
2013-08-10 07:23:32 +00:00
sys.exit(exit_code)