blob: e83095187a58dae24ef3e69d98c4d4792434a4e6 [file] [log] [blame]
Don Garrettc4114cc2016-11-01 20:04:06 -07001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Bootstrap for cbuildbot.
6
7This script is intended to checkout chromite on the branch specified by -b or
8--branch (as normally accepted by cbuildbot), and then invoke cbuildbot. Most
9arguments are not parsed, only passed along. If a branch is not specified, this
Julio Hurtado9265c7e2021-04-19 23:04:44 +000010script will use 'main'.
Don Garrettc4114cc2016-11-01 20:04:06 -070011
12Among other things, this allows us to invoke build configs that exist on a given
13branch, but not on TOT.
14"""
15
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060016import base64
Don Garrett125d4dc2017-04-25 16:26:03 -070017import functools
Don Garrettc4114cc2016-11-01 20:04:06 -070018import os
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000019import time
Don Garrettc4114cc2016-11-01 20:04:06 -070020
21from chromite.cbuildbot import repository
Don Garrett597ddff2017-02-17 18:29:37 -080022from chromite.cbuildbot.stages import sync_stages
Lann Martinebae73d2018-05-21 17:12:00 -060023from chromite.lib import boto_compat
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060024from chromite.lib import build_summary
Don Garrett86881cb2017-02-15 15:41:55 -080025from chromite.lib import config_lib
Don Garretta50bf492017-09-28 18:33:02 -070026from chromite.lib import constants
Don Garrettc4114cc2016-11-01 20:04:06 -070027from chromite.lib import cros_build_lib
28from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060029from chromite.lib import cros_sdk_lib
Don Garrettacbb2392017-05-11 18:27:41 -070030from chromite.lib import metrics
Don Garrettc4114cc2016-11-01 20:04:06 -070031from chromite.lib import osutils
Mike Frysingerf0146252019-09-02 13:31:05 -040032from chromite.lib import timeout_util
Don Garrettacbb2392017-05-11 18:27:41 -070033from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080034from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070035
Mike Frysinger3c831a72020-02-19 02:47:04 -050036
Don Garrett60967922017-04-12 18:51:44 -070037# This number should be incremented when we change the layout of the buildroot
38# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070039BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070040_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070041
Don Garrettacbb2392017-05-11 18:27:41 -070042# Metrics reported to Monarch.
Mike Nicholscb29fbd2018-07-24 11:09:33 -060043METRIC_PREFIX = 'chromeos/chromite/cbuildbot_launch/'
44METRIC_ACTIVE = METRIC_PREFIX + 'active'
45METRIC_INVOKED = METRIC_PREFIX + 'invoked'
46METRIC_COMPLETED = METRIC_PREFIX + 'completed'
47METRIC_PREP = METRIC_PREFIX + 'prep_completed'
48METRIC_CLEAN = METRIC_PREFIX + 'clean_buildroot_durations'
49METRIC_INITIAL = METRIC_PREFIX + 'initial_checkout_durations'
50METRIC_CBUILDBOT = METRIC_PREFIX + 'cbuildbot_durations'
51METRIC_CBUILDBOT_INSTANCE = METRIC_PREFIX + 'cbuildbot_instance_durations'
52METRIC_CLOBBER = METRIC_PREFIX + 'clobber'
53METRIC_BRANCH_CLEANUP = METRIC_PREFIX + 'branch_cleanup'
54METRIC_DISTFILES_CLEANUP = METRIC_PREFIX + 'distfiles_cleanup'
55METRIC_CHROOT_CLEANUP = METRIC_PREFIX + 'chroot_cleanup'
Don Garrettacbb2392017-05-11 18:27:41 -070056
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060057# Builder state
58BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
59
Don Garrett60967922017-04-12 18:51:44 -070060
Don Garrett125d4dc2017-04-25 16:26:03 -070061def StageDecorator(functor):
62 """A Decorator that adds buildbot stage tags around a method.
63
Don Garrettacbb2392017-05-11 18:27:41 -070064 It uses the method name as the stage name, and assumes failure on a true
65 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070066 """
67 @functools.wraps(functor)
68 def wrapped_functor(*args, **kwargs):
69 try:
70 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070071 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070072 except Exception:
73 logging.PrintBuildbotStepFailure()
74 raise
75
Don Garrettacbb2392017-05-11 18:27:41 -070076 if result:
77 logging.PrintBuildbotStepFailure()
78 return result
79
Don Garrett125d4dc2017-04-25 16:26:03 -070080 return wrapped_functor
81
82
Don Garrettb5fc08b2017-11-20 21:51:16 +000083def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070084 """Helper for inserting more fields into a metrics fields dictionary.
85
86 Args:
87 fields: Dictionary of metrics fields.
88 kwargs: Each argument is a key/value pair to insert into dict.
89
90 Returns:
91 Copy of original dictionary with kwargs set as fields.
92 """
93 f = fields.copy()
94 f.update(kwargs)
95 return f
96
Don Garretta50bf492017-09-28 18:33:02 -070097
98def PrependPath(prepend):
99 """Generate path with new directory at the beginning.
100
101 Args:
102 prepend: Directory to add at the beginning of the path.
103
104 Returns:
105 Extended path as a string.
106 """
107 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
108
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000109
Don Garrett86881cb2017-02-15 15:41:55 -0800110def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700111 """Extract the branch name from cbuildbot command line arguments.
112
Don Garrettc4114cc2016-11-01 20:04:06 -0700113 Args:
114 argv: The command line arguments to parse.
115
116 Returns:
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000117 Branch as a string ('main' if nothing is specified).
Don Garrettc4114cc2016-11-01 20:04:06 -0700118 """
Don Garrett86881cb2017-02-15 15:41:55 -0800119 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400120 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800121
122 if not options.cache_dir:
123 options.cache_dir = os.path.join(options.buildroot,
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000124 'repository', '.cache')
Don Garrett61ce1ee2019-02-26 16:20:25 -0800125
Don Garrettd1d90dd2017-06-13 17:35:52 -0700126 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800127
128 # This option isn't required for cbuildbot, but is for us.
129 if not options.buildroot:
130 cros_build_lib.Die('--buildroot is a required option.')
131
132 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700133
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000134
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600135def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600136 """Extract information about the current build state from command-line args.
137
138 Args:
139 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600140 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600141
142 Returns:
143 A BuildSummary object describing the current build.
144 """
145 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600146 status=constants.BUILDER_STATUS_INFLIGHT,
147 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
148 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600149 if options.buildnumber:
150 build_state.build_number = options.buildnumber
151 if options.buildbucket_id:
152 build_state.buildbucket_id = options.buildbucket_id
153 if options.master_build_id:
154 build_state.master_build_id = options.master_build_id
155 return build_state
156
157
158def GetLastBuildState(root):
159 """Fetch the state of the last build run from |root|.
160
161 If the saved state file can't be read or doesn't contain valid JSON, a default
162 state will be returned.
163
164 Args:
165 root: Root of the working directory tree as a string.
166
167 Returns:
168 A BuildSummary object representing the previous build.
169 """
170 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
171
172 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600173
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600174 try:
175 state_raw = osutils.ReadFile(state_file)
176 state.from_json(state_raw)
177 except IOError as e:
178 logging.warning('Unable to read %s: %s', state_file, e)
179 return state
180 except ValueError as e:
181 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
182 return state
183
184 if not state.is_valid():
185 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600186 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600187
188 return state
189
190
191def SetLastBuildState(root, new_state):
192 """Save the state of the last build under |root|.
193
194 Args:
195 root: Root of the working directory tree as a string.
196 new_state: BuildSummary object containing the state to be saved.
197 """
198 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
199 osutils.WriteFile(state_file, new_state.to_json())
200
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600201 # Remove old state file. Its contents have been migrated into the new file.
202 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
203 osutils.SafeUnlink(old_state_file)
204
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600205
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000206def _MaybeCleanDistfiles(cache_dir, distfiles_ts):
207 """Cleans the distfiles directory if too old.
208
209 Args:
210 cache_dir: Directory of the cache, as a string.
211 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
212 be None.
213
214 Returns:
215 The new distfiles_ts to persist in state.
216 """
217 # distfiles_ts can be None for a fresh environment, which means clean.
218 if distfiles_ts is None:
219 return time.time()
220
221 distfiles_age = (time.time() - distfiles_ts) / 3600.0
222 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
223 return distfiles_ts
224
225 logging.info('Remove old distfiles cache (cache expiry %d hours)',
226 _DISTFILES_CACHE_EXPIRY_HOURS)
227 osutils.RmDir(os.path.join(cache_dir, 'distfiles'),
228 ignore_missing=True, sudo=True)
229 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
230 fields=field({}, reason='cache_expired'))
231
232 # Cleaned cache, so reset distfiles_ts
233 return time.time()
234
235
236def SanitizeCacheDir(cache_dir):
237 """Make certain the .cache directory is valid.
238
239 Args:
240 cache_dir: Directory of the cache, as a string.
241 """
242 logging.info('Cleaning up cache dir at %s', cache_dir)
243 # Verify that .cache is writable by the current user.
244 try:
245 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
246 except IOError:
247 logging.info('Bad Permissions for cache dir, wiping: %s', cache_dir)
248 osutils.RmDir(cache_dir, sudo=True)
249 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
250
251 osutils.RmDir(os.path.join(cache_dir, 'paygen_cache'),
252 ignore_missing=True, sudo=True)
253 logging.info('Finished cleaning cache_dir.')
254
255
256@StageDecorator
257def CleanBuildRoot(root, repo, cache_dir, build_state):
258 """Some kinds of branch transitions break builds.
259
260 This method ensures that cbuildbot's buildroot is a clean checkout on the
261 given branch when it starts. If necessary (a branch transition) it will wipe
262 assorted state that cannot be safely reused from the previous build.
263
264 Args:
265 root: Root directory owned by cbuildbot_launch.
266 repo: repository.RepoRepository instance.
267 cache_dir: Cache directory.
268 build_state: BuildSummary object containing the current build state that
269 will be saved into the cleaned root. The distfiles_ts property will
270 be updated if the distfiles cache is cleaned.
271 """
272 previous_state = GetLastBuildState(root)
273 SetLastBuildState(root, build_state)
274 SanitizeCacheDir(cache_dir)
275 build_state.distfiles_ts = _MaybeCleanDistfiles(
276 cache_dir, previous_state.distfiles_ts)
277
278 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
279 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
280 metrics.Counter(METRIC_CLOBBER).increment(
281 fields=field({}, reason='layout_change'))
282 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
283 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
284 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
285 osutils.RmDir(root, ignore_missing=True, sudo=True)
286 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
287 else:
288 if previous_state.branch != repo.branch:
289 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
290 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
291 repo.branch)
292 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
293 fields=field({}, old_branch=previous_state.branch))
294
295 logging.info('Remove Chroot.')
296 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
297 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
298 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
299
300 logging.info('Remove Chrome checkout.')
301 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
302 ignore_missing=True, sudo=True)
303
304 try:
305 # If there is any failure doing the cleanup, wipe everything.
306 # The previous run might have been killed in the middle leaving stale git
307 # locks. Clean those up, first.
308 repo.PreLoad()
309
310 # If the previous build didn't exit normally, run an expensive step to
311 # cleanup abandoned git locks.
312 if previous_state.status not in (constants.BUILDER_STATUS_FAILED,
313 constants.BUILDER_STATUS_PASSED):
314 repo.CleanStaleLocks()
315
316 repo.BuildRootGitCleanup(prune_all=True)
317 except Exception:
318 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
319 metrics.Counter(METRIC_CLOBBER).increment(
320 fields=field({}, reason='repo_cleanup_failure'))
321 repository.ClearBuildRoot(repo.directory)
322
323 # Ensure buildroot exists. Save the state we are prepped for.
324 osutils.SafeMakedirs(repo.directory)
325 SetLastBuildState(root, build_state)
326
327
Don Garrett125d4dc2017-04-25 16:26:03 -0700328@StageDecorator
Mike Nichols9fb48832021-01-29 14:47:15 -0700329def InitialCheckout(repo, options):
Don Garrett86881cb2017-02-15 15:41:55 -0800330 """Preliminary ChromeOS checkout.
331
332 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
333 match what the build needs, but ensures the buildroot both has a 'hot'
334 checkout, and is close enough that the branched cbuildbot can successfully get
335 the right checkout.
336
337 This checks out full ChromeOS, even if a ChromiumOS build is going to be
338 performed. This is because we have no knowledge of the build config to be
339 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700340
341 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700342 repo: repository.RepoRepository instance.
Mike Nichols9fb48832021-01-29 14:47:15 -0700343 options: A parsed options object from a cbuildbot parser.
Don Garrettc4114cc2016-11-01 20:04:06 -0700344 """
Don Garrettf324bc32017-05-23 14:00:53 -0700345 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800346 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700347 repo.branch)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000348 repo.PreLoad('/preload/chromeos')
349 repo.Sync(detach=True,
Mike Nichols9fb48832021-01-29 14:47:15 -0700350 downgrade_repo=_ShouldDowngradeRepo(options))
Don Garrettc4114cc2016-11-01 20:04:06 -0700351
352
Lann Martin8c4c6802018-05-23 11:09:46 -0600353def ShouldFixBotoCerts(options):
354 """Decide if FixBotoCerts should be applied for this branch."""
355 try:
356 # Only apply to factory and firmware branches.
357 branch = options.branch or ''
358 prefix = branch.split('-')[0]
359 if prefix not in ('factory', 'firmware'):
360 return False
361
362 # Only apply to "old" branches.
363 if branch.endswith('.B'):
364 version = branch[:-2].split('-')[-1]
365 major = int(version.split('.')[0])
366 return major <= 9667 # This is the newest known to be failing.
367
368 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400369 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600370 logging.warning(' failed: %s', e)
371 # Conservatively continue without the fix.
372 return False
373
374
Mike Nichols9fb48832021-01-29 14:47:15 -0700375def _ShouldDowngradeRepo(options):
376 """Determine which repo version to set for the branch.
377
378 Repo version is set at cache creation time, in the nightly builder,
379 which means we are typically at the latest version. Older branches
380 are incompatible with newer version of ToT, therefore we downgrade
381 repo to a known working version.
382
383 Args:
384 options: A parsed options object from a cbuildbot parser.
385
386 Returns:
387 bool of whether to downgrade repo version based on branch.
388 """
389 try:
390 branch = options.branch or ''
391 # Only apply to "old" branches.
392 if branch.endswith('.B'):
393 branch_num = branch[:-2].split('-')[1][1:3]
394 return branch_num <= 79 # This is the newest known to be failing.
395
396 return False
397 except Exception as e:
398 logging.warning(' failed: %s', e)
399 # Conservatively continue without the fix.
400 return False
401
402
Don Garrett066e6f52017-09-28 19:14:01 -0700403@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700404def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700405 """Start cbuildbot in specified directory with all arguments.
406
407 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700408 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700409 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700410 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700411
412 Returns:
413 Return code of cbuildbot as an integer.
414 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700415 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700416
Don Garrettd1d90dd2017-06-13 17:35:52 -0700417 # Fixup buildroot parameter.
418 argv = argv[:]
Mike Frysinger79cca962019-06-13 15:26:53 -0400419 for i, arg in enumerate(argv):
420 if arg in ('-r', '--buildroot'):
421 argv[i + 1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800422
Don Garrettd1d90dd2017-06-13 17:35:52 -0700423 # This filters out command line arguments not supported by older versions
424 # of cbuildbot.
425 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400426 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700427 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800428 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700429 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800430
Don Garretta50bf492017-09-28 18:33:02 -0700431 # We want cbuildbot to use branched depot_tools scripts from our manifest,
432 # so that depot_tools is branched to match cbuildbot.
433 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
434 extra_env = {'PATH': PrependPath(depot_tools_path)}
435
Lann Martinebae73d2018-05-21 17:12:00 -0600436 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600437 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600438
439 with boto_compat.FixBotoCerts(activate=fix_boto):
Mike Frysinger45602c72019-09-22 02:15:11 -0400440 result = cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500441 cmd, extra_env=extra_env, check=False, cwd=buildroot)
Lann Martinebae73d2018-05-21 17:12:00 -0600442
Don Garrettacbb2392017-05-11 18:27:41 -0700443 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700444
Don Garrett60967922017-04-12 18:51:44 -0700445
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700446@StageDecorator
447def CleanupChroot(buildroot):
448 """Unmount/clean up an image-based chroot without deleting the backing image.
449
450 Args:
451 buildroot: Directory containing the chroot to be cleaned up.
452 """
453 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
454 logging.info('Cleaning up chroot at %s', chroot_dir)
455 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Mike Frysingerf0146252019-09-02 13:31:05 -0400456 try:
457 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
458 except timeout_util.TimeoutError:
459 logging.exception('Cleaning up chroot timed out')
460 # Dump debug info to help https://crbug.com/1000034.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500461 cros_build_lib.run(['mount'], check=True)
462 cros_build_lib.run(['uname', '-a'], check=True)
463 cros_build_lib.sudo_run(['losetup', '-a'], check=True)
464 cros_build_lib.run(['dmesg'], check=True)
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400465 logging.warning('Assuming the bot is going to reboot, so ignoring this '
466 'failure; see https://crbug.com/1000034')
Mike Frysingerf0146252019-09-02 13:31:05 -0400467
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400468 # NB: We ignore errors at this point because this stage runs last. If the
469 # chroot failed to unmount, we're going to reboot the system once we're done,
470 # and that will implicitly take care of cleaning things up. If the bots stop
471 # rebooting after every run, we'll need to make this fatal all the time.
472 #
473 # TODO(crbug.com/1000034): This should be fatal all the time.
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700474
475
Don Garrettf15d65b2017-04-12 12:39:55 -0700476def ConfigureGlobalEnvironment():
477 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700478 # Set umask to 022 so files created by buildbot are readable.
479 os.umask(0o22)
480
Don Garrett86fec482017-05-17 18:13:33 -0700481 # These variables can interfere with LANG / locale behavior.
482 unwanted_local_vars = [
483 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
484 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
485 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
486 ]
487 for v in unwanted_local_vars:
488 os.environ.pop(v, None)
489
490 # This variable is required for repo sync's to work in all cases.
491 os.environ['LANG'] = 'en_US.UTF-8'
492
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000493
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600494def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700495 """main method of script.
496
497 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600498 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700499 argv: All command line arguments to pass as list of strings.
500
501 Returns:
502 Return code of cbuildbot as an integer.
503 """
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000504 branchname = options.branch or 'main'
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000505 root = options.buildroot
506 buildroot = os.path.join(root, 'repository')
507 workspace = os.path.join(root, 'workspace')
Don Garretta50bf492017-09-28 18:33:02 -0700508 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700509
Ben Pastenec8e4e792018-05-14 20:20:25 +0000510 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600511 with metrics.Presence(METRIC_ACTIVE), \
512 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700513
Ben Pastenec8e4e792018-05-14 20:20:25 +0000514 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600515 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000516 if options.enable_buildbot_tags:
517 logging.EnableBuildbotMarkers()
518 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800519
Ben Pastenec8e4e792018-05-14 20:20:25 +0000520 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600521 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600522 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000523 repo = repository.RepoRepository(manifest_url, buildroot,
524 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000525 git_cache_dir=options.git_cache_dir)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000526 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800527
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000528 # Clean up the buildroot to a safe state.
529 with metrics.SecondsTimer(METRIC_CLEAN):
530 build_state = GetCurrentBuildState(options, branchname)
531 CleanBuildRoot(root, repo, options.cache_dir, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700532
Ben Pastenec8e4e792018-05-14 20:20:25 +0000533 # Get a checkout close enough to the branch that cbuildbot can handle it.
534 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600535 with metrics.SecondsTimer(METRIC_INITIAL):
Mike Nichols9fb48832021-01-29 14:47:15 -0700536 InitialCheckout(repo, options)
Don Garrettacbb2392017-05-11 18:27:41 -0700537
Ben Pastenec8e4e792018-05-14 20:20:25 +0000538 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600539 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
540 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000541 if previous_build_state.is_valid():
542 argv.append('--previous-build-state')
Mike Frysinger1a443332019-11-24 02:48:03 -0500543 argv.append(base64.b64encode(previous_build_state.to_json().encode(
544 'utf-8')).decode('utf-8'))
Don Garrettb497f552018-07-09 16:01:13 -0700545 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600546
Don Garrett61ce1ee2019-02-26 16:20:25 -0800547 if not options.cache_dir_specified:
548 argv.extend(['--cache-dir', options.cache_dir])
549
Ben Pastenec8e4e792018-05-14 20:20:25 +0000550 result = Cbuildbot(buildroot, depot_tools_path, argv)
551 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600552
Ben Pastenec8e4e792018-05-14 20:20:25 +0000553 build_state.status = (
554 constants.BUILDER_STATUS_PASSED
555 if result == 0 else constants.BUILDER_STATUS_FAILED)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000556 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600557
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600558 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400559 CleanupChroot(buildroot)
Don Garrett91a8cfc2018-06-22 15:39:36 -0700560
Ben Pastenec8e4e792018-05-14 20:20:25 +0000561 return result
562
Don Garrettacbb2392017-05-11 18:27:41 -0700563
564def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600565 options = PreParseArguments(argv)
566 metric_fields = {
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000567 'branch_name': options.branch or 'main',
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600568 'build_config': options.build_config_name,
569 'tryjob': options.remote_trybot,
570 }
571
Ben Pastenec8e4e792018-05-14 20:20:25 +0000572 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600573 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
574 common_metric_fields=metric_fields,
575 indirect=True):
576 return _main(options, argv)