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

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

Powered by Google App Engine
This is Rietveld 408576698