blob: 690f6693483d6d1e5c3b2ce5abf6ec7600bb4f8d [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Don Garrettc4114cc2016-11-01 20:04:06 -07002# Copyright 2016 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Bootstrap for cbuildbot.
7
8This script is intended to checkout chromite on the branch specified by -b or
9--branch (as normally accepted by cbuildbot), and then invoke cbuildbot. Most
10arguments are not parsed, only passed along. If a branch is not specified, this
11script will use 'master'.
12
13Among other things, this allows us to invoke build configs that exist on a given
14branch, but not on TOT.
15"""
16
17from __future__ import print_function
18
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060019import base64
Don Garrett125d4dc2017-04-25 16:26:03 -070020import functools
Don Garrettc4114cc2016-11-01 20:04:06 -070021import os
Mike Frysinger3c831a72020-02-19 02:47:04 -050022import sys
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070023import time
Don Garrettc4114cc2016-11-01 20:04:06 -070024
25from chromite.cbuildbot import repository
Don Garrett597ddff2017-02-17 18:29:37 -080026from chromite.cbuildbot.stages import sync_stages
Lann Martinebae73d2018-05-21 17:12:00 -060027from chromite.lib import boto_compat
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060028from chromite.lib import build_summary
Don Garrett86881cb2017-02-15 15:41:55 -080029from chromite.lib import config_lib
Don Garretta50bf492017-09-28 18:33:02 -070030from chromite.lib import constants
Don Garrettc4114cc2016-11-01 20:04:06 -070031from chromite.lib import cros_build_lib
32from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060033from chromite.lib import cros_sdk_lib
Don Garrettacbb2392017-05-11 18:27:41 -070034from chromite.lib import metrics
Don Garrettc4114cc2016-11-01 20:04:06 -070035from chromite.lib import osutils
Mike Frysingerf0146252019-09-02 13:31:05 -040036from chromite.lib import timeout_util
Don Garrettacbb2392017-05-11 18:27:41 -070037from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080038from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070039
Mike Frysinger3c831a72020-02-19 02:47:04 -050040
41assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
42
43
Don Garrett60967922017-04-12 18:51:44 -070044# This number should be incremented when we change the layout of the buildroot
45# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070046BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070047_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070048
Don Garrettacbb2392017-05-11 18:27:41 -070049# Metrics reported to Monarch.
Mike Nicholscb29fbd2018-07-24 11:09:33 -060050METRIC_PREFIX = 'chromeos/chromite/cbuildbot_launch/'
51METRIC_ACTIVE = METRIC_PREFIX + 'active'
52METRIC_INVOKED = METRIC_PREFIX + 'invoked'
53METRIC_COMPLETED = METRIC_PREFIX + 'completed'
54METRIC_PREP = METRIC_PREFIX + 'prep_completed'
55METRIC_CLEAN = METRIC_PREFIX + 'clean_buildroot_durations'
56METRIC_INITIAL = METRIC_PREFIX + 'initial_checkout_durations'
57METRIC_CBUILDBOT = METRIC_PREFIX + 'cbuildbot_durations'
58METRIC_CBUILDBOT_INSTANCE = METRIC_PREFIX + 'cbuildbot_instance_durations'
59METRIC_CLOBBER = METRIC_PREFIX + 'clobber'
60METRIC_BRANCH_CLEANUP = METRIC_PREFIX + 'branch_cleanup'
61METRIC_DISTFILES_CLEANUP = METRIC_PREFIX + 'distfiles_cleanup'
62METRIC_CHROOT_CLEANUP = METRIC_PREFIX + 'chroot_cleanup'
Don Garrettacbb2392017-05-11 18:27:41 -070063
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060064# Builder state
65BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
66
Don Garrett60967922017-04-12 18:51:44 -070067
Don Garrett125d4dc2017-04-25 16:26:03 -070068def StageDecorator(functor):
69 """A Decorator that adds buildbot stage tags around a method.
70
Don Garrettacbb2392017-05-11 18:27:41 -070071 It uses the method name as the stage name, and assumes failure on a true
72 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070073 """
74 @functools.wraps(functor)
75 def wrapped_functor(*args, **kwargs):
76 try:
77 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070078 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070079 except Exception:
80 logging.PrintBuildbotStepFailure()
81 raise
82
Don Garrettacbb2392017-05-11 18:27:41 -070083 if result:
84 logging.PrintBuildbotStepFailure()
85 return result
86
Don Garrett125d4dc2017-04-25 16:26:03 -070087 return wrapped_functor
88
89
Don Garrettb5fc08b2017-11-20 21:51:16 +000090def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070091 """Helper for inserting more fields into a metrics fields dictionary.
92
93 Args:
94 fields: Dictionary of metrics fields.
95 kwargs: Each argument is a key/value pair to insert into dict.
96
97 Returns:
98 Copy of original dictionary with kwargs set as fields.
99 """
100 f = fields.copy()
101 f.update(kwargs)
102 return f
103
Don Garretta50bf492017-09-28 18:33:02 -0700104
105def PrependPath(prepend):
106 """Generate path with new directory at the beginning.
107
108 Args:
109 prepend: Directory to add at the beginning of the path.
110
111 Returns:
112 Extended path as a string.
113 """
114 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
115
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000116
Don Garrett86881cb2017-02-15 15:41:55 -0800117def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700118 """Extract the branch name from cbuildbot command line arguments.
119
Don Garrettc4114cc2016-11-01 20:04:06 -0700120 Args:
121 argv: The command line arguments to parse.
122
123 Returns:
124 Branch as a string ('master' if nothing is specified).
125 """
Don Garrett86881cb2017-02-15 15:41:55 -0800126 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400127 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800128
129 if not options.cache_dir:
130 options.cache_dir = os.path.join(options.buildroot,
131 'repository', '.cache')
132
Don Garrettd1d90dd2017-06-13 17:35:52 -0700133 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800134
135 # This option isn't required for cbuildbot, but is for us.
136 if not options.buildroot:
137 cros_build_lib.Die('--buildroot is a required option.')
138
139 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700140
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000141
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600142def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600143 """Extract information about the current build state from command-line args.
144
145 Args:
146 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600147 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600148
149 Returns:
150 A BuildSummary object describing the current build.
151 """
152 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600153 status=constants.BUILDER_STATUS_INFLIGHT,
154 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
155 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600156 if options.buildnumber:
157 build_state.build_number = options.buildnumber
158 if options.buildbucket_id:
159 build_state.buildbucket_id = options.buildbucket_id
160 if options.master_build_id:
161 build_state.master_build_id = options.master_build_id
162 return build_state
163
164
165def GetLastBuildState(root):
166 """Fetch the state of the last build run from |root|.
167
168 If the saved state file can't be read or doesn't contain valid JSON, a default
169 state will be returned.
170
171 Args:
172 root: Root of the working directory tree as a string.
173
174 Returns:
175 A BuildSummary object representing the previous build.
176 """
177 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
178
179 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600180
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600181 try:
182 state_raw = osutils.ReadFile(state_file)
183 state.from_json(state_raw)
184 except IOError as e:
185 logging.warning('Unable to read %s: %s', state_file, e)
186 return state
187 except ValueError as e:
188 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
189 return state
190
191 if not state.is_valid():
192 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600193 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600194
195 return state
196
197
198def SetLastBuildState(root, new_state):
199 """Save the state of the last build under |root|.
200
201 Args:
202 root: Root of the working directory tree as a string.
203 new_state: BuildSummary object containing the state to be saved.
204 """
205 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
206 osutils.WriteFile(state_file, new_state.to_json())
207
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600208 # Remove old state file. Its contents have been migrated into the new file.
209 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
210 osutils.SafeUnlink(old_state_file)
211
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600212
Don Garrett61ce1ee2019-02-26 16:20:25 -0800213def _MaybeCleanDistfiles(cache_dir, distfiles_ts):
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700214 """Cleans the distfiles directory if too old.
215
216 Args:
Don Garrett61ce1ee2019-02-26 16:20:25 -0800217 cache_dir: Directory of the cache, as a string.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700218 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
219 be None.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700220
221 Returns:
222 The new distfiles_ts to persist in state.
223 """
Don Garrette3f1a472018-06-19 13:03:21 -0700224 # distfiles_ts can be None for a fresh environment, which means clean.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700225 if distfiles_ts is None:
Don Garrette3f1a472018-06-19 13:03:21 -0700226 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700227
228 distfiles_age = (time.time() - distfiles_ts) / 3600.0
229 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
230 return distfiles_ts
231
232 logging.info('Remove old distfiles cache (cache expiry %d hours)',
233 _DISTFILES_CACHE_EXPIRY_HOURS)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800234 osutils.RmDir(os.path.join(cache_dir, 'distfiles'),
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700235 ignore_missing=True, sudo=True)
236 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700237 fields=field({}, reason='cache_expired'))
Don Garrette3f1a472018-06-19 13:03:21 -0700238
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700239 # Cleaned cache, so reset distfiles_ts
Don Garrette3f1a472018-06-19 13:03:21 -0700240 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700241
242
Don Garrett61ce1ee2019-02-26 16:20:25 -0800243def SanitizeCacheDir(cache_dir):
244 """Make certain the .cache directory is valid.
245
246 Args:
247 cache_dir: Directory of the cache, as a string.
248 """
249 logging.info('Cleaning up cache dir at %s', cache_dir)
250 # Verify that .cache is writable by the current user.
251 try:
252 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
253 except IOError:
254 logging.info('Bad Permissions for cache dir, wiping: %s', cache_dir)
255 osutils.RmDir(cache_dir, sudo=True)
256 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
257
Don Garrett14a41b32019-02-28 12:54:14 -0800258 osutils.RmDir(os.path.join(cache_dir, 'paygen_cache'),
259 ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800260 logging.info('Finished cleaning cache_dir.')
261
262
Don Garrett125d4dc2017-04-25 16:26:03 -0700263@StageDecorator
Don Garrett61ce1ee2019-02-26 16:20:25 -0800264def CleanBuildRoot(root, repo, cache_dir, build_state):
Don Garrett7ade05a2017-02-17 13:31:47 -0800265 """Some kinds of branch transitions break builds.
266
Don Garrettbf90cdf2017-05-19 15:54:02 -0700267 This method ensures that cbuildbot's buildroot is a clean checkout on the
268 given branch when it starts. If necessary (a branch transition) it will wipe
269 assorted state that cannot be safely reused from the previous build.
Don Garrett7ade05a2017-02-17 13:31:47 -0800270
Don Garrett7ade05a2017-02-17 13:31:47 -0800271 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700272 root: Root directory owned by cbuildbot_launch.
Don Garrettf324bc32017-05-23 14:00:53 -0700273 repo: repository.RepoRepository instance.
Don Garrett61ce1ee2019-02-26 16:20:25 -0800274 cache_dir: Cache directory.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600275 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600276 will be saved into the cleaned root. The distfiles_ts property will
277 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800278 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600279 previous_state = GetLastBuildState(root)
Don Garrett4166d182018-12-17 12:52:02 -0800280 SetLastBuildState(root, build_state)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800281 SanitizeCacheDir(cache_dir)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600282 build_state.distfiles_ts = _MaybeCleanDistfiles(
Don Garrett61ce1ee2019-02-26 16:20:25 -0800283 cache_dir, previous_state.distfiles_ts)
Don Garrette17e1d92017-04-12 15:28:19 -0700284
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600285 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700286 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrettb5fc08b2017-11-20 21:51:16 +0000287 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700288 fields=field({}, reason='layout_change'))
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700289 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000290 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700291 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000292 osutils.RmDir(root, ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800293 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
Don Garrettf324bc32017-05-23 14:00:53 -0700294 else:
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600295 if previous_state.branch != repo.branch:
Don Garrettf324bc32017-05-23 14:00:53 -0700296 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600297 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
298 repo.branch)
Don Garrettacbb2392017-05-11 18:27:41 -0700299 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700300 fields=field({}, old_branch=previous_state.branch))
Don Garrett39963602017-02-27 14:41:58 -0800301
Don Garrettf324bc32017-05-23 14:00:53 -0700302 logging.info('Remove Chroot.')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700303 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
Benjamin Gordon59ba2f82017-08-28 15:31:06 -0600304 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700305 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800306
Don Garrettf324bc32017-05-23 14:00:53 -0700307 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700308 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700309 ignore_missing=True, sudo=True)
310
Don Garrett1241c4e2018-08-06 17:33:41 -0700311 try:
312 # If there is any failure doing the cleanup, wipe everything.
313 # The previous run might have been killed in the middle leaving stale git
314 # locks. Clean those up, first.
315 repo.PreLoad()
Don Garrett4166d182018-12-17 12:52:02 -0800316
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
Don Garrett1241c4e2018-08-06 17:33:41 -0700323 repo.BuildRootGitCleanup(prune_all=True)
324 except Exception:
325 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
326 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700327 fields=field({}, reason='repo_cleanup_failure'))
Don Garrett1241c4e2018-08-06 17:33:41 -0700328 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800329
Don Garrettbf90cdf2017-05-19 15:54:02 -0700330 # Ensure buildroot exists. Save the state we are prepped for.
331 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600332 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800333
334
Don Garrett125d4dc2017-04-25 16:26:03 -0700335@StageDecorator
Mike Nichols9fb48832021-01-29 14:47:15 -0700336def InitialCheckout(repo, options):
Don Garrett86881cb2017-02-15 15:41:55 -0800337 """Preliminary ChromeOS checkout.
338
339 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
340 match what the build needs, but ensures the buildroot both has a 'hot'
341 checkout, and is close enough that the branched cbuildbot can successfully get
342 the right checkout.
343
344 This checks out full ChromeOS, even if a ChromiumOS build is going to be
345 performed. This is because we have no knowledge of the build config to be
346 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700347
348 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700349 repo: repository.RepoRepository instance.
Mike Nichols9fb48832021-01-29 14:47:15 -0700350 options: A parsed options object from a cbuildbot parser.
Don Garrettc4114cc2016-11-01 20:04:06 -0700351 """
Don Garrettf324bc32017-05-23 14:00:53 -0700352 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800353 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700354 repo.branch)
Don Garrett5516acb2018-11-15 16:02:59 -0800355 repo.PreLoad('/preload/chromeos')
Mike Nichols9fb48832021-01-29 14:47:15 -0700356 repo.Sync(detach=True,
357 downgrade_repo=_ShouldDowngradeRepo(options))
Don Garrettc4114cc2016-11-01 20:04:06 -0700358
359
Lann Martin8c4c6802018-05-23 11:09:46 -0600360def ShouldFixBotoCerts(options):
361 """Decide if FixBotoCerts should be applied for this branch."""
362 try:
363 # Only apply to factory and firmware branches.
364 branch = options.branch or ''
365 prefix = branch.split('-')[0]
366 if prefix not in ('factory', 'firmware'):
367 return False
368
369 # Only apply to "old" branches.
370 if branch.endswith('.B'):
371 version = branch[:-2].split('-')[-1]
372 major = int(version.split('.')[0])
373 return major <= 9667 # This is the newest known to be failing.
374
375 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400376 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600377 logging.warning(' failed: %s', e)
378 # Conservatively continue without the fix.
379 return False
380
381
Mike Nichols9fb48832021-01-29 14:47:15 -0700382def _ShouldDowngradeRepo(options):
383 """Determine which repo version to set for the branch.
384
385 Repo version is set at cache creation time, in the nightly builder,
386 which means we are typically at the latest version. Older branches
387 are incompatible with newer version of ToT, therefore we downgrade
388 repo to a known working version.
389
390 Args:
391 options: A parsed options object from a cbuildbot parser.
392
393 Returns:
394 bool of whether to downgrade repo version based on branch.
395 """
396 try:
397 branch = options.branch or ''
398 # Only apply to "old" branches.
399 if branch.endswith('.B'):
400 branch_num = branch[:-2].split('-')[1][1:3]
401 return branch_num <= 79 # This is the newest known to be failing.
402
403 return False
404 except Exception as e:
405 logging.warning(' failed: %s', e)
406 # Conservatively continue without the fix.
407 return False
408
409
Don Garrett066e6f52017-09-28 19:14:01 -0700410@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700411def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700412 """Start cbuildbot in specified directory with all arguments.
413
414 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700415 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700416 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700417 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700418
419 Returns:
420 Return code of cbuildbot as an integer.
421 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700422 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700423
Don Garrettd1d90dd2017-06-13 17:35:52 -0700424 # Fixup buildroot parameter.
425 argv = argv[:]
Mike Frysinger79cca962019-06-13 15:26:53 -0400426 for i, arg in enumerate(argv):
427 if arg in ('-r', '--buildroot'):
428 argv[i + 1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800429
Don Garrettd1d90dd2017-06-13 17:35:52 -0700430 # This filters out command line arguments not supported by older versions
431 # of cbuildbot.
432 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400433 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700434 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800435 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700436 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800437
Don Garretta50bf492017-09-28 18:33:02 -0700438 # We want cbuildbot to use branched depot_tools scripts from our manifest,
439 # so that depot_tools is branched to match cbuildbot.
440 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
441 extra_env = {'PATH': PrependPath(depot_tools_path)}
442
Lann Martinebae73d2018-05-21 17:12:00 -0600443 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600444 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600445
446 with boto_compat.FixBotoCerts(activate=fix_boto):
Mike Frysinger45602c72019-09-22 02:15:11 -0400447 result = cros_build_lib.run(
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500448 cmd, extra_env=extra_env, check=False, cwd=buildroot)
Lann Martinebae73d2018-05-21 17:12:00 -0600449
Don Garrettacbb2392017-05-11 18:27:41 -0700450 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700451
Don Garrett60967922017-04-12 18:51:44 -0700452
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700453@StageDecorator
454def CleanupChroot(buildroot):
455 """Unmount/clean up an image-based chroot without deleting the backing image.
456
457 Args:
458 buildroot: Directory containing the chroot to be cleaned up.
459 """
460 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
461 logging.info('Cleaning up chroot at %s', chroot_dir)
462 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Mike Frysingerf0146252019-09-02 13:31:05 -0400463 try:
464 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
465 except timeout_util.TimeoutError:
466 logging.exception('Cleaning up chroot timed out')
467 # Dump debug info to help https://crbug.com/1000034.
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500468 cros_build_lib.run(['mount'], check=True)
469 cros_build_lib.run(['uname', '-a'], check=True)
470 cros_build_lib.sudo_run(['losetup', '-a'], check=True)
471 cros_build_lib.run(['dmesg'], check=True)
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400472 logging.warning('Assuming the bot is going to reboot, so ignoring this '
473 'failure; see https://crbug.com/1000034')
Mike Frysingerf0146252019-09-02 13:31:05 -0400474
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400475 # NB: We ignore errors at this point because this stage runs last. If the
476 # chroot failed to unmount, we're going to reboot the system once we're done,
477 # and that will implicitly take care of cleaning things up. If the bots stop
478 # rebooting after every run, we'll need to make this fatal all the time.
479 #
480 # TODO(crbug.com/1000034): This should be fatal all the time.
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700481
482
Don Garrettf15d65b2017-04-12 12:39:55 -0700483def ConfigureGlobalEnvironment():
484 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700485 # Set umask to 022 so files created by buildbot are readable.
486 os.umask(0o22)
487
Don Garrett86fec482017-05-17 18:13:33 -0700488 # These variables can interfere with LANG / locale behavior.
489 unwanted_local_vars = [
490 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
491 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
492 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
493 ]
494 for v in unwanted_local_vars:
495 os.environ.pop(v, None)
496
497 # This variable is required for repo sync's to work in all cases.
498 os.environ['LANG'] = 'en_US.UTF-8'
499
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000500
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600501def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700502 """main method of script.
503
504 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600505 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700506 argv: All command line arguments to pass as list of strings.
507
508 Returns:
509 Return code of cbuildbot as an integer.
510 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700511 branchname = options.branch or 'master'
512 root = options.buildroot
513 buildroot = os.path.join(root, 'repository')
Don Garrettb497f552018-07-09 16:01:13 -0700514 workspace = os.path.join(root, 'workspace')
Don Garretta50bf492017-09-28 18:33:02 -0700515 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700516
Ben Pastenec8e4e792018-05-14 20:20:25 +0000517 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600518 with metrics.Presence(METRIC_ACTIVE), \
519 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700520
Ben Pastenec8e4e792018-05-14 20:20:25 +0000521 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600522 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000523 if options.enable_buildbot_tags:
524 logging.EnableBuildbotMarkers()
525 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800526
Ben Pastenec8e4e792018-05-14 20:20:25 +0000527 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600528 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600529 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000530 repo = repository.RepoRepository(manifest_url, buildroot,
531 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000532 git_cache_dir=options.git_cache_dir)
Ben Pastenec8e4e792018-05-14 20:20:25 +0000533 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800534
Ben Pastenec8e4e792018-05-14 20:20:25 +0000535 # Clean up the buildroot to a safe state.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600536 with metrics.SecondsTimer(METRIC_CLEAN):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000537 build_state = GetCurrentBuildState(options, branchname)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800538 CleanBuildRoot(root, repo, options.cache_dir, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700539
Ben Pastenec8e4e792018-05-14 20:20:25 +0000540 # Get a checkout close enough to the branch that cbuildbot can handle it.
541 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600542 with metrics.SecondsTimer(METRIC_INITIAL):
Mike Nichols9fb48832021-01-29 14:47:15 -0700543 InitialCheckout(repo, options)
Don Garrettacbb2392017-05-11 18:27:41 -0700544
Ben Pastenec8e4e792018-05-14 20:20:25 +0000545 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600546 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
547 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000548 if previous_build_state.is_valid():
549 argv.append('--previous-build-state')
Mike Frysinger1a443332019-11-24 02:48:03 -0500550 argv.append(base64.b64encode(previous_build_state.to_json().encode(
551 'utf-8')).decode('utf-8'))
Don Garrettb497f552018-07-09 16:01:13 -0700552 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600553
Don Garrett61ce1ee2019-02-26 16:20:25 -0800554 if not options.cache_dir_specified:
555 argv.extend(['--cache-dir', options.cache_dir])
556
Ben Pastenec8e4e792018-05-14 20:20:25 +0000557 result = Cbuildbot(buildroot, depot_tools_path, argv)
558 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600559
Ben Pastenec8e4e792018-05-14 20:20:25 +0000560 build_state.status = (
561 constants.BUILDER_STATUS_PASSED
562 if result == 0 else constants.BUILDER_STATUS_FAILED)
563 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600564
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600565 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400566 CleanupChroot(buildroot)
Don Garrett91a8cfc2018-06-22 15:39:36 -0700567
Ben Pastenec8e4e792018-05-14 20:20:25 +0000568 return result
569
Don Garrettacbb2392017-05-11 18:27:41 -0700570
571def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600572 options = PreParseArguments(argv)
573 metric_fields = {
574 'branch_name': options.branch or 'master',
575 'build_config': options.build_config_name,
576 'tryjob': options.remote_trybot,
577 }
578
Ben Pastenec8e4e792018-05-14 20:20:25 +0000579 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600580 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
581 common_metric_fields=metric_fields,
582 indirect=True):
583 return _main(options, argv)