blob: b5ebe9f4af279f14273dc69b5cbfe1c2c6cf2ea4 [file] [log] [blame]
James E. Blair4f568262017-12-21 09:18:21 -08001#!/usr/bin/env python
2
3# Copyright 2014 Hewlett-Packard Development Company, L.P.
4# Copyright 2014 Rackspace Australia
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18import asyncio
19import threading
20import os
21import json
22import urllib
23import time
24import socket
25from unittest import skip
26
27import webob
28
29import zuul.web
30
31from tests.base import ZuulTestCase, FIXTURE_DIR
32
33
34class TestWeb(ZuulTestCase):
35 tenant_config_file = 'config/single-tenant/main.yaml'
36
37 def setUp(self):
38 super(TestWeb, self).setUp()
39 self.executor_server.hold_jobs_in_build = True
40 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
41 A.addApproval('Code-Review', 2)
42 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
43 B = self.fake_gerrit.addFakeChange('org/project1', 'master', 'B')
44 B.addApproval('Code-Review', 2)
45 self.fake_gerrit.addEvent(B.addApproval('Approved', 1))
46 self.waitUntilSettled()
47
48 # Start the web server
49 self.web = zuul.web.ZuulWeb(
50 listen_address='127.0.0.1', listen_port=0,
51 gear_server='127.0.0.1', gear_port=self.gearman_server.port)
52 loop = asyncio.new_event_loop()
53 loop.set_debug(True)
54 ws_thread = threading.Thread(target=self.web.run, args=(loop,))
55 ws_thread.start()
56 self.addCleanup(loop.close)
57 self.addCleanup(ws_thread.join)
58 self.addCleanup(self.web.stop)
59
60 self.host = 'localhost'
61 # Wait until web server is started
62 while True:
63 time.sleep(0.1)
64 if self.web.server is None:
65 continue
66 self.port = self.web.server.sockets[0].getsockname()[1]
67 print(self.host, self.port)
68 try:
69 with socket.create_connection((self.host, self.port)):
70 break
71 except ConnectionRefusedError:
72 pass
73
74 def tearDown(self):
75 self.executor_server.hold_jobs_in_build = False
76 self.executor_server.release()
77 self.waitUntilSettled()
78 super(TestWeb, self).tearDown()
79
80 def test_web_status(self):
Tobias Henkele0bad8d2018-01-23 12:34:15 +010081 "Test that we can retrieve JSON status info"
82 self.executor_server.hold_jobs_in_build = True
83 A = self.fake_gerrit.addFakeChange('org/project', 'master', 'A')
84 A.addApproval('Code-Review', 2)
85 self.fake_gerrit.addEvent(A.addApproval('Approved', 1))
86 self.waitUntilSettled()
87
88 self.executor_server.release('project-merge')
89 self.waitUntilSettled()
James E. Blair4f568262017-12-21 09:18:21 -080090
91 req = urllib.request.Request(
92 "http://localhost:%s/tenant-one/status.json" % self.port)
93 f = urllib.request.urlopen(req)
Tobias Henkele0bad8d2018-01-23 12:34:15 +010094 headers = f.info()
95 self.assertIn('Content-Length', headers)
96 self.assertIn('Content-Type', headers)
97 self.assertEqual(
98 'application/json; charset=utf-8', headers['Content-Type'])
99 self.assertIn('Access-Control-Allow-Origin', headers)
100 self.assertIn('Cache-Control', headers)
101 self.assertIn('Last-Modified', headers)
102 data = f.read().decode('utf8')
James E. Blair4f568262017-12-21 09:18:21 -0800103
Tobias Henkele0bad8d2018-01-23 12:34:15 +0100104 self.executor_server.hold_jobs_in_build = False
105 self.executor_server.release()
106 self.waitUntilSettled()
107
108 data = json.loads(data)
109 status_jobs = []
110 for p in data['pipelines']:
111 for q in p['change_queues']:
112 if p['name'] in ['gate', 'conflict']:
113 self.assertEqual(q['window'], 20)
114 else:
115 self.assertEqual(q['window'], 0)
116 for head in q['heads']:
117 for change in head:
118 self.assertTrue(change['active'])
119 self.assertIn(change['id'], ('1,1', '2,1', '3,1'))
120 for job in change['jobs']:
121 status_jobs.append(job)
122 self.assertEqual('project-merge', status_jobs[0]['name'])
123 # TODO(mordred) pull uuids from self.builds
124 self.assertEqual(
125 'stream.html?uuid={uuid}&logfile=console.log'.format(
126 uuid=status_jobs[0]['uuid']),
127 status_jobs[0]['url'])
128 self.assertEqual(
129 'finger://{hostname}/{uuid}'.format(
130 hostname=self.executor_server.hostname,
131 uuid=status_jobs[0]['uuid']),
132 status_jobs[0]['finger_url'])
133 # TOOD(mordred) configure a success-url on the base job
134 self.assertEqual(
135 'finger://{hostname}/{uuid}'.format(
136 hostname=self.executor_server.hostname,
137 uuid=status_jobs[0]['uuid']),
138 status_jobs[0]['report_url'])
139 self.assertEqual('project-test1', status_jobs[1]['name'])
140 self.assertEqual(
141 'stream.html?uuid={uuid}&logfile=console.log'.format(
142 uuid=status_jobs[1]['uuid']),
143 status_jobs[1]['url'])
144 self.assertEqual(
145 'finger://{hostname}/{uuid}'.format(
146 hostname=self.executor_server.hostname,
147 uuid=status_jobs[1]['uuid']),
148 status_jobs[1]['finger_url'])
149 self.assertEqual(
150 'finger://{hostname}/{uuid}'.format(
151 hostname=self.executor_server.hostname,
152 uuid=status_jobs[1]['uuid']),
153 status_jobs[1]['report_url'])
154
155 self.assertEqual('project-test2', status_jobs[2]['name'])
156 self.assertEqual(
157 'stream.html?uuid={uuid}&logfile=console.log'.format(
158 uuid=status_jobs[2]['uuid']),
159 status_jobs[2]['url'])
160 self.assertEqual(
161 'finger://{hostname}/{uuid}'.format(
162 hostname=self.executor_server.hostname,
163 uuid=status_jobs[2]['uuid']),
164 status_jobs[2]['finger_url'])
165 self.assertEqual(
166 'finger://{hostname}/{uuid}'.format(
167 hostname=self.executor_server.hostname,
168 uuid=status_jobs[2]['uuid']),
169 status_jobs[2]['report_url'])
170
171 # check job dependencies
172 self.assertIsNotNone(status_jobs[0]['dependencies'])
173 self.assertIsNotNone(status_jobs[1]['dependencies'])
174 self.assertIsNotNone(status_jobs[2]['dependencies'])
175 self.assertEqual(len(status_jobs[0]['dependencies']), 0)
176 self.assertEqual(len(status_jobs[1]['dependencies']), 1)
177 self.assertEqual(len(status_jobs[2]['dependencies']), 1)
178 self.assertIn('project-merge', status_jobs[1]['dependencies'])
179 self.assertIn('project-merge', status_jobs[2]['dependencies'])
James E. Blair4f568262017-12-21 09:18:21 -0800180
181 def test_web_bad_url(self):
182 # do we 404 correctly
183 req = urllib.request.Request(
184 "http://localhost:%s/status/foo" % self.port)
185 self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, req)
186
187 @skip("This is not supported by zuul-web")
188 def test_web_find_change(self):
189 # can we filter by change id
190 req = urllib.request.Request(
191 "http://localhost:%s/tenant-one/status/change/1,1" % self.port)
192 f = urllib.request.urlopen(req)
193 data = json.loads(f.read().decode('utf8'))
194
195 self.assertEqual(1, len(data), data)
196 self.assertEqual("org/project", data[0]['project'])
197
198 req = urllib.request.Request(
199 "http://localhost:%s/tenant-one/status/change/2,1" % self.port)
200 f = urllib.request.urlopen(req)
201 data = json.loads(f.read().decode('utf8'))
202
203 self.assertEqual(1, len(data), data)
204 self.assertEqual("org/project1", data[0]['project'], data)
205
206 def test_web_keys(self):
207 with open(os.path.join(FIXTURE_DIR, 'public.pem'), 'rb') as f:
208 public_pem = f.read()
209
210 req = urllib.request.Request(
211 "http://localhost:%s/tenant-one/org/project.pub" %
212 self.port)
213 f = urllib.request.urlopen(req)
214 self.assertEqual(f.read(), public_pem)
215
216 @skip("This may not apply to zuul-web")
217 def test_web_custom_handler(self):
218 def custom_handler(path, tenant_name, request):
219 return webob.Response(body='ok')
220
221 self.webapp.register_path('/custom', custom_handler)
222 req = urllib.request.Request(
223 "http://localhost:%s/custom" % self.port)
224 f = urllib.request.urlopen(req)
225 self.assertEqual(b'ok', f.read())
226
227 self.webapp.unregister_path('/custom')
228 self.assertRaises(urllib.error.HTTPError, urllib.request.urlopen, req)
229
230 @skip("This returns a 500")
231 def test_web_404_on_unknown_tenant(self):
232 req = urllib.request.Request(
233 "http://localhost:{}/non-tenant/status.json".format(self.port))
234 e = self.assertRaises(
235 urllib.error.HTTPError, urllib.request.urlopen, req)
236 self.assertEqual(404, e.code)