blob: 5988b2a3f790c23f9a1387bb595878a44426dfc6 [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
Chris McDonaldb55b7032021-06-17 16:41:32 -060018import logging
Don Garrettc4114cc2016-11-01 20:04:06 -070019import os
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000020import time
Don Garrettc4114cc2016-11-01 20:04:06 -070021
Chris McDonaldb55b7032021-06-17 16:41:32 -060022from chromite.cbuildbot import cbuildbot_alerts
Don Garrettc4114cc2016-11-01 20:04:06 -070023from chromite.cbuildbot import repository
Don Garrett597ddff2017-02-17 18:29:37 -080024from chromite.cbuildbot.stages import sync_stages
Lann Martinebae73d2018-05-21 17:12:00 -060025from chromite.lib import boto_compat
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060026from chromite.lib import build_summary
Don Garrett86881cb2017-02-15 15:41:55 -080027from chromite.lib import config_lib
Don Garretta50bf492017-09-28 18:33:02 -070028from chromite.lib import constants
Don Garrettc4114cc2016-11-01 20:04:06 -070029from chromite.lib import cros_build_lib
Benjamin Gordon74645232018-05-04 17:40:42 -060030from chromite.lib import cros_sdk_lib
Don Garrettacbb2392017-05-11 18:27:41 -070031from chromite.lib import metrics
Don Garrettc4114cc2016-11-01 20:04:06 -070032from chromite.lib import osutils
Mike Frysingerf0146252019-09-02 13:31:05 -040033from chromite.lib import timeout_util
Don Garrettacbb2392017-05-11 18:27:41 -070034from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080035from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070036
Mike Frysinger3c831a72020-02-19 02:47:04 -050037
Don Garrett60967922017-04-12 18:51:44 -070038# This number should be incremented when we change the layout of the buildroot
39# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070040BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070041_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070042
Don Garrettacbb2392017-05-11 18:27:41 -070043# Metrics reported to Monarch.
Mike Nicholscb29fbd2018-07-24 11:09:33 -060044METRIC_PREFIX = 'chromeos/chromite/cbuildbot_launch/'
45METRIC_ACTIVE = METRIC_PREFIX + 'active'
46METRIC_INVOKED = METRIC_PREFIX + 'invoked'
47METRIC_COMPLETED = METRIC_PREFIX + 'completed'
48METRIC_PREP = METRIC_PREFIX + 'prep_completed'
49METRIC_CLEAN = METRIC_PREFIX + 'clean_buildroot_durations'
50METRIC_INITIAL = METRIC_PREFIX + 'initial_checkout_durations'
51METRIC_CBUILDBOT = METRIC_PREFIX + 'cbuildbot_durations'
52METRIC_CBUILDBOT_INSTANCE = METRIC_PREFIX + 'cbuildbot_instance_durations'
53METRIC_CLOBBER = METRIC_PREFIX + 'clobber'
54METRIC_BRANCH_CLEANUP = METRIC_PREFIX + 'branch_cleanup'
55METRIC_DISTFILES_CLEANUP = METRIC_PREFIX + 'distfiles_cleanup'
56METRIC_CHROOT_CLEANUP = METRIC_PREFIX + 'chroot_cleanup'
Don Garrettacbb2392017-05-11 18:27:41 -070057
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060058# Builder state
59BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
60
Don Garrett60967922017-04-12 18:51:44 -070061
Don Garrett125d4dc2017-04-25 16:26:03 -070062def StageDecorator(functor):
63 """A Decorator that adds buildbot stage tags around a method.
64
Don Garrettacbb2392017-05-11 18:27:41 -070065 It uses the method name as the stage name, and assumes failure on a true
66 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070067 """
68 @functools.wraps(functor)
69 def wrapped_functor(*args, **kwargs):
70 try:
Chris McDonaldb55b7032021-06-17 16:41:32 -060071 cbuildbot_alerts.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070072 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070073 except Exception:
Chris McDonaldb55b7032021-06-17 16:41:32 -060074 cbuildbot_alerts.PrintBuildbotStepFailure()
Don Garrett125d4dc2017-04-25 16:26:03 -070075 raise
76
Don Garrettacbb2392017-05-11 18:27:41 -070077 if result:
Chris McDonaldb55b7032021-06-17 16:41:32 -060078 cbuildbot_alerts.PrintBuildbotStepFailure()
Don Garrettacbb2392017-05-11 18:27:41 -070079 return result
80
Don Garrett125d4dc2017-04-25 16:26:03 -070081 return wrapped_functor
82
83
Don Garrettb5fc08b2017-11-20 21:51:16 +000084def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070085 """Helper for inserting more fields into a metrics fields dictionary.
86
87 Args:
88 fields: Dictionary of metrics fields.
89 kwargs: Each argument is a key/value pair to insert into dict.
90
91 Returns:
92 Copy of original dictionary with kwargs set as fields.
93 """
94 f = fields.copy()
95 f.update(kwargs)
96 return f
97
Don Garretta50bf492017-09-28 18:33:02 -070098
99def PrependPath(prepend):
100 """Generate path with new directory at the beginning.
101
102 Args:
103 prepend: Directory to add at the beginning of the path.
104
105 Returns:
106 Extended path as a string.
107 """
108 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
109
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000110
Don Garrett86881cb2017-02-15 15:41:55 -0800111def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700112 """Extract the branch name from cbuildbot command line arguments.
113
Don Garrettc4114cc2016-11-01 20:04:06 -0700114 Args:
115 argv: The command line arguments to parse.
116
117 Returns:
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000118 Branch as a string ('main' if nothing is specified).
Don Garrettc4114cc2016-11-01 20:04:06 -0700119 """
Don Garrett86881cb2017-02-15 15:41:55 -0800120 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400121 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800122
123 if not options.cache_dir:
124 options.cache_dir = os.path.join(options.buildroot,
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000125 'repository', '.cache')
Don Garrett61ce1ee2019-02-26 16:20:25 -0800126
Don Garrettd1d90dd2017-06-13 17:35:52 -0700127 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800128
129 # This option isn't required for cbuildbot, but is for us.
130 if not options.buildroot:
131 cros_build_lib.Die('--buildroot is a required option.')
132
133 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700134
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000135
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600136def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600137 """Extract information about the current build state from command-line args.
138
139 Args:
140 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600141 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600142
143 Returns:
144 A BuildSummary object describing the current build.
145 """
146 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600147 status=constants.BUILDER_STATUS_INFLIGHT,
148 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
149 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600150 if options.buildnumber:
151 build_state.build_number = options.buildnumber
152 if options.buildbucket_id:
153 build_state.buildbucket_id = options.buildbucket_id
154 if options.master_build_id:
155 build_state.master_build_id = options.master_build_id
156 return build_state
157
158
159def GetLastBuildState(root):
160 """Fetch the state of the last build run from |root|.
161
162 If the saved state file can't be read or doesn't contain valid JSON, a default
163 state will be returned.
164
165 Args:
166 root: Root of the working directory tree as a string.
167
168 Returns:
169 A BuildSummary object representing the previous build.
170 """
171 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
172
173 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600174
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600175 try:
176 state_raw = osutils.ReadFile(state_file)
177 state.from_json(state_raw)
178 except IOError as e:
Madeleine Hardt187d1562022-03-17 23:51:34 +0000179 logging.info('Unable to read %s: %s. Expected for first task on bot.',
180 state_file, e)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600181 return state
182 except ValueError as e:
183 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
184 return state
185
186 if not state.is_valid():
187 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600188 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600189
190 return state
191
192
193def SetLastBuildState(root, new_state):
194 """Save the state of the last build under |root|.
195
196 Args:
197 root: Root of the working directory tree as a string.
198 new_state: BuildSummary object containing the state to be saved.
199 """
200 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
201 osutils.WriteFile(state_file, new_state.to_json())
202
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600203 # Remove old state file. Its contents have been migrated into the new file.
204 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
205 osutils.SafeUnlink(old_state_file)
206
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600207
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000208def _MaybeCleanDistfiles(cache_dir, distfiles_ts):
209 """Cleans the distfiles directory if too old.
210
211 Args:
212 cache_dir: Directory of the cache, as a string.
213 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
214 be None.
215
216 Returns:
217 The new distfiles_ts to persist in state.
218 """
219 # distfiles_ts can be None for a fresh environment, which means clean.
220 if distfiles_ts is None:
221 return time.time()
222
223 distfiles_age = (time.time() - distfiles_ts) / 3600.0
224 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
225 return distfiles_ts
226
227 logging.info('Remove old distfiles cache (cache expiry %d hours)',
228 _DISTFILES_CACHE_EXPIRY_HOURS)
229 osutils.RmDir(os.path.join(cache_dir, 'distfiles'),
230 ignore_missing=True, sudo=True)
231 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
232 fields=field({}, reason='cache_expired'))
233
234 # Cleaned cache, so reset distfiles_ts
235 return time.time()
236
237
238def SanitizeCacheDir(cache_dir):
239 """Make certain the .cache directory is valid.
240
241 Args:
242 cache_dir: Directory of the cache, as a string.
243 """
244 logging.info('Cleaning up cache dir at %s', cache_dir)
245 # Verify that .cache is writable by the current user.
246 try:
247 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
248 except IOError:
249 logging.info('Bad Permissions for cache dir, wiping: %s', cache_dir)
250 osutils.RmDir(cache_dir, sudo=True)
251 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
252
253 osutils.RmDir(os.path.join(cache_dir, 'paygen_cache'),
254 ignore_missing=True, sudo=True)
255 logging.info('Finished cleaning cache_dir.')
256
257
258@StageDecorator
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400259def CleanBuildRoot(root, repo, cache_dir, build_state, source_cache=False):
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000260 """Some kinds of branch transitions break builds.
261
262 This method ensures that cbuildbot's buildroot is a clean checkout on the
263 given branch when it starts. If necessary (a branch transition) it will wipe
264 assorted state that cannot be safely reused from the previous build.
265
266 Args:
267 root: Root directory owned by cbuildbot_launch.
268 repo: repository.RepoRepository instance.
269 cache_dir: Cache directory.
270 build_state: BuildSummary object containing the current build state that
271 will be saved into the cleaned root. The distfiles_ts property will
272 be updated if the distfiles cache is cleaned.
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400273 source_cache: Bool whether to use source cache mounts.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000274 """
275 previous_state = GetLastBuildState(root)
276 SetLastBuildState(root, build_state)
277 SanitizeCacheDir(cache_dir)
278 build_state.distfiles_ts = _MaybeCleanDistfiles(
279 cache_dir, previous_state.distfiles_ts)
280
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400281 if not source_cache:
282 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Chris McDonaldb55b7032021-06-17 16:41:32 -0600283 cbuildbot_alerts.PrintBuildbotStepText(
Mike Nicholse333e402021-10-04 15:57:03 -0400284 'Unknown layout: Wiping buildroot.')
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400285 metrics.Counter(METRIC_CLOBBER).increment(
286 fields=field({}, reason='layout_change'))
287 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000288 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
289 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400290 osutils.RmDir(root, ignore_missing=True, sudo=True)
291 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
292 else:
293 if previous_state.branch != repo.branch:
294 cbuildbot_alerts.PrintBuildbotStepText(
295 'Branch change: Cleaning buildroot.')
296 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
Mike Nicholse333e402021-10-04 15:57:03 -0400297 repo.branch)
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400298 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
299 fields=field({}, old_branch=previous_state.branch))
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000300
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400301 logging.info('Remove Chroot.')
302 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
303 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
304 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
305
306 logging.info('Remove Chrome checkout.')
307 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
308 ignore_missing=True, sudo=True)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000309
310 try:
311 # If there is any failure doing the cleanup, wipe everything.
312 # The previous run might have been killed in the middle leaving stale git
313 # locks. Clean those up, first.
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400314 if not source_cache:
315 repo.PreLoad()
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000316
317 # If the previous build didn't exit normally, run an expensive step to
318 # cleanup abandoned git locks.
319 if previous_state.status not in (constants.BUILDER_STATUS_FAILED,
320 constants.BUILDER_STATUS_PASSED):
321 repo.CleanStaleLocks()
322
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400323 if not source_cache:
324 repo.BuildRootGitCleanup(prune_all=True)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000325 except Exception:
326 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
327 metrics.Counter(METRIC_CLOBBER).increment(
328 fields=field({}, reason='repo_cleanup_failure'))
329 repository.ClearBuildRoot(repo.directory)
330
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400331 if not source_cache:
332 # Ensure buildroot exists. Save the state we are prepped for.
333 osutils.SafeMakedirs(repo.directory)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000334 SetLastBuildState(root, build_state)
335
336
Don Garrett125d4dc2017-04-25 16:26:03 -0700337@StageDecorator
Mike Nichols9fb48832021-01-29 14:47:15 -0700338def InitialCheckout(repo, options):
Don Garrett86881cb2017-02-15 15:41:55 -0800339 """Preliminary ChromeOS checkout.
340
341 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
342 match what the build needs, but ensures the buildroot both has a 'hot'
343 checkout, and is close enough that the branched cbuildbot can successfully get
344 the right checkout.
345
346 This checks out full ChromeOS, even if a ChromiumOS build is going to be
347 performed. This is because we have no knowledge of the build config to be
348 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700349
350 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700351 repo: repository.RepoRepository instance.
Mike Nichols9fb48832021-01-29 14:47:15 -0700352 options: A parsed options object from a cbuildbot parser.
Don Garrettc4114cc2016-11-01 20:04:06 -0700353 """
Chris McDonaldb55b7032021-06-17 16:41:32 -0600354 cbuildbot_alerts.PrintBuildbotStepText('Branch: %s' % repo.branch)
Mike Nicholsb49ceb92021-09-01 15:43:37 -0400355 if not options.source_cache:
356 logging.info('Bootstrap script starting initial sync on branch: %s',
357 repo.branch)
358 repo.PreLoad('/preload/chromeos')
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400359 repo.Sync(jobs=32,
360 detach=True,
Mike Nichols9fb48832021-01-29 14:47:15 -0700361 downgrade_repo=_ShouldDowngradeRepo(options))
Don Garrettc4114cc2016-11-01 20:04:06 -0700362
363
Lann Martin8c4c6802018-05-23 11:09:46 -0600364def ShouldFixBotoCerts(options):
365 """Decide if FixBotoCerts should be applied for this branch."""
366 try:
367 # Only apply to factory and firmware branches.
368 branch = options.branch or ''
369 prefix = branch.split('-')[0]
370 if prefix not in ('factory', 'firmware'):
371 return False
372
373 # Only apply to "old" branches.
374 if branch.endswith('.B'):
375 version = branch[:-2].split('-')[-1]
376 major = int(version.split('.')[0])
377 return major <= 9667 # This is the newest known to be failing.
378
379 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400380 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600381 logging.warning(' failed: %s', e)
382 # Conservatively continue without the fix.
383 return False
384
385
Mike Nichols9fb48832021-01-29 14:47:15 -0700386def _ShouldDowngradeRepo(options):
387 """Determine which repo version to set for the branch.
388
389 Repo version is set at cache creation time, in the nightly builder,
390 which means we are typically at the latest version. Older branches
391 are incompatible with newer version of ToT, therefore we downgrade
392 repo to a known working version.
393
394 Args:
395 options: A parsed options object from a cbuildbot parser.
396
397 Returns:
398 bool of whether to downgrade repo version based on branch.
399 """
400 try:
401 branch = options.branch or ''
402 # Only apply to "old" branches.
403 if branch.endswith('.B'):
404 branch_num = branch[:-2].split('-')[1][1:3]
405 return branch_num <= 79 # This is the newest known to be failing.
406
407 return False
408 except Exception as e:
409 logging.warning(' failed: %s', e)
410 # Conservatively continue without the fix.
411 return False
412
413
Don Garrett066e6f52017-09-28 19:14:01 -0700414@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700415def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700416 """Start cbuildbot in specified directory with all arguments.
417
418 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700419 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700420 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700421 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700422
423 Returns:
424 Return code of cbuildbot as an integer.
425 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700426 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700427
Don Garrettd1d90dd2017-06-13 17:35:52 -0700428 # Fixup buildroot parameter.
429 argv = argv[:]
Mike Frysinger79cca962019-06-13 15:26:53 -0400430 for i, arg in enumerate(argv):
431 if arg in ('-r', '--buildroot'):
432 argv[i + 1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800433
Mike Nicholse333e402021-10-04 15:57:03 -0400434 # Source_cache flag is only used to indicate a transition to cache disks
435 # and doesn't need to be passed back to Cbuildbot.
436 if '--source_cache' in argv:
437 argv.remove('--source_cache')
438
Mike Nichols0b5555e2021-10-05 17:03:23 -0400439 logging.info('Cbuildbot Args: %s', argv)
440
Don Garrettd1d90dd2017-06-13 17:35:52 -0700441 # This filters out command line arguments not supported by older versions
442 # of cbuildbot.
443 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400444 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700445 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800446 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700447 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800448
Don Garretta50bf492017-09-28 18:33:02 -0700449 # We want cbuildbot to use branched depot_tools scripts from our manifest,
450 # so that depot_tools is branched to match cbuildbot.
451 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
452 extra_env = {'PATH': PrependPath(depot_tools_path)}
453
Lann Martinebae73d2018-05-21 17:12:00 -0600454 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600455 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600456
457 with boto_compat.FixBotoCerts(activate=fix_boto):
Mike Frysinger45602c72019-09-22 02:15:11 -0400458 result = cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500459 cmd, extra_env=extra_env, check=False, cwd=buildroot)
Lann Martinebae73d2018-05-21 17:12:00 -0600460
Don Garrettacbb2392017-05-11 18:27:41 -0700461 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700462
Don Garrett60967922017-04-12 18:51:44 -0700463
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700464@StageDecorator
465def CleanupChroot(buildroot):
466 """Unmount/clean up an image-based chroot without deleting the backing image.
467
468 Args:
469 buildroot: Directory containing the chroot to be cleaned up.
470 """
471 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
472 logging.info('Cleaning up chroot at %s', chroot_dir)
473 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Mike Frysingerf0146252019-09-02 13:31:05 -0400474 try:
475 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
476 except timeout_util.TimeoutError:
477 logging.exception('Cleaning up chroot timed out')
478 # Dump debug info to help https://crbug.com/1000034.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500479 cros_build_lib.run(['mount'], check=True)
480 cros_build_lib.run(['uname', '-a'], check=True)
481 cros_build_lib.sudo_run(['losetup', '-a'], check=True)
482 cros_build_lib.run(['dmesg'], check=True)
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400483 logging.warning('Assuming the bot is going to reboot, so ignoring this '
484 'failure; see https://crbug.com/1000034')
Mike Frysingerf0146252019-09-02 13:31:05 -0400485
Chris McDonaldb55b7032021-06-17 16:41:32 -0600486 # NB: We ignore errors at this point because this stage runs last. If the
487 # chroot failed to unmount, we're going to reboot the system once we're done,
488 # and that will implicitly take care of cleaning things up. If the bots stop
489 # rebooting after every run, we'll need to make this fatal all the time.
490 #
491 # TODO(crbug.com/1000034): This should be fatal all the time.
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700492
493
Don Garrettf15d65b2017-04-12 12:39:55 -0700494def ConfigureGlobalEnvironment():
495 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700496 # Set umask to 022 so files created by buildbot are readable.
497 os.umask(0o22)
498
Don Garrett86fec482017-05-17 18:13:33 -0700499 # These variables can interfere with LANG / locale behavior.
500 unwanted_local_vars = [
501 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
502 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
503 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
504 ]
505 for v in unwanted_local_vars:
506 os.environ.pop(v, None)
507
508 # This variable is required for repo sync's to work in all cases.
509 os.environ['LANG'] = 'en_US.UTF-8'
510
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000511
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600512def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700513 """main method of script.
514
515 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600516 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700517 argv: All command line arguments to pass as list of strings.
518
519 Returns:
520 Return code of cbuildbot as an integer.
521 """
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000522 branchname = options.branch or 'main'
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000523 root = options.buildroot
524 buildroot = os.path.join(root, 'repository')
525 workspace = os.path.join(root, 'workspace')
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400526 if options.source_cache:
527 buildroot = options.buildroot
528 if options.workspace:
529 workspace = options.workspace
Don Garretta50bf492017-09-28 18:33:02 -0700530 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700531
Ben Pastenec8e4e792018-05-14 20:20:25 +0000532 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600533 with metrics.Presence(METRIC_ACTIVE), \
534 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700535
Ben Pastenec8e4e792018-05-14 20:20:25 +0000536 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600537 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000538 if options.enable_buildbot_tags:
Chris McDonaldb55b7032021-06-17 16:41:32 -0600539 cbuildbot_alerts.EnableBuildbotMarkers()
Ben Pastenec8e4e792018-05-14 20:20:25 +0000540 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800541
Ben Pastenec8e4e792018-05-14 20:20:25 +0000542 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600543 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600544 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000545 repo = repository.RepoRepository(manifest_url, buildroot,
546 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000547 git_cache_dir=options.git_cache_dir)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000548 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800549
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000550 # Clean up the buildroot to a safe state.
551 with metrics.SecondsTimer(METRIC_CLEAN):
552 build_state = GetCurrentBuildState(options, branchname)
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400553 CleanBuildRoot(root, repo, options.cache_dir, build_state,
554 options.source_cache)
Don Garrettacbb2392017-05-11 18:27:41 -0700555
Ben Pastenec8e4e792018-05-14 20:20:25 +0000556 # Get a checkout close enough to the branch that cbuildbot can handle it.
557 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600558 with metrics.SecondsTimer(METRIC_INITIAL):
Mike Nichols9fb48832021-01-29 14:47:15 -0700559 InitialCheckout(repo, options)
Don Garrettacbb2392017-05-11 18:27:41 -0700560
Ben Pastenec8e4e792018-05-14 20:20:25 +0000561 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600562 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
563 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000564 if previous_build_state.is_valid():
565 argv.append('--previous-build-state')
Mike Frysinger1a443332019-11-24 02:48:03 -0500566 argv.append(base64.b64encode(previous_build_state.to_json().encode(
567 'utf-8')).decode('utf-8'))
Don Garrettb497f552018-07-09 16:01:13 -0700568 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600569
Don Garrett61ce1ee2019-02-26 16:20:25 -0800570 if not options.cache_dir_specified:
571 argv.extend(['--cache-dir', options.cache_dir])
572
Ben Pastenec8e4e792018-05-14 20:20:25 +0000573 result = Cbuildbot(buildroot, depot_tools_path, argv)
574 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600575
Ben Pastenec8e4e792018-05-14 20:20:25 +0000576 build_state.status = (
577 constants.BUILDER_STATUS_PASSED
578 if result == 0 else constants.BUILDER_STATUS_FAILED)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000579 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600580
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600581 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400582 CleanupChroot(buildroot)
Don Garrett91a8cfc2018-06-22 15:39:36 -0700583
Ben Pastenec8e4e792018-05-14 20:20:25 +0000584 return result
585
Don Garrettacbb2392017-05-11 18:27:41 -0700586
587def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600588 options = PreParseArguments(argv)
589 metric_fields = {
Julio Hurtado9265c7e2021-04-19 23:04:44 +0000590 'branch_name': options.branch or 'main',
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600591 'build_config': options.build_config_name,
592 'tryjob': options.remote_trybot,
593 }
594
Ben Pastenec8e4e792018-05-14 20:20:25 +0000595 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600596 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
597 common_metric_fields=metric_fields,
598 indirect=True):
599 return _main(options, argv)