Split playbook into vars, pre-playbook and playbook
In order to get to the point where playbooks that people write for tests are playbooks that they could conceivably also use outside of the zuul context, we need to remove the need for zuul-specific things in the main playbook. Add a pre-playbook that runs before the playbook and runs the things that are not tied to current JJB content - namely setting up the logger and prepping directories. Move the SUCCESS/FAILURE message to the post-playbook. Extract the injected variables into a variables file and add a -e@vars.yaml option to the playbook invocation. This provides variables in a known namespace. Obviously there is still an exercise in how a user might write a playbook that wants to consume those variables in some way. Change-Id: Ie5ec6ec65a03ceea9afc3ac59df73cb28f5ca4dd
This commit is contained in:
parent
f166784a28
commit
6767fceba6
|
@ -38,6 +38,7 @@ from zuul.lib import commandsocket
|
|||
|
||||
ANSIBLE_WATCHDOG_GRACE = 5 * 60
|
||||
ANSIBLE_DEFAULT_TIMEOUT = 2 * 60 * 60
|
||||
ANSIBLE_DEFAULT_PRE_TIMEOUT = 10 * 60
|
||||
ANSIBLE_DEFAULT_POST_TIMEOUT = 10 * 60
|
||||
|
||||
|
||||
|
@ -151,6 +152,8 @@ class JobDir(object):
|
|||
os.makedirs(self.ansible_root)
|
||||
self.known_hosts = os.path.join(self.ansible_root, 'known_hosts')
|
||||
self.inventory = os.path.join(self.ansible_root, 'inventory')
|
||||
self.vars = os.path.join(self.ansible_root, 'vars.yaml')
|
||||
self.pre_playbook = os.path.join(self.ansible_root, 'pre_playbook')
|
||||
self.playbook = os.path.join(self.ansible_root, 'playbook')
|
||||
self.post_playbook = os.path.join(self.ansible_root, 'post_playbook')
|
||||
self.config = os.path.join(self.ansible_root, 'ansible.cfg')
|
||||
|
@ -618,6 +621,7 @@ class NodeWorker(object):
|
|||
self._aborted_job = False
|
||||
self._watchdog_timeout = False
|
||||
self._sent_complete_event = False
|
||||
self.ansible_pre_proc = None
|
||||
self.ansible_job_proc = None
|
||||
self.ansible_post_proc = None
|
||||
self.workspace_root = config.get('launcher', 'workspace_root')
|
||||
|
@ -896,6 +900,7 @@ class NodeWorker(object):
|
|||
'SUCCESS', {})
|
||||
|
||||
def runJob(self, job, args):
|
||||
self.ansible_pre_proc = None
|
||||
self.ansible_job_proc = None
|
||||
self.ansible_post_proc = None
|
||||
result = None
|
||||
|
@ -924,6 +929,12 @@ class NodeWorker(object):
|
|||
job.sendWorkData(json.dumps(data))
|
||||
job.sendWorkStatus(0, 100)
|
||||
|
||||
pre_status = self.runAnsiblePrePlaybook(jobdir)
|
||||
if pre_status is None:
|
||||
# These should really never fail, so return None and have
|
||||
# zuul try again
|
||||
return result
|
||||
|
||||
job_status = self.runAnsiblePlaybook(jobdir, timeout)
|
||||
if job_status is None:
|
||||
# The result of the job is indeterminate. Zuul will
|
||||
|
@ -1013,7 +1024,7 @@ class NodeWorker(object):
|
|||
syncargs['rsync_opts'] = rsync_opts
|
||||
task = dict(synchronize=syncargs)
|
||||
if not scpfile.get('copy-after-failure'):
|
||||
task['when'] = 'success'
|
||||
task['when'] = 'success|bool'
|
||||
task.update(self.retry_args)
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1057,7 +1068,7 @@ class NodeWorker(object):
|
|||
task = dict(shell=shellargs,
|
||||
delegate_to='127.0.0.1')
|
||||
if not scpfile.get('copy-after-failure'):
|
||||
task['when'] = 'success'
|
||||
task['when'] = 'success|bool'
|
||||
|
||||
return task
|
||||
|
||||
|
@ -1086,11 +1097,11 @@ class NodeWorker(object):
|
|||
if rsync_opts:
|
||||
syncargs['rsync_opts'] = rsync_opts
|
||||
task = dict(synchronize=syncargs,
|
||||
when='success')
|
||||
when='success|bool')
|
||||
task.update(self.retry_args)
|
||||
tasks.append(task)
|
||||
task = dict(shell='lftp -f %s' % ftpscript,
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
ftpsource = ftpcontent
|
||||
if ftp.get('remove-prefix'):
|
||||
|
@ -1151,7 +1162,7 @@ class NodeWorker(object):
|
|||
if rsync_opts:
|
||||
syncargs['rsync_opts'] = rsync_opts
|
||||
task = dict(synchronize=syncargs,
|
||||
when='success')
|
||||
when='success|bool')
|
||||
task.update(self.retry_args)
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1179,7 +1190,7 @@ class NodeWorker(object):
|
|||
# content at the root *and* at a tag location).
|
||||
task = dict(shell=find_pipe.format(path=afssource,
|
||||
file=src_markers_file),
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1187,7 +1198,7 @@ class NodeWorker(object):
|
|||
# published site.
|
||||
task = dict(shell=find_pipe.format(path=afstarget,
|
||||
file=dst_markers_file),
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1199,7 +1210,7 @@ class NodeWorker(object):
|
|||
dst=dst_markers_file,
|
||||
exclude=exclude_file)
|
||||
task = dict(shell=exclude_command,
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1225,7 +1236,7 @@ class NodeWorker(object):
|
|||
src=src_markers_file,
|
||||
filter=filter_file))
|
||||
task = dict(shell=command,
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1241,7 +1252,7 @@ class NodeWorker(object):
|
|||
exclude=exclude_file,
|
||||
filter=filter_file))
|
||||
task = dict(shell=command,
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1259,7 +1270,7 @@ class NodeWorker(object):
|
|||
exclude=exclude_file,
|
||||
filter=filter_file))
|
||||
task = dict(shell=command,
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1288,7 +1299,7 @@ class NodeWorker(object):
|
|||
keytab=site['keytab'])
|
||||
|
||||
task = dict(shell=shellargs,
|
||||
when='success',
|
||||
when='success|bool',
|
||||
delegate_to='127.0.0.1')
|
||||
tasks.append(task)
|
||||
|
||||
|
@ -1301,7 +1312,7 @@ class NodeWorker(object):
|
|||
|
||||
task = dict(shell=shell)
|
||||
task['name'] = 'command generated from JJB'
|
||||
task['environment'] = parameters
|
||||
task['environment'] = "{{ zuul.environment }}"
|
||||
task['args'] = dict(chdir=parameters['WORKSPACE'])
|
||||
if executable:
|
||||
task['args']['executable'] = executable
|
||||
|
@ -1363,52 +1374,53 @@ class NodeWorker(object):
|
|||
if not timeout:
|
||||
timeout = ANSIBLE_DEFAULT_TIMEOUT
|
||||
|
||||
with open(jobdir.playbook, 'w') as playbook:
|
||||
pre_tasks = []
|
||||
tasks = []
|
||||
main_block = []
|
||||
error_block = []
|
||||
variables = []
|
||||
with open(jobdir.vars, 'w') as vars_yaml:
|
||||
variables = dict(
|
||||
timeout=timeout,
|
||||
environment=parameters,
|
||||
)
|
||||
zuul_vars = dict(zuul=variables)
|
||||
vars_yaml.write(
|
||||
yaml.safe_dump(zuul_vars, default_flow_style=False))
|
||||
|
||||
with open(jobdir.pre_playbook, 'w') as pre_playbook:
|
||||
|
||||
shellargs = "ssh-keyscan {{ ansible_host }} > %s" % (
|
||||
jobdir.known_hosts)
|
||||
pre_tasks.append(dict(shell=shellargs,
|
||||
delegate_to='127.0.0.1'))
|
||||
|
||||
tasks.append(dict(block=main_block,
|
||||
rescue=error_block))
|
||||
tasks = []
|
||||
tasks.append(dict(shell=shellargs, delegate_to='127.0.0.1'))
|
||||
|
||||
task = dict(file=dict(path='/tmp/console.html', state='absent'))
|
||||
main_block.append(task)
|
||||
tasks.append(task)
|
||||
|
||||
task = dict(zuul_console=dict(path='/tmp/console.html',
|
||||
port=19885))
|
||||
main_block.append(task)
|
||||
tasks.append(task)
|
||||
|
||||
task = dict(file=dict(path=parameters['WORKSPACE'],
|
||||
state='directory'))
|
||||
main_block.append(task)
|
||||
tasks.append(task)
|
||||
|
||||
msg = [
|
||||
"Launched by %s" % self.manager_name,
|
||||
"Building remotely on %s in workspace %s" % (
|
||||
self.name, parameters['WORKSPACE'])]
|
||||
task = dict(zuul_log=dict(msg=msg))
|
||||
main_block.append(task)
|
||||
tasks.append(task)
|
||||
|
||||
play = dict(hosts='node', name='Job setup', tasks=tasks)
|
||||
pre_playbook.write(
|
||||
yaml.safe_dump([play], default_flow_style=False))
|
||||
|
||||
with open(jobdir.playbook, 'w') as playbook:
|
||||
tasks = []
|
||||
|
||||
for builder in jjb_job.get('builders', []):
|
||||
if 'shell' in builder:
|
||||
main_block.extend(
|
||||
tasks.extend(
|
||||
self._makeBuilderTask(jobdir, builder, parameters))
|
||||
task = dict(zuul_log=dict(msg="Job complete, result: SUCCESS"))
|
||||
main_block.append(task)
|
||||
|
||||
task = dict(zuul_log=dict(msg="Job complete, result: FAILURE"))
|
||||
error_block.append(task)
|
||||
error_block.append(dict(fail=dict(msg='FAILURE')))
|
||||
|
||||
play = dict(hosts='node', name='Job body', vars=variables,
|
||||
pre_tasks=pre_tasks, tasks=tasks)
|
||||
play = dict(hosts='node', name='Job body', tasks=tasks)
|
||||
playbook.write(yaml.safe_dump([play], default_flow_style=False))
|
||||
|
||||
early_publishers, late_publishers = self._transformPublishers(jjb_job)
|
||||
|
@ -1434,6 +1446,14 @@ class NodeWorker(object):
|
|||
# we run the log publisher regardless of whether the rest
|
||||
# of the publishers succeed.
|
||||
tasks = []
|
||||
|
||||
task = dict(zuul_log=dict(msg="Job complete, result: SUCCESS"),
|
||||
when='success|bool')
|
||||
blocks[0].insert(0, task)
|
||||
task = dict(zuul_log=dict(msg="Job complete, result: FAILURE"),
|
||||
when='not success|bool')
|
||||
blocks[0].insert(0, task)
|
||||
|
||||
tasks.append(dict(block=blocks[0],
|
||||
always=blocks[1]))
|
||||
|
||||
|
@ -1470,6 +1490,46 @@ class NodeWorker(object):
|
|||
self.log.warning(msg)
|
||||
self.abortRunningProc(proc)
|
||||
|
||||
def runAnsiblePrePlaybook(self, jobdir):
|
||||
# Set LOGNAME env variable so Ansible log_path log reports
|
||||
# the correct user.
|
||||
env_copy = os.environ.copy()
|
||||
env_copy['LOGNAME'] = 'zuul'
|
||||
|
||||
if self.options['verbose']:
|
||||
verbose = '-vvv'
|
||||
else:
|
||||
verbose = '-v'
|
||||
|
||||
cmd = ['ansible-playbook', jobdir.pre_playbook,
|
||||
'-e@%s' % jobdir.vars, verbose]
|
||||
self.log.debug("Ansible pre command: %s" % (cmd,))
|
||||
|
||||
self.ansible_pre_proc = subprocess.Popen(
|
||||
cmd,
|
||||
cwd=jobdir.ansible_root,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
preexec_fn=os.setsid,
|
||||
env=env_copy,
|
||||
)
|
||||
ret = None
|
||||
watchdog = Watchdog(ANSIBLE_DEFAULT_PRE_TIMEOUT,
|
||||
self._ansibleTimeout,
|
||||
(self.ansible_pre_proc,
|
||||
"Ansible pre timeout exceeded"))
|
||||
watchdog.start()
|
||||
try:
|
||||
for line in iter(self.ansible_pre_proc.stdout.readline, b''):
|
||||
line = line[:1024].rstrip()
|
||||
self.log.debug("Ansible pre output: %s" % (line,))
|
||||
ret = self.ansible_pre_proc.wait()
|
||||
finally:
|
||||
watchdog.stop()
|
||||
self.log.debug("Ansible pre exit code: %s" % (ret,))
|
||||
self.ansible_pre_proc = None
|
||||
return ret == 0
|
||||
|
||||
def runAnsiblePlaybook(self, jobdir, timeout):
|
||||
# Set LOGNAME env variable so Ansible log_path log reports
|
||||
# the correct user.
|
||||
|
@ -1481,7 +1541,8 @@ class NodeWorker(object):
|
|||
else:
|
||||
verbose = '-v'
|
||||
|
||||
cmd = ['ansible-playbook', jobdir.playbook, verbose]
|
||||
cmd = ['ansible-playbook', jobdir.playbook, verbose,
|
||||
'-e@%s' % jobdir.vars]
|
||||
self.log.debug("Ansible command: %s" % (cmd,))
|
||||
|
||||
self.ansible_job_proc = subprocess.Popen(
|
||||
|
@ -1530,7 +1591,9 @@ class NodeWorker(object):
|
|||
verbose = '-v'
|
||||
|
||||
cmd = ['ansible-playbook', jobdir.post_playbook,
|
||||
'-e', 'success=%s' % success, verbose]
|
||||
'-e', 'success=%s' % success,
|
||||
'-e@%s' % jobdir.vars,
|
||||
verbose]
|
||||
self.log.debug("Ansible post command: %s" % (cmd,))
|
||||
|
||||
self.ansible_post_proc = subprocess.Popen(
|
||||
|
|
Loading…
Reference in New Issue