salt/tests/jenkins.py

435 lines
12 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
import os
import re
2013-08-10 07:23:32 +00:00
import sys
import time
import random
import shutil
import hashlib
import optparse
try:
from salt.utils.vt import Terminal
except ImportError:
# Salt not installed, or vt 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.vt import Terminal
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 vt import Terminal # pylint: disable=F0401
2013-09-06 11:14:27 +00:00
def generate_vm_name(platform):
'''
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 = hashlib.md5(
str(random.randint(1, 100000000))).hexdigest()[:6]
return 'ZJENKINS-{0}-{1}'.format(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 = Terminal(
2013-08-17 06:59:13 +00:00
cmd,
shell=True,
stream_stdout=True,
stream_stderr=True,
log_stderr=True,
log_stdout=True
2013-08-17 06:59:13 +00:00
)
while proc.isalive():
time.sleep(0.025)
return proc.exitstatus
2013-08-17 06:59:13 +00:00
def echo_parseable_environment(options):
2013-09-06 11:14:27 +00:00
'''
Echo NAME=VAL parseable output
'''
name = generate_vm_name(options.platform)
2013-09-06 11:14:27 +00:00
output = (
2013-09-08 11:06:45 +00:00
'JENKINS_SALTCLOUD_VM_PROVIDER={provider}\n'
'JENKINS_SALTCLOUD_VM_PLATFORM={platform}\n'
'JENKINS_SALTCLOUD_VM_NAME={name}\n').format(name=name,
provider=options.provider,
platform=options.platform)
2013-09-06 11:14:27 +00:00
sys.stdout.write(output)
sys.stdout.flush()
def download_unittest_reports(options):
print('Downloading remote unittest reports...')
sys.stdout.flush()
if os.path.isdir('xml-test-reports'):
shutil.rmtree('xml-test-reports')
os.makedirs('xml-test-reports')
cmds = (
2013-09-09 20:52:58 +00:00
'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/{0}/files/tmp/xml-test-reports.tar.gz {1}',
'tar zxvf {1}/xml-test-reports.tar.gz -C {1}/xml-test-reports',
'rm -f {1}/xml-test-reports.tar.gz'
)
vm_name = options.download_unittest_reports
2013-09-09 20:52:58 +00:00
workspace = options.workspace
for cmd in cmds:
2013-09-09 20:52:58 +00:00
cmd = cmd.format(vm_name, workspace)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = Terminal(
cmd,
shell=True,
stream_stdout=True,
stream_stderr=True,
log_stderr=True,
log_stdout=True
)
while proc.isalive():
time.sleep(0.025)
if proc.exitstatus != 0:
print(
'\nFailed to execute command. Exit code: {0}'.format(
proc.exitstatus
)
)
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',
2013-09-09 20:52:58 +00:00
'gunzip /var/cache/salt/master/minions/{0}/files/tmp/coverage.xml.gz',
'mv /var/cache/salt/master/minions/{0}/files/tmp/coverage.xml {1}'
)
for cmd in cmds:
cmd = cmd.format(vm_name, workspace)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = Terminal(
cmd,
shell=True,
stream_stdout=True,
stream_stderr=True,
log_stderr=True,
log_stdout=True
)
while proc.isalive():
time.sleep(0.025)
if proc.exitstatus != 0:
print(
'\nFailed to execute command. Exit code: {0}'.format(
proc.exitstatus
)
)
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))
cmds = (
'salt {0} archive.gzip /tmp/salt-runtests.log',
'salt {0} archive.gzip /var/log/salt/minion',
'salt {0} cp.push /tmp/salt-runtests.log.gz',
'salt {0} cp.push /var/log/salt/minion.gz',
'gunzip /var/cache/salt/master/minions/{0}/files/tmp/salt-runtests.log.gz',
'gunzip /var/cache/salt/master/minions/{0}/files/var/log/salt/minion.gz',
'mv /var/cache/salt/master/minions/{0}/files/tmp/salt-runtests.log {1}/salt-runtests.log',
'mv /var/cache/salt/master/minions/{0}/files/var/log/salt/minion {1}/minion.log'
)
for cmd in cmds:
cmd = cmd.format(vm_name, workspace)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = Terminal(
cmd,
shell=True,
stream_stdout=True,
stream_stderr=True,
log_stderr=True,
log_stdout=True
)
while proc.isalive():
time.sleep(0.025)
if proc.exitstatus != 0:
print(
'\nFailed to execute command. Exit code: {0}'.format(
proc.exitstatus
)
)
2013-10-03 14:08:50 +00:00
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',
2013-09-06 11:14:27 +00:00
generate_vm_name(opts.platform)
)
2013-09-10 11:21:41 +00:00
if opts.download_remote_reports:
opts.download_coverage_report = vm_name
opts.download_unittest_reports = vm_name
2013-09-06 11:14:27 +00:00
cmd = (
'salt-cloud -l debug --script-args "-D -n git {commit}" -p '
'{provider}_{platform} {0}'.format(vm_name, **opts.__dict__)
)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = Terminal(
cmd,
shell=True,
stream_stdout=True,
stream_stderr=True,
log_stderr=True,
log_stdout=True
)
while proc.isalive():
time.sleep(0.025)
retcode = proc.exitstatus
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(vm_name)
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)
# Run tests here
cmd = (
'salt -t 1800 {vm_name} state.sls {sls} pillar="{pillar}" '
'--no-color'.format(
2013-09-06 11:14:27 +00:00
sls=opts.sls,
pillar=opts.pillar.format(commit=opts.commit),
vm_name=vm_name,
commit=opts.commit
2013-09-06 11:14:27 +00:00
)
)
print('Running CMD: {0}'.format(cmd))
sys.stdout.flush()
proc = Terminal(
cmd,
shell=True,
stream_stdout=True,
stream_stderr=True,
log_stderr=True,
log_stdout=True
)
retcode = None
while proc.isalive():
stdout, _ = proc.recv()
if stdout and retcode is None:
try:
match = re.search(r'Test Suite Exit Code: (?P<exitcode>[\d]+)',
stdout)
if match:
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
time.sleep(0.025)
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
2013-09-10 11:21:41 +00:00
download_coverage_report(opts)
if opts.clean and 'JENKINS_SALTCLOUD_VM_NAME' not in os.environ:
delete_vm(vm_name)
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(
'-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')
2013-09-08 22:41:38 +00:00
parser.add_option(
'--commit',
2013-09-06 11:14:27 +00:00
help='The git commit to track')
2013-09-08 22:41:38 +00:00
parser.add_option(
'--sls',
2013-09-06 11:14:27 +00:00
default='testrun',
help='The sls file to execute')
2013-09-08 22:41:38 +00:00
parser.add_option(
'--pillar',
2013-09-06 11:14:27 +00:00
default='{{git_commit: {commit}}}',
help='Pillar values to pass to the sls file')
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(
'--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(
'--download-remote-logs',
default=None,
help='Download remote minion and runtests log files'
)
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.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.commit:
download_unittest_reports(options)
parser.exit(0)
if options.download_coverage_report is not None and not options.commit:
download_coverage_report(options)
parser.exit(0)
if options.download_remote_logs is not None and not options.commit:
download_remote_logs(options)
parser.exit(0)
if not options.platform:
parser.exit('--platform is required')
2013-09-06 11:14:27 +00:00
if not options.provider:
parser.exit('--provider is required')
2013-09-06 11:14:27 +00:00
if options.echo_parseable_environment:
echo_parseable_environment(options)
2013-09-06 11:14:27 +00:00
parser.exit(0)
if not options.commit:
parser.exit('--commit 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)