Merge "Write a logging config file and pass it to callbacks" into feature/zuulv3
diff --git a/doc/source/admin/components.rst b/doc/source/admin/components.rst
index 2c70d47..fbb8cbc 100644
--- a/doc/source/admin/components.rst
+++ b/doc/source/admin/components.rst
@@ -149,6 +149,11 @@
 
       IP address or domain name on which to listen.
 
+   .. attr:: port
+      :default: 4730
+
+      TCP port on which to listen.
+
    .. attr:: log_config
 
       Path to log config file for internal Gearman server.
diff --git a/doc/source/user/config.rst b/doc/source/user/config.rst
index cad2167..973470d 100644
--- a/doc/source/user/config.rst
+++ b/doc/source/user/config.rst
@@ -755,7 +755,7 @@
 
    .. attr:: timeout
 
-      The time in minutes that the job should be allowed to run before
+      The time in seconds that the job should be allowed to run before
       it is automatically aborted and failure is reported.  If no
       timeout is supplied, the job may run indefinitely.  Supplying a
       timeout is highly recommended.
diff --git a/doc/source/user/jobs.rst b/doc/source/user/jobs.rst
index 4e1880a..4c8b784 100644
--- a/doc/source/user/jobs.rst
+++ b/doc/source/user/jobs.rst
@@ -207,10 +207,42 @@
 
       .. var:: src_dir
 
-         The path to the source code on the remote host, relative
-         to the home dir of the remote user.
-         E.g., `src/git.example.com/org/project`.
+         The path to the source code relative to the work dir.  E.g.,
+         `src/git.example.com/org/project`.
 
+   .. var:: projects
+      :type: list
+
+      A list of all projects prepared by Zuul for the item.  It
+      includes, at least, the item's own project.  It also includes
+      the projects of any items this item depends on, as well as the
+      projects that appear in :attr:`job.required-projects`.
+
+      This is a list of dictionaries, with each element consisting of:
+
+      .. var:: name
+
+         The name of the project, excluding hostname.  E.g., `org/project`.
+
+      .. var:: short_name
+
+         The name of the project, excluding directories or
+         organizations.  E.g., `project`.
+
+      .. var:: canonical_hostname
+
+         The canonical hostname where the project lives.  E.g.,
+         `git.example.com`.
+
+      .. var:: canonical_name
+
+         The full canonical name of the project including hostname.
+         E.g., `git.example.com/org/project`.
+
+      .. var:: src_dir
+
+         The path to the source code, relative to the work dir.  E.g.,
+         `src/git.example.com/org/project`.
 
    .. var:: tenant
 
diff --git a/etc/zuul.conf-sample b/etc/zuul.conf-sample
index 6e79f9b..ba7aace 100644
--- a/etc/zuul.conf-sample
+++ b/etc/zuul.conf-sample
@@ -1,5 +1,6 @@
 [gearman]
 server=127.0.0.1
+;port=4730
 ;ssl_ca=/path/to/ca.pem
 ;ssl_cert=/path/to/client.pem
 ;ssl_key=/path/to/client.key
@@ -12,6 +13,7 @@
 ;ssl_ca=/path/to/ca.pem
 ;ssl_cert=/path/to/server.pem
 ;ssl_key=/path/to/server.key
+;port=4730
 
 [scheduler]
 tenant_config=/etc/zuul/main.yaml
diff --git a/zuul/cmd/scheduler.py b/zuul/cmd/scheduler.py
index b7b12fe..a9923c6 100755
--- a/zuul/cmd/scheduler.py
+++ b/zuul/cmd/scheduler.py
@@ -100,10 +100,12 @@
             statsd_host = os.environ.get('STATSD_HOST')
             statsd_port = int(os.environ.get('STATSD_PORT', 8125))
             host = get_default(self.config, 'gearman_server', 'listen_address')
+            port = int(get_default(self.config, 'gearman_server', 'port',
+                                   4730))
             ssl_key = get_default(self.config, 'gearman_server', 'ssl_key')
             ssl_cert = get_default(self.config, 'gearman_server', 'ssl_cert')
             ssl_ca = get_default(self.config, 'gearman_server', 'ssl_ca')
-            zuul.lib.gearserver.GearServer(4730,
+            zuul.lib.gearserver.GearServer(port,
                                            ssl_key=ssl_key,
                                            ssl_cert=ssl_cert,
                                            ssl_ca=ssl_ca,
diff --git a/zuul/driver/github/githubconnection.py b/zuul/driver/github/githubconnection.py
index f31df6a..bac66f1 100644
--- a/zuul/driver/github/githubconnection.py
+++ b/zuul/driver/github/githubconnection.py
@@ -75,7 +75,9 @@
             raise webob.exc.HTTPMethodNotAllowed(
                 'Only POST method is allowed.')
 
-        self.log.debug("Github Webhook Received.")
+        delivery = request.headers.get('X-GitHub-Delivery')
+        self.log.debug("Github Webhook Received: {delivery}".format(
+            delivery=delivery))
 
         self._validate_signature(request)
 
diff --git a/zuul/executor/client.py b/zuul/executor/client.py
index 40ad860..fcb6bb2 100644
--- a/zuul/executor/client.py
+++ b/zuul/executor/client.py
@@ -182,6 +182,7 @@
         if (hasattr(item.change, 'newrev') and item.change.newrev
             and item.change.newrev != '0' * 40):
             zuul_params['newrev'] = item.change.newrev
+        zuul_params['projects'] = []  # Set below
         zuul_params['items'] = []
         for i in all_items:
             d = dict()
@@ -200,7 +201,6 @@
                 d['branch'] = i.change.branch
             zuul_params['items'].append(d)
 
-        # Legacy environment variables
         params = dict()
         params['job'] = job.name
         params['timeout'] = job.timeout
@@ -221,16 +221,9 @@
         nodeset = item.current_build_set.getJobNodeSet(job.name)
         nodes = []
         for node in nodeset.getNodes():
-            nodes.append(dict(name=node.name, label=node.label,
-                              az=node.az,
-                              cloud=node.cloud,
-                              host_keys=node.host_keys,
-                              provider=node.provider,
-                              region=node.region,
-                              ssh_port=node.ssh_port,
-                              interface_ip=node.interface_ip,
-                              public_ipv6=node.public_ipv6,
-                              public_ipv4=node.public_ipv4))
+            n = node.toDict()
+            n.update(dict(name=node.name, label=node.label))
+            nodes.append(n)
         params['nodes'] = nodes
         params['groups'] = [group.toDict() for group in nodeset.getGroups()]
         params['vars'] = copy.deepcopy(job.variables)
@@ -268,6 +261,15 @@
                 params['projects'].append(make_project_dict(project))
                 projects.add(project)
 
+        for p in projects:
+            zuul_params['projects'].append(dict(
+                name=p.name,
+                short_name=p.name.split('/')[-1],
+                canonical_hostname=p.canonical_hostname,
+                canonical_name=p.canonical_name,
+                src_dir=os.path.join('src', p.canonical_name),
+            ))
+
         build = Build(job, uuid)
         build.parameters = params
 
diff --git a/zuul/executor/server.py b/zuul/executor/server.py
index 0c415fd..96c809c 100644
--- a/zuul/executor/server.py
+++ b/zuul/executor/server.py
@@ -1101,6 +1101,7 @@
         result = None
 
         pre_failed = False
+        success = False
         for index, playbook in enumerate(self.jobdir.pre_playbooks):
             # TODOv3(pabelanger): Implement pre-run timeout setting.
             pre_status, pre_code = self.runAnsiblePlaybook(
@@ -1109,26 +1110,28 @@
                 # These should really never fail, so return None and have
                 # zuul try again
                 pre_failed = True
-                success = False
                 break
 
         if not pre_failed:
             job_status, job_code = self.runAnsiblePlaybook(
                 self.jobdir.playbook, args['timeout'], phase='run')
-            if job_status == self.RESULT_TIMED_OUT:
-                return 'TIMED_OUT'
             if job_status == self.RESULT_ABORTED:
                 return 'ABORTED'
-            if job_status != self.RESULT_NORMAL:
+            elif job_status == self.RESULT_TIMED_OUT:
+                # Set the pre-failure flag so this doesn't get
+                # overridden by a post-failure.
+                pre_failed = True
+                result = 'TIMED_OUT'
+            elif job_status == self.RESULT_NORMAL:
+                success = (job_code == 0)
+                if success:
+                    result = 'SUCCESS'
+                else:
+                    result = 'FAILURE'
+            else:
                 # The result of the job is indeterminate.  Zuul will
                 # run it again.
-                return result
-
-            success = (job_code == 0)
-            if success:
-                result = 'SUCCESS'
-            else:
-                result = 'FAILURE'
+                return None
 
         for index, playbook in enumerate(self.jobdir.post_playbooks):
             # TODOv3(pabelanger): Implement post-run timeout setting.