Add configurable footer-message reports

Currently there is no way to add extra information to the end of a
report sent back by zuul. This adds a 'footer-message' field to the
pipelines allowing operators to leave extra information at the end
of a build result.

This is useful for leaving CI contact and debugging information.

Change-Id: Ieae62e845915fa3997353b6b425e215966a9338c
diff --git a/tests/fixtures/layout-footer-message.yaml b/tests/fixtures/layout-footer-message.yaml
new file mode 100644
index 0000000..7977c19
--- /dev/null
+++ b/tests/fixtures/layout-footer-message.yaml
@@ -0,0 +1,34 @@
+includes:
+  - python-file: custom_functions.py
+
+pipelines:
+  - name: gate
+    manager: DependentPipelineManager
+    failure-message: Build failed.  For information on how to proceed, see http://wiki.example.org/Test_Failures
+    footer-message: For CI problems and help debugging, contact ci@example.org
+    trigger:
+      gerrit:
+        - event: comment-added
+          approval:
+            - approved: 1
+    success:
+      gerrit:
+        verified: 2
+        submit: true
+      smtp:
+        to: success@example.org
+    failure:
+      gerrit:
+        verified: -2
+      smtp:
+        to: failure@example.org
+    start:
+      gerrit:
+        verified: 0
+    precedence: high
+
+projects:
+  - name: org/project
+    gate:
+      - test1
+      - test2
diff --git a/tests/test_scheduler.py b/tests/test_scheduler.py
index 9576440..f3d0e15 100755
--- a/tests/test_scheduler.py
+++ b/tests/test_scheduler.py
@@ -3640,3 +3640,43 @@
         self.worker.hold_jobs_in_build = False
         self.worker.release()
         self.waitUntilSettled()
+
+    def test_footer_message(self):
+        "Test a pipeline's footer message is correctly added to the report."
+        self.config.set('zuul', 'layout_config',
+                        'tests/fixtures/layout-footer-message.yaml')
+        self.sched.reconfigure(self.config)
+        self.registerJobs()
+
+        A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
+        A.addApproval('CRVW', 2)
+        self.worker.addFailTest('test1', A)
+        self.fake_gerrit.addEvent(A.addApproval('APRV', 1))
+        self.waitUntilSettled()
+
+        B = self.fake_gerrit.addFakeChange('org/project', 'master', 'B')
+        B.addApproval('CRVW', 2)
+        self.fake_gerrit.addEvent(B.addApproval('APRV', 1))
+        self.waitUntilSettled()
+
+        self.assertEqual(2, len(self.smtp_messages))
+
+        failure_body = """\
+Build failed.  For information on how to proceed, see \
+http://wiki.example.org/Test_Failures
+
+- test1 http://logs.example.com/1/1/gate/test1/0 : FAILURE in 0s
+- test2 http://logs.example.com/1/1/gate/test2/1 : SUCCESS in 0s
+
+For CI problems and help debugging, contact ci@example.org"""
+
+        success_body = """\
+Build succeeded.
+
+- test1 http://logs.example.com/2/1/gate/test1/2 : SUCCESS in 0s
+- test2 http://logs.example.com/2/1/gate/test2/3 : SUCCESS in 0s
+
+For CI problems and help debugging, contact ci@example.org"""
+
+        self.assertEqual(failure_body, self.smtp_messages[0]['body'])
+        self.assertEqual(success_body, self.smtp_messages[1]['body'])