blob: e9d61d7d20164965fecf3cb409e07f70e0ed6147 [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.
Alex Klein1699fab2022-09-08 08:46:06 -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
Alex Klein1699fab2022-09-08 08:46:06 -060059BUILDER_STATE_FILENAME = ".cbuildbot_build_state.json"
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060060
Don Garrett60967922017-04-12 18:51:44 -070061
Don Garrett125d4dc2017-04-25 16:26:03 -070062def StageDecorator(functor):
Alex Klein1699fab2022-09-08 08:46:06 -060063 """A Decorator that adds buildbot stage tags around a method.
Don Garrett125d4dc2017-04-25 16:26:03 -070064
Alex Klein1699fab2022-09-08 08:46:06 -060065 It uses the method name as the stage name, and assumes failure on a true
66 return value, or an exception.
67 """
Don Garrett125d4dc2017-04-25 16:26:03 -070068
Alex Klein1699fab2022-09-08 08:46:06 -060069 @functools.wraps(functor)
70 def wrapped_functor(*args, **kwargs):
71 try:
72 cbuildbot_alerts.PrintBuildbotStepName(functor.__name__)
73 result = functor(*args, **kwargs)
74 except Exception:
75 cbuildbot_alerts.PrintBuildbotStepFailure()
76 raise
Don Garrettacbb2392017-05-11 18:27:41 -070077
Alex Klein1699fab2022-09-08 08:46:06 -060078 if result:
79 cbuildbot_alerts.PrintBuildbotStepFailure()
80 return result
81
82 return wrapped_functor
Don Garrett125d4dc2017-04-25 16:26:03 -070083
84
Don Garrettb5fc08b2017-11-20 21:51:16 +000085def field(fields, **kwargs):
Alex Klein1699fab2022-09-08 08:46:06 -060086 """Helper for inserting more fields into a metrics fields dictionary.
Don Garrettacbb2392017-05-11 18:27:41 -070087
Alex Klein1699fab2022-09-08 08:46:06 -060088 Args:
89 fields: Dictionary of metrics fields.
90 kwargs: Each argument is a key/value pair to insert into dict.
Don Garrettacbb2392017-05-11 18:27:41 -070091
Alex Klein1699fab2022-09-08 08:46:06 -060092 Returns:
93 Copy of original dictionary with kwargs set as fields.
94 """
95 f = fields.copy()
96 f.update(kwargs)
97 return f
Don Garrettacbb2392017-05-11 18:27:41 -070098
Don Garretta50bf492017-09-28 18:33:02 -070099
100def PrependPath(prepend):
Alex Klein1699fab2022-09-08 08:46:06 -0600101 """Generate path with new directory at the beginning.
Don Garretta50bf492017-09-28 18:33:02 -0700102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 Args:
104 prepend: Directory to add at the beginning of the path.
Don Garretta50bf492017-09-28 18:33:02 -0700105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 Returns:
107 Extended path as a string.
108 """
109 return os.pathsep.join([prepend, os.environ.get("PATH", os.defpath)])
Don Garretta50bf492017-09-28 18:33:02 -0700110
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000111
Don Garrett86881cb2017-02-15 15:41:55 -0800112def PreParseArguments(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600113 """Extract the branch name from cbuildbot command line arguments.
Don Garrettc4114cc2016-11-01 20:04:06 -0700114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 Args:
116 argv: The command line arguments to parse.
Don Garrettc4114cc2016-11-01 20:04:06 -0700117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 Returns:
119 Branch as a string ('main' if nothing is specified).
120 """
121 parser = cbuildbot.CreateParser()
122 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800123
Alex Klein1699fab2022-09-08 08:46:06 -0600124 if not options.cache_dir:
125 options.cache_dir = os.path.join(
126 options.buildroot, "repository", ".cache"
127 )
Don Garrett61ce1ee2019-02-26 16:20:25 -0800128
Alex Klein1699fab2022-09-08 08:46:06 -0600129 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800130
Alex Klein1699fab2022-09-08 08:46:06 -0600131 # This option isn't required for cbuildbot, but is for us.
132 if not options.buildroot:
133 cros_build_lib.Die("--buildroot is a required option.")
Don Garrett86881cb2017-02-15 15:41:55 -0800134
Alex Klein1699fab2022-09-08 08:46:06 -0600135 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700136
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000137
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600138def GetCurrentBuildState(options, branch):
Alex Klein1699fab2022-09-08 08:46:06 -0600139 """Extract information about the current build state from command-line args.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600140
Alex Klein1699fab2022-09-08 08:46:06 -0600141 Args:
142 options: A parsed options object from a cbuildbot parser.
143 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600144
Alex Klein1699fab2022-09-08 08:46:06 -0600145 Returns:
146 A BuildSummary object describing the current build.
147 """
148 build_state = build_summary.BuildSummary(
149 status=constants.BUILDER_STATUS_INFLIGHT,
150 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
151 branch=branch,
152 )
153 if options.buildnumber:
154 build_state.build_number = options.buildnumber
155 if options.buildbucket_id:
156 build_state.buildbucket_id = options.buildbucket_id
157 if options.master_build_id:
158 build_state.master_build_id = options.master_build_id
159 return build_state
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600160
161
162def GetLastBuildState(root):
Alex Klein1699fab2022-09-08 08:46:06 -0600163 """Fetch the state of the last build run from |root|.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600164
Alex Klein1699fab2022-09-08 08:46:06 -0600165 If the saved state file can't be read or doesn't contain valid JSON, a default
166 state will be returned.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600167
Alex Klein1699fab2022-09-08 08:46:06 -0600168 Args:
169 root: Root of the working directory tree as a string.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 Returns:
172 A BuildSummary object representing the previous build.
173 """
174 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600175
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600176 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600177
Alex Klein1699fab2022-09-08 08:46:06 -0600178 try:
179 state_raw = osutils.ReadFile(state_file)
180 state.from_json(state_raw)
181 except IOError as e:
182 logging.info(
183 "Unable to read %s: %s. Expected for first task on bot.",
184 state_file,
185 e,
186 )
187 return state
188 except ValueError as e:
189 logging.warning(
190 "Saved state file %s is not valid JSON: %s", state_file, e
191 )
192 return state
193
194 if not state.is_valid():
195 logging.warning("Previous build state is not valid. Ignoring.")
196 state = build_summary.BuildSummary()
197
198 return state
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600199
200
201def SetLastBuildState(root, new_state):
Alex Klein1699fab2022-09-08 08:46:06 -0600202 """Save the state of the last build under |root|.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 Args:
205 root: Root of the working directory tree as a string.
206 new_state: BuildSummary object containing the state to be saved.
207 """
208 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
209 osutils.WriteFile(state_file, new_state.to_json())
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600210
Alex Klein1699fab2022-09-08 08:46:06 -0600211 # Remove old state file. Its contents have been migrated into the new file.
212 old_state_file = os.path.join(root, ".cbuildbot_launch_state")
213 osutils.SafeUnlink(old_state_file)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600214
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600215
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000216def _MaybeCleanDistfiles(cache_dir, distfiles_ts):
Alex Klein1699fab2022-09-08 08:46:06 -0600217 """Cleans the distfiles directory if too old.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000218
Alex Klein1699fab2022-09-08 08:46:06 -0600219 Args:
220 cache_dir: Directory of the cache, as a string.
221 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
222 be None.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000223
Alex Klein1699fab2022-09-08 08:46:06 -0600224 Returns:
225 The new distfiles_ts to persist in state.
226 """
227 # distfiles_ts can be None for a fresh environment, which means clean.
228 if distfiles_ts is None:
229 return time.time()
230
231 distfiles_age = (time.time() - distfiles_ts) / 3600.0
232 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
233 return distfiles_ts
234
235 logging.info(
236 "Remove old distfiles cache (cache expiry %d hours)",
237 _DISTFILES_CACHE_EXPIRY_HOURS,
238 )
239 osutils.RmDir(
240 os.path.join(cache_dir, "distfiles"), ignore_missing=True, sudo=True
241 )
242 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
243 fields=field({}, reason="cache_expired")
244 )
245
246 # Cleaned cache, so reset distfiles_ts
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000247 return time.time()
248
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000249
250def SanitizeCacheDir(cache_dir):
Alex Klein1699fab2022-09-08 08:46:06 -0600251 """Make certain the .cache directory is valid.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 Args:
254 cache_dir: Directory of the cache, as a string.
255 """
256 logging.info("Cleaning up cache dir at %s", cache_dir)
257 # Verify that .cache is writable by the current user.
258 try:
259 osutils.Touch(
260 os.path.join(cache_dir, ".cbuildbot_launch"), makedirs=True
261 )
262 except IOError:
263 logging.info("Bad Permissions for cache dir, wiping: %s", cache_dir)
264 osutils.RmDir(cache_dir, sudo=True)
265 osutils.Touch(
266 os.path.join(cache_dir, ".cbuildbot_launch"), makedirs=True
267 )
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000268
Alex Klein1699fab2022-09-08 08:46:06 -0600269 osutils.RmDir(
270 os.path.join(cache_dir, "paygen_cache"), ignore_missing=True, sudo=True
271 )
272 logging.info("Finished cleaning cache_dir.")
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000273
274
275@StageDecorator
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400276def CleanBuildRoot(root, repo, cache_dir, build_state, source_cache=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600277 """Some kinds of branch transitions break builds.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000278
Alex Klein1699fab2022-09-08 08:46:06 -0600279 This method ensures that cbuildbot's buildroot is a clean checkout on the
280 given branch when it starts. If necessary (a branch transition) it will wipe
281 assorted state that cannot be safely reused from the previous build.
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000282
Alex Klein1699fab2022-09-08 08:46:06 -0600283 Args:
284 root: Root directory owned by cbuildbot_launch.
285 repo: repository.RepoRepository instance.
286 cache_dir: Cache directory.
287 build_state: BuildSummary object containing the current build state that
288 will be saved into the cleaned root. The distfiles_ts property will
289 be updated if the distfiles cache is cleaned.
290 source_cache: Bool whether to use source cache mounts.
291 """
292 previous_state = GetLastBuildState(root)
293 SetLastBuildState(root, build_state)
294 SanitizeCacheDir(cache_dir)
295 build_state.distfiles_ts = _MaybeCleanDistfiles(
296 cache_dir, previous_state.distfiles_ts
297 )
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000298
Mike Nicholsfe1a5e62021-09-03 13:11:17 -0400299 if not source_cache:
Alex Klein1699fab2022-09-08 08:46:06 -0600300 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
301 cbuildbot_alerts.PrintBuildbotStepText(
302 "Unknown layout: Wiping buildroot."
303 )
304 metrics.Counter(METRIC_CLOBBER).increment(
305 fields=field({}, reason="layout_change")
306 )
307 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
308 if os.path.exists(chroot_dir) or os.path.exists(
309 chroot_dir + ".img"
310 ):
311 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
312 osutils.RmDir(root, ignore_missing=True, sudo=True)
313 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
314 else:
315 if previous_state.branch != repo.branch:
316 cbuildbot_alerts.PrintBuildbotStepText(
317 "Branch change: Cleaning buildroot."
318 )
319 logging.info(
320 "Unmatched branch: %s -> %s",
321 previous_state.branch,
322 repo.branch,
323 )
324 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
325 fields=field({}, old_branch=previous_state.branch)
326 )
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000327
Alex Klein1699fab2022-09-08 08:46:06 -0600328 logging.info("Remove Chroot.")
329 chroot_dir = os.path.join(
330 repo.directory, constants.DEFAULT_CHROOT_DIR
331 )
332 if os.path.exists(chroot_dir) or os.path.exists(
333 chroot_dir + ".img"
334 ):
335 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
336
337 logging.info("Remove Chrome checkout.")
338 osutils.RmDir(
339 os.path.join(repo.directory, ".cache", "distfiles"),
340 ignore_missing=True,
341 sudo=True,
342 )
343
344 try:
345 # If there is any failure doing the cleanup, wipe everything.
346 # The previous run might have been killed in the middle leaving stale git
347 # locks. Clean those up, first.
348 if not source_cache:
349 repo.PreLoad()
350
351 # If the previous build didn't exit normally, run an expensive step to
352 # cleanup abandoned git locks.
353 if previous_state.status not in (
354 constants.BUILDER_STATUS_FAILED,
355 constants.BUILDER_STATUS_PASSED,
356 ):
357 repo.CleanStaleLocks()
358
359 if not source_cache:
360 repo.BuildRootGitCleanup(prune_all=True)
361 except Exception:
362 logging.info(
363 "Checkout cleanup failed, wiping buildroot:", exc_info=True
364 )
365 metrics.Counter(METRIC_CLOBBER).increment(
366 fields=field({}, reason="repo_cleanup_failure")
367 )
368 repository.ClearBuildRoot(repo.directory)
369
370 if not source_cache:
371 # Ensure buildroot exists. Save the state we are prepped for.
372 osutils.SafeMakedirs(repo.directory)
373 SetLastBuildState(root, build_state)
Mike Nicholsd0acc7f2021-05-21 17:18:24 +0000374
375
Don Garrett125d4dc2017-04-25 16:26:03 -0700376@StageDecorator
Mike Nichols9fb48832021-01-29 14:47:15 -0700377def InitialCheckout(repo, options):
Alex Klein1699fab2022-09-08 08:46:06 -0600378 """Preliminary ChromeOS checkout.
Don Garrett86881cb2017-02-15 15:41:55 -0800379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
381 match what the build needs, but ensures the buildroot both has a 'hot'
382 checkout, and is close enough that the branched cbuildbot can successfully get
383 the right checkout.
Don Garrett86881cb2017-02-15 15:41:55 -0800384
Alex Klein1699fab2022-09-08 08:46:06 -0600385 This checks out full ChromeOS, even if a ChromiumOS build is going to be
386 performed. This is because we have no knowledge of the build config to be
387 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700388
Alex Klein1699fab2022-09-08 08:46:06 -0600389 Args:
390 repo: repository.RepoRepository instance.
391 options: A parsed options object from a cbuildbot parser.
392 """
393 cbuildbot_alerts.PrintBuildbotStepText("Branch: %s" % repo.branch)
394 if not options.source_cache:
395 logging.info(
396 "Bootstrap script starting initial sync on branch: %s", repo.branch
397 )
398 repo.PreLoad("/preload/chromeos")
399 repo.Sync(
400 jobs=32, detach=True, downgrade_repo=_ShouldDowngradeRepo(options)
401 )
Don Garrettc4114cc2016-11-01 20:04:06 -0700402
403
Lann Martin8c4c6802018-05-23 11:09:46 -0600404def ShouldFixBotoCerts(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600405 """Decide if FixBotoCerts should be applied for this branch."""
406 try:
407 # Only apply to factory and firmware branches.
408 branch = options.branch or ""
409 prefix = branch.split("-")[0]
410 if prefix not in ("factory", "firmware"):
411 return False
Lann Martin8c4c6802018-05-23 11:09:46 -0600412
Alex Klein1699fab2022-09-08 08:46:06 -0600413 # Only apply to "old" branches.
414 if branch.endswith(".B"):
415 version = branch[:-2].split("-")[-1]
416 major = int(version.split(".")[0])
417 return major <= 9667 # This is the newest known to be failing.
Lann Martin8c4c6802018-05-23 11:09:46 -0600418
Alex Klein1699fab2022-09-08 08:46:06 -0600419 return False
420 except Exception as e:
421 logging.warning(" failed: %s", e)
422 # Conservatively continue without the fix.
423 return False
Lann Martin8c4c6802018-05-23 11:09:46 -0600424
425
Mike Nichols9fb48832021-01-29 14:47:15 -0700426def _ShouldDowngradeRepo(options):
Alex Klein1699fab2022-09-08 08:46:06 -0600427 """Determine which repo version to set for the branch.
Mike Nichols9fb48832021-01-29 14:47:15 -0700428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 Repo version is set at cache creation time, in the nightly builder,
430 which means we are typically at the latest version. Older branches
431 are incompatible with newer version of ToT, therefore we downgrade
432 repo to a known working version.
Mike Nichols9fb48832021-01-29 14:47:15 -0700433
Alex Klein1699fab2022-09-08 08:46:06 -0600434 Args:
435 options: A parsed options object from a cbuildbot parser.
Mike Nichols9fb48832021-01-29 14:47:15 -0700436
Alex Klein1699fab2022-09-08 08:46:06 -0600437 Returns:
438 bool of whether to downgrade repo version based on branch.
439 """
440 try:
441 branch = options.branch or ""
442 # Only apply to "old" branches.
443 if branch.endswith(".B"):
444 branch_num = branch[:-2].split("-")[1][1:3]
445 return branch_num <= 79 # This is the newest known to be failing.
Mike Nichols9fb48832021-01-29 14:47:15 -0700446
Alex Klein1699fab2022-09-08 08:46:06 -0600447 return False
448 except Exception as e:
449 logging.warning(" failed: %s", e)
450 # Conservatively continue without the fix.
451 return False
Mike Nichols9fb48832021-01-29 14:47:15 -0700452
453
Don Garrett066e6f52017-09-28 19:14:01 -0700454@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700455def Cbuildbot(buildroot, depot_tools_path, argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600456 """Start cbuildbot in specified directory with all arguments.
Don Garrettc4114cc2016-11-01 20:04:06 -0700457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 Args:
459 buildroot: Directory to be passed to cbuildbot with --buildroot.
460 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
461 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700462
Alex Klein1699fab2022-09-08 08:46:06 -0600463 Returns:
464 Return code of cbuildbot as an integer.
465 """
466 logging.info("Bootstrap cbuildbot in: %s", buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 # Fixup buildroot parameter.
469 argv = argv[:]
470 for i, arg in enumerate(argv):
471 if arg in ("-r", "--buildroot"):
472 argv[i + 1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800473
Alex Klein1699fab2022-09-08 08:46:06 -0600474 # Source_cache flag is only used to indicate a transition to cache disks
475 # and doesn't need to be passed back to Cbuildbot.
476 if "--source_cache" in argv:
477 argv.remove("--source_cache")
Mike Nicholse333e402021-10-04 15:57:03 -0400478
Alex Klein1699fab2022-09-08 08:46:06 -0600479 logging.info("Cbuildbot Args: %s", argv)
Mike Nichols0b5555e2021-10-05 17:03:23 -0400480
Alex Klein1699fab2022-09-08 08:46:06 -0600481 # This filters out command line arguments not supported by older versions
482 # of cbuildbot.
483 parser = cbuildbot.CreateParser()
484 options = cbuildbot.ParseCommandLine(parser, argv)
485 cbuildbot_path = os.path.join(buildroot, "chromite", "bin", "cbuildbot")
486 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
487 buildroot, cbuildbot_path, options
488 )
Don Garrett597ddff2017-02-17 18:29:37 -0800489
Alex Klein1699fab2022-09-08 08:46:06 -0600490 # We want cbuildbot to use branched depot_tools scripts from our manifest,
491 # so that depot_tools is branched to match cbuildbot.
492 logging.info("Adding depot_tools into PATH: %s", depot_tools_path)
493 extra_env = {"PATH": PrependPath(depot_tools_path)}
Don Garretta50bf492017-09-28 18:33:02 -0700494
Alex Klein1699fab2022-09-08 08:46:06 -0600495 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
496 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 with boto_compat.FixBotoCerts(activate=fix_boto):
499 result = cros_build_lib.run(
500 cmd, extra_env=extra_env, check=False, cwd=buildroot
501 )
Lann Martinebae73d2018-05-21 17:12:00 -0600502
Alex Klein1699fab2022-09-08 08:46:06 -0600503 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700504
Don Garrett60967922017-04-12 18:51:44 -0700505
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700506@StageDecorator
507def CleanupChroot(buildroot):
Alex Klein1699fab2022-09-08 08:46:06 -0600508 """Unmount/clean up an image-based chroot without deleting the backing image.
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700509
Alex Klein1699fab2022-09-08 08:46:06 -0600510 Args:
511 buildroot: Directory containing the chroot to be cleaned up.
512 """
513 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
514 logging.info("Cleaning up chroot at %s", chroot_dir)
515 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + ".img"):
516 try:
517 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
518 except timeout_util.TimeoutError:
519 logging.exception("Cleaning up chroot timed out")
520 # Dump debug info to help https://crbug.com/1000034.
521 cros_build_lib.run(["mount"], check=True)
522 cros_build_lib.run(["uname", "-a"], check=True)
523 cros_build_lib.sudo_run(["losetup", "-a"], check=True)
524 cros_build_lib.run(["dmesg"], check=True)
525 logging.warning(
526 "Assuming the bot is going to reboot, so ignoring this "
527 "failure; see https://crbug.com/1000034"
528 )
Mike Frysingerf0146252019-09-02 13:31:05 -0400529
Alex Klein1699fab2022-09-08 08:46:06 -0600530 # NB: We ignore errors at this point because this stage runs last. If the
531 # chroot failed to unmount, we're going to reboot the system once we're done,
532 # and that will implicitly take care of cleaning things up. If the bots stop
533 # rebooting after every run, we'll need to make this fatal all the time.
534 #
535 # TODO(crbug.com/1000034): This should be fatal all the time.
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700536
537
Don Garrettf15d65b2017-04-12 12:39:55 -0700538def ConfigureGlobalEnvironment():
Alex Klein1699fab2022-09-08 08:46:06 -0600539 """Setup process wide environmental changes."""
540 # Set umask to 022 so files created by buildbot are readable.
541 os.umask(0o22)
Don Garrettf15d65b2017-04-12 12:39:55 -0700542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 # These variables can interfere with LANG / locale behavior.
544 unwanted_local_vars = [
545 "LC_ALL",
546 "LC_CTYPE",
547 "LC_COLLATE",
548 "LC_TIME",
549 "LC_NUMERIC",
550 "LC_MONETARY",
551 "LC_MESSAGES",
552 "LC_PAPER",
553 "LC_NAME",
554 "LC_ADDRESS",
555 "LC_TELEPHONE",
556 "LC_MEASUREMENT",
557 "LC_IDENTIFICATION",
558 ]
559 for v in unwanted_local_vars:
560 os.environ.pop(v, None)
Don Garrett86fec482017-05-17 18:13:33 -0700561
Alex Klein1699fab2022-09-08 08:46:06 -0600562 # This variable is required for repo sync's to work in all cases.
563 os.environ["LANG"] = "en_US.UTF-8"
Don Garrett86fec482017-05-17 18:13:33 -0700564
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000565
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600566def _main(options, argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600567 """main method of script.
Don Garrettc4114cc2016-11-01 20:04:06 -0700568
Alex Klein1699fab2022-09-08 08:46:06 -0600569 Args:
570 options: preparsed options object for the build.
571 argv: All command line arguments to pass as list of strings.
Don Garrettc4114cc2016-11-01 20:04:06 -0700572
Alex Klein1699fab2022-09-08 08:46:06 -0600573 Returns:
574 Return code of cbuildbot as an integer.
575 """
576 branchname = options.branch or "main"
577 root = options.buildroot
578 buildroot = os.path.join(root, "repository")
579 workspace = os.path.join(root, "workspace")
580 if options.source_cache:
581 buildroot = options.buildroot
582 if options.workspace:
583 workspace = options.workspace
584 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700585
Alex Klein1699fab2022-09-08 08:46:06 -0600586 # Does the entire build pass or fail.
587 with metrics.Presence(METRIC_ACTIVE), metrics.SuccessCounter(
588 METRIC_COMPLETED
589 ) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700590
Alex Klein1699fab2022-09-08 08:46:06 -0600591 # Preliminary set, mostly command line parsing.
592 with metrics.SuccessCounter(METRIC_INVOKED):
593 if options.enable_buildbot_tags:
594 cbuildbot_alerts.EnableBuildbotMarkers()
595 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800596
Alex Klein1699fab2022-09-08 08:46:06 -0600597 # Prepare the buildroot with source for the build.
598 with metrics.SuccessCounter(METRIC_PREP):
599 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
600 repo = repository.RepoRepository(
601 manifest_url,
602 buildroot,
603 branch=branchname,
604 git_cache_dir=options.git_cache_dir,
605 )
606 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800607
Alex Klein1699fab2022-09-08 08:46:06 -0600608 # Clean up the buildroot to a safe state.
609 with metrics.SecondsTimer(METRIC_CLEAN):
610 build_state = GetCurrentBuildState(options, branchname)
611 CleanBuildRoot(
612 root,
613 repo,
614 options.cache_dir,
615 build_state,
616 options.source_cache,
617 )
Don Garrettacbb2392017-05-11 18:27:41 -0700618
Alex Klein1699fab2022-09-08 08:46:06 -0600619 # Get a checkout close enough to the branch that cbuildbot can handle it.
620 if options.sync:
621 with metrics.SecondsTimer(METRIC_INITIAL):
622 InitialCheckout(repo, options)
Don Garrettacbb2392017-05-11 18:27:41 -0700623
Alex Klein1699fab2022-09-08 08:46:06 -0600624 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
625 with metrics.SecondsTimer(
626 METRIC_CBUILDBOT
627 ), metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
628 if previous_build_state.is_valid():
629 argv.append("--previous-build-state")
630 argv.append(
631 base64.b64encode(
632 previous_build_state.to_json().encode("utf-8")
633 ).decode("utf-8")
634 )
635 argv.extend(["--workspace", workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600636
Alex Klein1699fab2022-09-08 08:46:06 -0600637 if not options.cache_dir_specified:
638 argv.extend(["--cache-dir", options.cache_dir])
Don Garrett61ce1ee2019-02-26 16:20:25 -0800639
Alex Klein1699fab2022-09-08 08:46:06 -0600640 result = Cbuildbot(buildroot, depot_tools_path, argv)
641 s_fields["success"] = result == 0
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600642
Alex Klein1699fab2022-09-08 08:46:06 -0600643 build_state.status = (
644 constants.BUILDER_STATUS_PASSED
645 if result == 0
646 else constants.BUILDER_STATUS_FAILED
647 )
648 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600649
Alex Klein1699fab2022-09-08 08:46:06 -0600650 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
651 CleanupChroot(buildroot)
Don Garrett91a8cfc2018-06-22 15:39:36 -0700652
Alex Klein1699fab2022-09-08 08:46:06 -0600653 return result
Ben Pastenec8e4e792018-05-14 20:20:25 +0000654
Don Garrettacbb2392017-05-11 18:27:41 -0700655
656def main(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600657 options = PreParseArguments(argv)
658 metric_fields = {
659 "branch_name": options.branch or "main",
660 "build_config": options.build_config_name,
661 "tryjob": options.remote_trybot,
662 }
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600663
Alex Klein1699fab2022-09-08 08:46:06 -0600664 # Enable Monarch metrics gathering.
665 with ts_mon_config.SetupTsMonGlobalState(
666 "cbuildbot_launch", common_metric_fields=metric_fields, indirect=True
667 ):
668 return _main(options, argv)