mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 00:55:19 +00:00
5c864fa003
By restricting this function call to when the script is successful, we end up losing the log file from buildpackage.py when it fails, making it next to impossible to troubleshoot. This commit makes jenkins retrieve at least the log file, no matter what happens.
973 lines
31 KiB
Python
973 lines
31 KiB
Python
# -*- 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!!
|
|
'''
|
|
|
|
# Import python libs
|
|
from __future__ import print_function
|
|
import glob
|
|
import os
|
|
import re
|
|
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
|
|
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
|
|
import yaml
|
|
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}
|
|
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 yaml.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):
|
|
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']),
|
|
'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-unitests-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:
|
|
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__
|
|
)
|
|
)
|
|
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()
|
|
|
|
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
|
|
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)
|
|
|
|
if not stdout.strip():
|
|
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 = json.loads(stdout.strip())
|
|
bootstrap_minion_version = os.environ.get(
|
|
'SALT_MINION_BOOTSTRAP_RELEASE',
|
|
opts.bootstrap_salt_commit[:7]
|
|
)
|
|
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!r} does not contain {1!r}'.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!r}'.format(stdout.strip()))
|
|
|
|
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()
|
|
|
|
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()
|
|
|
|
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:
|
|
time.sleep(3)
|
|
# 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.')
|
|
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,
|
|
)
|
|
stdout, stderr = proc.communicate()
|
|
|
|
if stdout:
|
|
print(stdout)
|
|
sys.stdout.flush()
|
|
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)
|
|
|
|
# Run remote checks
|
|
if opts.test_git_url is not None:
|
|
time.sleep(1)
|
|
# 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,
|
|
)
|
|
|
|
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 = 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()))
|
|
|
|
if opts.test_git_commit is not None:
|
|
time.sleep(1)
|
|
|
|
# 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 = 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
|
|
time.sleep(3)
|
|
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()
|
|
|
|
if stdout:
|
|
print(stdout)
|
|
sys.stdout.flush()
|
|
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)
|
|
|
|
if opts.download_remote_reports:
|
|
# Download unittest reports
|
|
download_unittest_reports(opts)
|
|
# Download coverage report
|
|
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'),
|
|
help='Set to raet to run integration tests with raet transport. Default: %default')
|
|
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)',
|
|
)
|
|
|
|
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.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)
|