diff --git a/requirements.txt b/requirements.txt index 6318a593ee..f626f4c98a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,3 +16,4 @@ apscheduler>=2.1.1,<3.0 PrettyTable>=0.6,<0.8 babel>=1.0 six>=1.6.0 +ansible>=2.0.0.1 diff --git a/tests/base.py b/tests/base.py index 90d2df5802..13f85cc020 100755 --- a/tests/base.py +++ b/tests/base.py @@ -48,8 +48,8 @@ import zuul.connection.smtp import zuul.scheduler import zuul.webapp import zuul.rpclistener -import zuul.launcher.ansible -import zuul.launcher.gearman +import zuul.launcher.ansiblelaunchserver +import zuul.launcher.launchclient import zuul.lib.swift import zuul.lib.connections import zuul.merger.client @@ -636,7 +636,7 @@ class FakeBuild(threading.Thread): self.worker.lock.release() -class RecordingLaunchServer(zuul.launcher.ansible.LaunchServer): +class RecordingLaunchServer(zuul.launcher.ansiblelaunchserver.LaunchServer): def __init__(self, *args, **kw): super(RecordingLaunchServer, self).__init__(*args, **kw) self.job_history = [] @@ -977,8 +977,8 @@ class ZuulTestCase(BaseTestCase): self.connections) self.merge_server.start() - self.launcher = zuul.launcher.gearman.Gearman(self.config, self.sched, - self.swift) + self.launcher = zuul.launcher.launchclient.LaunchClient( + self.config, self.sched, self.swift) self.merge_client = zuul.merger.client.MergeClient( self.config, self.sched) @@ -1280,7 +1280,8 @@ class ZuulTestCase(BaseTestCase): return False if server_job.waiting: continue - worker_job = self.worker.gearman_jobs.get(server_job.unique) + worker_job = self.ansible_server.worker.gearman_jobs.get( + server_job.unique) if worker_job: if build.number is None: self.log.debug("%s has not reported start" % worker_job) diff --git a/zuul/launcher/ansible.py b/zuul/launcher/ansiblelaunchserver.py similarity index 76% rename from zuul/launcher/ansible.py rename to zuul/launcher/ansiblelaunchserver.py index 1604f9773c..704d620a2f 100644 --- a/zuul/launcher/ansible.py +++ b/zuul/launcher/ansiblelaunchserver.py @@ -15,25 +15,35 @@ import collections import json import logging +import os import shutil +import subprocess import tempfile import threading import traceback import gear +import yaml import zuul.merger -class TempDir(object): +class JobDir(object): def __init__(self): - self.tmpdir = tempfile.mkdtemp() + self.root = tempfile.mkdtemp() + self.git_root = os.path.join(self.root, 'git') + os.makedirs(self.git_root) + self.ansible_root = os.path.join(self.root, 'ansible') + os.makedirs(self.ansible_root) + self.inventory = os.path.join(self.ansible_root, 'inventory') + self.playbook = os.path.join(self.ansible_root, 'playbook') + self.config = os.path.join(self.ansible_root, 'ansible.cfg') def __enter__(self): - return self.tmpdir + return self def __exit__(self, etype, value, tb): - shutil.rmtree(self.tmpdir) + shutil.rmtree(self.root) class UpdateTask(object): @@ -207,8 +217,9 @@ class LaunchServer(object): def _launch(self, job): self.log.debug("Job %s: beginning" % (job.unique,)) - with TempDir() as root: - self.log.debug("Job %s: git root at %s" % (job.unique, root)) + with JobDir() as jobdir: + self.log.debug("Job %s: job root at %s" % + (job.unique, jobdir.root)) args = json.loads(job.arguments) tasks = [] for project in args['projects']: @@ -218,9 +229,12 @@ class LaunchServer(object): for task in tasks: task.wait() self.log.debug("Job %s: git updates complete" % (job.unique,)) - merger = self._getMerger(root) + merger = self._getMerger(jobdir.git_root) commit = merger.mergeChanges(args['items']) # noqa + # TODOv3: Ansible the ansible thing here. + self.prepareAnsibleFiles(jobdir, args) + result = self.runAnsible(jobdir) data = { 'url': 'https://server/job', @@ -229,9 +243,47 @@ class LaunchServer(object): job.sendWorkData(json.dumps(data)) job.sendWorkStatus(0, 100) - result = dict(result='SUCCESS') + result = dict(result=result) job.sendWorkComplete(json.dumps(result)) + def getHostList(self, args): + # TODOv3: This should get the appropriate nodes from nodepool, + # or in the unit tests, be overriden to return localhost. + return [('localhost', dict(ansible_connection='local'))] + + def prepareAnsibleFiles(self, jobdir, args): + with open(jobdir.inventory, 'w') as inventory: + for host_name, host_vars in self.getHostList(args): + inventory.write(host_name) + inventory.write(' ') + for k, v in host_vars.items(): + inventory.write('%s=%s' % (k, v)) + inventory.write('\n') + with open(jobdir.playbook, 'w') as playbook: + play = dict(hosts='localhost', + tasks=[dict(name='test', + shell='echo Hello world')]) + playbook.write(yaml.dump([play])) + with open(jobdir.config, 'w') as config: + config.write('[defaults]\n') + config.write('hostfile = %s\n' % jobdir.inventory) + + def runAnsible(self, jobdir): + proc = subprocess.Popen( + ['ansible-playbook', jobdir.playbook], + cwd=jobdir.ansible_root, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + (out, err) = proc.communicate() + ret = proc.wait() + print out + print err + if ret == 0: + return 'SUCCESS' + else: + return 'FAILURE' + def cat(self, job): args = json.loads(job.arguments) task = self.update(args['project'], args['url']) diff --git a/zuul/launcher/gearman.py b/zuul/launcher/launchclient.py similarity index 99% rename from zuul/launcher/gearman.py rename to zuul/launcher/launchclient.py index eaad6b946e..cc39797369 100644 --- a/zuul/launcher/gearman.py +++ b/zuul/launcher/launchclient.py @@ -177,8 +177,8 @@ class ZuulGearmanClient(gear.Client): self.log.info("Done waiting for Gearman server") -class Gearman(object): - log = logging.getLogger("zuul.Gearman") +class LaunchClient(object): + log = logging.getLogger("zuul.LaunchClient") negative_function_cache_ttl = 5 def __init__(self, config, sched, swift):