Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: scripts/slave/unittests/annotated_run_test.py

Issue 1501663002: annotated_run.py: Add LogDog bootstrapping. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Comments. Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « scripts/slave/gce.py ('k') | scripts/tools/gzjsondump.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 2
3 # Copyright 2015 The Chromium Authors. All rights reserved. 3 # Copyright 2015 The Chromium Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """Tests that the tools/build annotated_run wrapper actually runs.""" 7 """Tests that the tools/build annotated_run wrapper actually runs."""
8 8
9 import collections
10 import contextlib
9 import json 11 import json
12 import logging
10 import os 13 import os
11 import subprocess 14 import subprocess
15 import sys
16 import tempfile
12 import unittest 17 import unittest
13 18
19 import test_env # pylint: disable=W0403,W0611
20
21 import mock
22 from common import chromium_utils
23 from common import env
24 from slave import annotated_run
25 from slave import gce
26
14 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 27 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
15 28
29
30 MockOptions = collections.namedtuple('MockOptions',
31 ('dry_run', 'logdog_force', 'logdog_butler_path', 'logdog_annotee_path',
32 'logdog_verbose', 'logdog_service_account_json'))
33
34
16 class AnnotatedRunTest(unittest.TestCase): 35 class AnnotatedRunTest(unittest.TestCase):
17 def test_example(self): 36 def test_example(self):
18 build_properties = { 37 build_properties = {
19 'recipe': 'annotated_run_test', 38 'recipe': 'annotated_run_test',
20 'true_prop': True, 39 'true_prop': True,
21 'num_prop': 123, 40 'num_prop': 123,
22 'string_prop': '321', 41 'string_prop': '321',
23 'dict_prop': {'foo': 'bar'}, 42 'dict_prop': {'foo': 'bar'},
24 } 43 }
25 44
26 script_path = os.path.join(BASE_DIR, 'annotated_run.py') 45 script_path = os.path.join(BASE_DIR, 'annotated_run.py')
27 exit_code = subprocess.call([ 46 exit_code = subprocess.call([
28 'python', script_path, 47 'python', script_path,
29 '--build-properties=%s' % json.dumps(build_properties)]) 48 '--build-properties=%s' % json.dumps(build_properties)])
30 self.assertEqual(exit_code, 0) 49 self.assertEqual(exit_code, 0)
31 50
51 @mock.patch('slave.annotated_run._run_command')
52 @mock.patch('slave.annotated_run.main')
53 @mock.patch('sys.platform', return_value='win')
54 @mock.patch('tempfile.mkstemp', side_effect=Exception('failure'))
55 @mock.patch('os.environ', {})
56 def test_update_scripts_must_run(self, _tempfile_mkstemp, _sys_platform,
57 main, run_command):
58 annotated_run.main.side_effect = Exception('Test error!')
59 annotated_run._run_command.return_value = (0, "")
60 annotated_run.shell_main(['annotated_run.py', 'foo'])
61
62 gclient_path = os.path.join(env.Build, os.pardir, 'depot_tools',
63 'gclient.bat')
64 run_command.assert_has_calls([
65 mock.call([gclient_path, 'sync', '--force', '--verbose', '--jobs=2'],
66 cwd=env.Build),
67 mock.call([sys.executable, 'annotated_run.py', 'foo']),
68 ])
69 main.assert_not_called()
70
71
72 class _AnnotatedRunExecTestBase(unittest.TestCase):
73 def setUp(self):
74 logging.basicConfig(level=logging.ERROR+1)
75
76 self.maxDiff = None
77 self._patchers = []
78 map(self._patch, (
79 mock.patch('slave.annotated_run._run_command'),
80 mock.patch('os.path.exists'),
81 mock.patch('os.getcwd'),
82 ))
83
84 self.rt = annotated_run.Runtime()
85 self.tdir = self.rt.tempdir()
86 self.opts = MockOptions(
87 dry_run=False,
88 logdog_force=False,
89 logdog_annotee_path=None,
90 logdog_butler_path=None,
91 logdog_verbose=False,
92 logdog_service_account_json=None)
93 self.config = annotated_run.Config(
94 run_cmd=['run.py'],
95 logdog_pubsub=None,
96 logdog_platform=None,
97 )
98 self.properties = {
99 'recipe': 'example/recipe',
100 'mastername': 'master.random',
101 'buildername': 'builder',
102 }
103 self.cwd = os.path.join('home', 'user')
104 self.rpy_path = os.path.join(env.Build, 'scripts', 'slave', 'recipes.py')
105 self.recipe_args = [
106 sys.executable, '-u', self.rpy_path, 'run',
107 '--workdir=%s' % (self.cwd,),
108 '--properties-file=%s' % (self._tp('recipe_properties.json'),),
109 'example/recipe']
110
111 # Use public recipes.py path.
112 os.getcwd.return_value = self.cwd
113 os.path.exists.return_value = False
114
115 def tearDown(self):
116 self.rt.close()
117 for p in reversed(self._patchers):
118 p.stop()
119
120 def _tp(self, *p):
121 return os.path.join(*((self.tdir,) + p))
122
123 def _patch(self, patcher):
124 self._patchers.append(patcher)
125 patcher.start()
126 return patcher
127
128 def _assertRecipeProperties(self, value):
129 # Double-translate "value", since JSON converts strings to unicode.
130 value = json.loads(json.dumps(value))
131 with open(self._tp('recipe_properties.json')) as fd:
132 self.assertEqual(json.load(fd), value)
133
134
135 class AnnotatedRunExecTest(_AnnotatedRunExecTestBase):
136
137 def test_exec_successful(self):
138 annotated_run._run_command.return_value = (0, '')
139
140 rv = annotated_run._exec_recipe(self.rt, self.opts, self.tdir, self.config,
141 self.properties)
142 self.assertEqual(rv, 0)
143 self._assertRecipeProperties(self.properties)
144
145 annotated_run._run_command.assert_called_once_with(self.recipe_args,
146 dry_run=False)
147
148
149 class AnnotatedRunLogDogExecTest(_AnnotatedRunExecTestBase):
150
151 def setUp(self):
152 super(AnnotatedRunLogDogExecTest, self).setUp()
153 self._orig_whitelist = annotated_run.LOGDOG_WHITELIST_MASTER_BUILDERS
154 annotated_run.LOGDOG_WHITELIST_MASTER_BUILDERS = {
155 'master.some': [
156 'yesbuilder',
157 ],
158
159 'master.all': [
160 annotated_run.WHITELIST_ALL,
161 ],
162 }
163 self.properties.update({
164 'mastername': 'master.some',
165 'buildername': 'nobuilder',
166 'buildnumber': 1337,
167 })
168 self.config = self.config._replace(
169 logdog_pubsub=annotated_run.PubSubConfig(project='test', topic='logs'),
170 logdog_platform=annotated_run.LogDogPlatform(
171 butler=annotated_run.CipdBinary('cipd/butler', 'head', 'butler'),
172 annotee=annotated_run.CipdBinary('cipd/annotee', 'head', 'annotee'),
173 credential_path=os.path.join('path', 'to', 'creds.json'),
174 streamserver='unix',
175 ),
176 )
177 self.is_gce = False
178
179 def is_gce():
180 return self.is_gce
181 is_gce_patch = mock.patch('slave.gce.Authenticator.is_gce',
182 side_effect=is_gce)
183 is_gce_patch.start()
184 self._patchers.append(is_gce_patch)
185
186 def tearDown(self):
187 annotated_run.LOGDOG_WHITELIST_MASTER_BUILDERS = self._orig_whitelist
188 super(AnnotatedRunLogDogExecTest, self).tearDown()
189
190 def _assertAnnoteeCommand(self, value):
191 # Double-translate "value", since JSON converts strings to unicode.
192 value = json.loads(json.dumps(value))
193 with open(self._tp('logdog_bootstrap', 'annotee_cmd.json')) as fd:
194 self.assertEqual(json.load(fd), value)
195
196 def test_should_run_logdog(self):
197 self.assertFalse(annotated_run._should_run_logdog({
198 'mastername': 'master.undefined', 'buildername': 'any'}))
199 self.assertFalse(annotated_run._should_run_logdog({
200 'mastername': 'master.some', 'buildername': 'nobuilder'}))
201 self.assertTrue(annotated_run._should_run_logdog({
202 'mastername': 'master.some', 'buildername': 'yesbuilder'}))
203 self.assertTrue(annotated_run._should_run_logdog({
204 'mastername': 'master.all', 'buildername': 'anybuilder'}))
205
206 @mock.patch('slave.annotated_run._get_service_account_json')
207 def test_exec_with_whitelist_builder_runs_logdog(self, service_account):
208 self.properties['buildername'] = 'yesbuilder'
209
210 butler_path = self._tp('logdog_bootstrap', 'cipd', 'butler')
211 annotee_path = self._tp('logdog_bootstrap', 'cipd', 'annotee')
212 service_account.return_value = 'creds.json'
213 annotated_run._run_command.return_value = (0, '')
214
215 self._patch(mock.patch('tempfile.mkdtemp', return_value='foo'))
216 rv = annotated_run._exec_recipe(self.rt, self.opts, self.tdir, self.config,
217 self.properties)
218 self.assertEqual(rv, 0)
219
220 streamserver_uri = 'unix:%s' % (os.path.join('foo', 'butler.sock'),)
221 service_account.assert_called_once_with(
222 self.opts, self.config.logdog_platform.credential_path)
223 annotated_run._run_command.assert_called_with(
224 [butler_path,
225 '-prefix', 'bb/master.some/yesbuilder/1337',
226 '-output', 'pubsub,project="test",topic="logs"',
227 '-service-account-json', 'creds.json',
228 'run',
229 '-stdout', 'tee=stdout',
230 '-stderr', 'tee=stderr',
231 '-streamserver-uri', streamserver_uri,
232 '--',
233 annotee_path,
234 '-butler-stream-server', streamserver_uri,
235 '-annotate', 'tee',
236 '-name-base', 'recipes',
237 '-print-summary',
238 '-tee',
239 '-json-args-path', self._tp('logdog_bootstrap',
240 'annotee_cmd.json'),
241 ],
242 dry_run=False)
243 self._assertRecipeProperties(self.properties)
244 self._assertAnnoteeCommand(self.recipe_args)
245
246 @mock.patch('slave.annotated_run._logdog_bootstrap', return_value=0)
247 def test_runs_bootstrap_when_forced(self, lb):
248 opts = self.opts._replace(logdog_force=True)
249 rv = annotated_run._exec_recipe(self.rt, opts, self.tdir, self.config,
250 self.properties)
251 self.assertEqual(rv, 0)
252 lb.assert_called_once()
253 annotated_run._run_command.assert_called_once()
254
255 @mock.patch('slave.annotated_run._logdog_bootstrap', return_value=2)
256 def test_forwards_error_code(self, lb):
257 opts = self.opts._replace(
258 logdog_force=True)
259 rv = annotated_run._exec_recipe(self.rt, opts, self.tdir, self.config,
260 self.properties)
261 self.assertEqual(rv, 2)
262 lb.assert_called_once()
263
264 @mock.patch('slave.annotated_run._logdog_bootstrap',
265 side_effect=Exception('Unhandled situation.'))
266 def test_runs_directly_if_bootstrap_fails(self, lb):
267 annotated_run._run_command.return_value = (123, '')
268
269 rv = annotated_run._exec_recipe(self.rt, self.opts, self.tdir, self.config,
270 self.properties)
271 self.assertEqual(rv, 123)
272
273 lb.assert_called_once()
274 annotated_run._run_command.assert_called_once_with(self.recipe_args,
275 dry_run=False)
276
277 @mock.patch('slave.annotated_run._logdog_install_cipd')
278 @mock.patch('slave.annotated_run._get_service_account_json')
279 def test_runs_directly_if_logdog_error(self, service_account, cipd):
280 self.properties['buildername'] = 'yesbuilder'
281
282 cipd.return_value = ('butler', 'annotee')
283 service_account.return_value = 'creds.json'
284 def error_for_logdog(args, **kw):
285 if len(args) > 0 and args[0] == 'butler':
286 return (250, '')
287 return (4, '')
288 annotated_run._run_command.side_effect = error_for_logdog
289
290 self._patch(mock.patch('tempfile.mkdtemp', return_value='foo'))
291 rv = annotated_run._exec_recipe(self.rt, self.opts, self.tdir, self.config,
292 self.properties)
293 self.assertEqual(rv, 4)
294
295 streamserver_uri = 'unix:%s' % (os.path.join('foo', 'butler.sock'),)
296 service_account.assert_called_once_with(
297 self.opts, self.config.logdog_platform.credential_path)
298 annotated_run._run_command.assert_has_calls([
299 mock.call([
300 'butler',
301 '-prefix', 'bb/master.some/yesbuilder/1337',
302 '-output', 'pubsub,project="test",topic="logs"',
303 '-service-account-json', 'creds.json',
304 'run',
305 '-stdout', 'tee=stdout',
306 '-stderr', 'tee=stderr',
307 '-streamserver-uri', streamserver_uri,
308 '--',
309 'annotee',
310 '-butler-stream-server', streamserver_uri,
311 '-annotate', 'tee',
312 '-name-base', 'recipes',
313 '-print-summary',
314 '-tee',
315 '-json-args-path', self._tp('logdog_bootstrap',
316 'annotee_cmd.json'),
317 ], dry_run=False),
318 mock.call(self.recipe_args, dry_run=False),
319 ])
320
321 @mock.patch('os.path.isfile')
322 def test_can_find_credentials(self, isfile):
323 isfile.return_value = True
324
325 service_account_json = annotated_run._get_service_account_json(
326 self.opts, 'creds.json')
327 self.assertEqual(service_account_json, 'creds.json')
328
329 def test_uses_no_credentials_on_gce(self):
330 self.is_gce = True
331 service_account_json = annotated_run._get_service_account_json(
332 self.opts, ('foo', 'bar'))
333 self.assertIsNone(service_account_json)
334
335 def test_cipd_install(self):
336 annotated_run._run_command.return_value = (0, '')
337
338 pkgs = annotated_run._logdog_install_cipd(self.tdir,
339 annotated_run.CipdBinary('infra/foo', 'v0', 'foo'),
340 annotated_run.CipdBinary('infra/bar', 'v1', 'baz'),
341 )
342 self.assertEqual(pkgs, (self._tp('foo'), self._tp('baz')))
343
344 annotated_run._run_command.assert_called_once_with([
345 sys.executable,
346 os.path.join(env.Build, 'scripts', 'slave', 'cipd.py'),
347 '--dest-directory', self.tdir,
348 '--json-output', os.path.join(self.tdir, 'packages.json'),
349 '-P', 'infra/foo@v0',
350 '-P', 'infra/bar@v1',
351 ])
352
353 def test_cipd_install_failure_raises_bootstrap_error(self):
354 annotated_run._run_command.return_value = (1, '')
355
356 self.assertRaises(annotated_run.LogDogBootstrapError,
357 annotated_run._logdog_install_cipd,
358 self.tdir,
359 annotated_run.CipdBinary('infra/foo', 'v0', 'foo'),
360 annotated_run.CipdBinary('infra/bar', 'v1', 'baz'),
361 )
362
363
32 if __name__ == '__main__': 364 if __name__ == '__main__':
33 unittest.main() 365 unittest.main()
OLDNEW
« no previous file with comments | « scripts/slave/gce.py ('k') | scripts/tools/gzjsondump.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698