blob: dfe797fd49b3b25e92b825b8fc1b7a572302707e [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
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070022import time
Don Garrettc4114cc2016-11-01 20:04:06 -070023
24from chromite.cbuildbot import repository
Don Garrett597ddff2017-02-17 18:29:37 -080025from chromite.cbuildbot.stages import sync_stages
Lann Martinebae73d2018-05-21 17:12:00 -060026from chromite.lib import boto_compat
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060027from chromite.lib import build_summary
Don Garrett86881cb2017-02-15 15:41:55 -080028from chromite.lib import config_lib
Don Garretta50bf492017-09-28 18:33:02 -070029from chromite.lib import constants
Don Garrettc4114cc2016-11-01 20:04:06 -070030from chromite.lib import cros_build_lib
31from chromite.lib import cros_logging as logging
Benjamin Gordon74645232018-05-04 17:40:42 -060032from chromite.lib import cros_sdk_lib
Don Garrettacbb2392017-05-11 18:27:41 -070033from chromite.lib import metrics
Don Garrettc4114cc2016-11-01 20:04:06 -070034from chromite.lib import osutils
Mike Frysingerf0146252019-09-02 13:31:05 -040035from chromite.lib import timeout_util
Don Garrettacbb2392017-05-11 18:27:41 -070036from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080037from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070038
Don Garrett60967922017-04-12 18:51:44 -070039# This number should be incremented when we change the layout of the buildroot
40# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070041BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070042_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070043
Don Garrettacbb2392017-05-11 18:27:41 -070044# Metrics reported to Monarch.
Mike Nicholscb29fbd2018-07-24 11:09:33 -060045METRIC_PREFIX = 'chromeos/chromite/cbuildbot_launch/'
46METRIC_ACTIVE = METRIC_PREFIX + 'active'
47METRIC_INVOKED = METRIC_PREFIX + 'invoked'
48METRIC_COMPLETED = METRIC_PREFIX + 'completed'
49METRIC_PREP = METRIC_PREFIX + 'prep_completed'
50METRIC_CLEAN = METRIC_PREFIX + 'clean_buildroot_durations'
51METRIC_INITIAL = METRIC_PREFIX + 'initial_checkout_durations'
52METRIC_CBUILDBOT = METRIC_PREFIX + 'cbuildbot_durations'
53METRIC_CBUILDBOT_INSTANCE = METRIC_PREFIX + 'cbuildbot_instance_durations'
54METRIC_CLOBBER = METRIC_PREFIX + 'clobber'
55METRIC_BRANCH_CLEANUP = METRIC_PREFIX + 'branch_cleanup'
56METRIC_DISTFILES_CLEANUP = METRIC_PREFIX + 'distfiles_cleanup'
57METRIC_CHROOT_CLEANUP = METRIC_PREFIX + 'chroot_cleanup'
Don Garrettacbb2392017-05-11 18:27:41 -070058
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060059# Builder state
60BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
61
Don Garrett60967922017-04-12 18:51:44 -070062
Don Garrett125d4dc2017-04-25 16:26:03 -070063def StageDecorator(functor):
64 """A Decorator that adds buildbot stage tags around a method.
65
Don Garrettacbb2392017-05-11 18:27:41 -070066 It uses the method name as the stage name, and assumes failure on a true
67 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070068 """
69 @functools.wraps(functor)
70 def wrapped_functor(*args, **kwargs):
71 try:
72 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070073 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070074 except Exception:
75 logging.PrintBuildbotStepFailure()
76 raise
77
Don Garrettacbb2392017-05-11 18:27:41 -070078 if result:
79 logging.PrintBuildbotStepFailure()
80 return result
81
Don Garrett125d4dc2017-04-25 16:26:03 -070082 return wrapped_functor
83
84
Don Garrettb5fc08b2017-11-20 21:51:16 +000085def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070086 """Helper for inserting more fields into a metrics fields dictionary.
87
88 Args:
89 fields: Dictionary of metrics fields.
90 kwargs: Each argument is a key/value pair to insert into dict.
91
92 Returns:
93 Copy of original dictionary with kwargs set as fields.
94 """
95 f = fields.copy()
96 f.update(kwargs)
97 return f
98
Don Garretta50bf492017-09-28 18:33:02 -070099
100def PrependPath(prepend):
101 """Generate path with new directory at the beginning.
102
103 Args:
104 prepend: Directory to add at the beginning of the path.
105
106 Returns:
107 Extended path as a string.
108 """
109 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
110
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000111
Don Garrett86881cb2017-02-15 15:41:55 -0800112def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700113 """Extract the branch name from cbuildbot command line arguments.
114
Don Garrettc4114cc2016-11-01 20:04:06 -0700115 Args:
116 argv: The command line arguments to parse.
117
118 Returns:
119 Branch as a string ('master' if nothing is specified).
120 """
Don Garrett86881cb2017-02-15 15:41:55 -0800121 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400122 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800123
124 if not options.cache_dir:
125 options.cache_dir = os.path.join(options.buildroot,
126 'repository', '.cache')
127
Don Garrettd1d90dd2017-06-13 17:35:52 -0700128 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800129
130 # This option isn't required for cbuildbot, but is for us.
131 if not options.buildroot:
132 cros_build_lib.Die('--buildroot is a required option.')
133
134 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700135
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000136
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600137def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600138 """Extract information about the current build state from command-line args.
139
140 Args:
141 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600142 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600143
144 Returns:
145 A BuildSummary object describing the current build.
146 """
147 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600148 status=constants.BUILDER_STATUS_INFLIGHT,
149 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
150 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600151 if options.buildnumber:
152 build_state.build_number = options.buildnumber
153 if options.buildbucket_id:
154 build_state.buildbucket_id = options.buildbucket_id
155 if options.master_build_id:
156 build_state.master_build_id = options.master_build_id
157 return build_state
158
159
160def GetLastBuildState(root):
161 """Fetch the state of the last build run from |root|.
162
163 If the saved state file can't be read or doesn't contain valid JSON, a default
164 state will be returned.
165
166 Args:
167 root: Root of the working directory tree as a string.
168
169 Returns:
170 A BuildSummary object representing the previous build.
171 """
172 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
173
174 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600175
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600176 try:
177 state_raw = osutils.ReadFile(state_file)
178 state.from_json(state_raw)
179 except IOError as e:
180 logging.warning('Unable to read %s: %s', state_file, e)
181 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
Don Garrett61ce1ee2019-02-26 16:20:25 -0800208def _MaybeCleanDistfiles(cache_dir, distfiles_ts):
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700209 """Cleans the distfiles directory if too old.
210
211 Args:
Don Garrett61ce1ee2019-02-26 16:20:25 -0800212 cache_dir: Directory of the cache, as a string.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700213 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
214 be None.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700215
216 Returns:
217 The new distfiles_ts to persist in state.
218 """
Don Garrette3f1a472018-06-19 13:03:21 -0700219 # distfiles_ts can be None for a fresh environment, which means clean.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700220 if distfiles_ts is None:
Don Garrette3f1a472018-06-19 13:03:21 -0700221 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700222
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)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800229 osutils.RmDir(os.path.join(cache_dir, 'distfiles'),
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700230 ignore_missing=True, sudo=True)
231 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700232 fields=field({}, reason='cache_expired'))
Don Garrette3f1a472018-06-19 13:03:21 -0700233
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700234 # Cleaned cache, so reset distfiles_ts
Don Garrette3f1a472018-06-19 13:03:21 -0700235 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700236
237
Don Garrett61ce1ee2019-02-26 16:20:25 -0800238def 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
Don Garrett14a41b32019-02-28 12:54:14 -0800253 osutils.RmDir(os.path.join(cache_dir, 'paygen_cache'),
254 ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800255 logging.info('Finished cleaning cache_dir.')
256
257
Don Garrett125d4dc2017-04-25 16:26:03 -0700258@StageDecorator
Don Garrett61ce1ee2019-02-26 16:20:25 -0800259def CleanBuildRoot(root, repo, cache_dir, build_state):
Don Garrett7ade05a2017-02-17 13:31:47 -0800260 """Some kinds of branch transitions break builds.
261
Don Garrettbf90cdf2017-05-19 15:54:02 -0700262 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.
Don Garrett7ade05a2017-02-17 13:31:47 -0800265
Don Garrett7ade05a2017-02-17 13:31:47 -0800266 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700267 root: Root directory owned by cbuildbot_launch.
Don Garrettf324bc32017-05-23 14:00:53 -0700268 repo: repository.RepoRepository instance.
Don Garrett61ce1ee2019-02-26 16:20:25 -0800269 cache_dir: Cache directory.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600270 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600271 will be saved into the cleaned root. The distfiles_ts property will
272 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800273 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600274 previous_state = GetLastBuildState(root)
Don Garrett4166d182018-12-17 12:52:02 -0800275 SetLastBuildState(root, build_state)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800276 SanitizeCacheDir(cache_dir)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600277 build_state.distfiles_ts = _MaybeCleanDistfiles(
Don Garrett61ce1ee2019-02-26 16:20:25 -0800278 cache_dir, previous_state.distfiles_ts)
Don Garrette17e1d92017-04-12 15:28:19 -0700279
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600280 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700281 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrettb5fc08b2017-11-20 21:51:16 +0000282 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700283 fields=field({}, reason='layout_change'))
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700284 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000285 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700286 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000287 osutils.RmDir(root, ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800288 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
Don Garrettf324bc32017-05-23 14:00:53 -0700289 else:
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600290 if previous_state.branch != repo.branch:
Don Garrettf324bc32017-05-23 14:00:53 -0700291 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600292 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
293 repo.branch)
Don Garrettacbb2392017-05-11 18:27:41 -0700294 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700295 fields=field({}, old_branch=previous_state.branch))
Don Garrett39963602017-02-27 14:41:58 -0800296
Don Garrettf324bc32017-05-23 14:00:53 -0700297 logging.info('Remove Chroot.')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700298 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
Benjamin Gordon59ba2f82017-08-28 15:31:06 -0600299 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700300 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800301
Don Garrettf324bc32017-05-23 14:00:53 -0700302 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700303 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700304 ignore_missing=True, sudo=True)
305
Don Garrett1241c4e2018-08-06 17:33:41 -0700306 try:
307 # If there is any failure doing the cleanup, wipe everything.
308 # The previous run might have been killed in the middle leaving stale git
309 # locks. Clean those up, first.
310 repo.PreLoad()
Don Garrett4166d182018-12-17 12:52:02 -0800311
312 # If the previous build didn't exit normally, run an expensive step to
313 # cleanup abandoned git locks.
314 if previous_state.status not in (constants.BUILDER_STATUS_FAILED,
315 constants.BUILDER_STATUS_PASSED):
316 repo.CleanStaleLocks()
317
Don Garrett1241c4e2018-08-06 17:33:41 -0700318 repo.BuildRootGitCleanup(prune_all=True)
319 except Exception:
320 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
321 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700322 fields=field({}, reason='repo_cleanup_failure'))
Don Garrett1241c4e2018-08-06 17:33:41 -0700323 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800324
Don Garrettbf90cdf2017-05-19 15:54:02 -0700325 # Ensure buildroot exists. Save the state we are prepped for.
326 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600327 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800328
329
Don Garrett125d4dc2017-04-25 16:26:03 -0700330@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700331def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800332 """Preliminary ChromeOS checkout.
333
334 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
335 match what the build needs, but ensures the buildroot both has a 'hot'
336 checkout, and is close enough that the branched cbuildbot can successfully get
337 the right checkout.
338
339 This checks out full ChromeOS, even if a ChromiumOS build is going to be
340 performed. This is because we have no knowledge of the build config to be
341 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700342
343 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700344 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700345 """
Don Garrettf324bc32017-05-23 14:00:53 -0700346 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800347 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700348 repo.branch)
Don Garrett5516acb2018-11-15 16:02:59 -0800349 repo.PreLoad('/preload/chromeos')
Don Garrett76496912017-05-11 16:59:11 -0700350 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700351
352
Lann Martin8c4c6802018-05-23 11:09:46 -0600353def ShouldFixBotoCerts(options):
354 """Decide if FixBotoCerts should be applied for this branch."""
355 try:
356 # Only apply to factory and firmware branches.
357 branch = options.branch or ''
358 prefix = branch.split('-')[0]
359 if prefix not in ('factory', 'firmware'):
360 return False
361
362 # Only apply to "old" branches.
363 if branch.endswith('.B'):
364 version = branch[:-2].split('-')[-1]
365 major = int(version.split('.')[0])
366 return major <= 9667 # This is the newest known to be failing.
367
368 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400369 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600370 logging.warning(' failed: %s', e)
371 # Conservatively continue without the fix.
372 return False
373
374
Don Garrett066e6f52017-09-28 19:14:01 -0700375@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700376def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700377 """Start cbuildbot in specified directory with all arguments.
378
379 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700380 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700381 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700382 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700383
384 Returns:
385 Return code of cbuildbot as an integer.
386 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700387 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700388
Don Garrettd1d90dd2017-06-13 17:35:52 -0700389 # Fixup buildroot parameter.
390 argv = argv[:]
Mike Frysinger79cca962019-06-13 15:26:53 -0400391 for i, arg in enumerate(argv):
392 if arg in ('-r', '--buildroot'):
393 argv[i + 1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800394
Don Garrettd1d90dd2017-06-13 17:35:52 -0700395 # This filters out command line arguments not supported by older versions
396 # of cbuildbot.
397 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400398 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700399 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800400 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700401 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800402
Don Garretta50bf492017-09-28 18:33:02 -0700403 # We want cbuildbot to use branched depot_tools scripts from our manifest,
404 # so that depot_tools is branched to match cbuildbot.
405 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
406 extra_env = {'PATH': PrependPath(depot_tools_path)}
407
Lann Martinebae73d2018-05-21 17:12:00 -0600408 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600409 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600410
411 with boto_compat.FixBotoCerts(activate=fix_boto):
Mike Frysinger45602c72019-09-22 02:15:11 -0400412 result = cros_build_lib.run(
Lann Martinebae73d2018-05-21 17:12:00 -0600413 cmd, extra_env=extra_env, error_code_ok=True, cwd=buildroot)
414
Don Garrettacbb2392017-05-11 18:27:41 -0700415 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700416
Don Garrett60967922017-04-12 18:51:44 -0700417
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700418@StageDecorator
419def CleanupChroot(buildroot):
420 """Unmount/clean up an image-based chroot without deleting the backing image.
421
422 Args:
423 buildroot: Directory containing the chroot to be cleaned up.
424 """
425 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
426 logging.info('Cleaning up chroot at %s', chroot_dir)
427 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Mike Frysingerf0146252019-09-02 13:31:05 -0400428 try:
429 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
430 except timeout_util.TimeoutError:
431 logging.exception('Cleaning up chroot timed out')
432 # Dump debug info to help https://crbug.com/1000034.
Mike Frysinger45602c72019-09-22 02:15:11 -0400433 cros_build_lib.run(['mount'], error_code_ok=False)
434 cros_build_lib.run(['uname', '-a'], error_code_ok=False)
435 cros_build_lib.sudo_run(['losetup', '-a'], error_code_ok=False)
436 cros_build_lib.run(['dmesg'], error_code_ok=False)
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400437 logging.warning('Assuming the bot is going to reboot, so ignoring this '
438 'failure; see https://crbug.com/1000034')
Mike Frysingerf0146252019-09-02 13:31:05 -0400439
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400440 # NB: We ignore errors at this point because this stage runs last. If the
441 # chroot failed to unmount, we're going to reboot the system once we're done,
442 # and that will implicitly take care of cleaning things up. If the bots stop
443 # rebooting after every run, we'll need to make this fatal all the time.
444 #
445 # TODO(crbug.com/1000034): This should be fatal all the time.
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700446
447
Don Garrettf15d65b2017-04-12 12:39:55 -0700448def ConfigureGlobalEnvironment():
449 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700450 # Set umask to 022 so files created by buildbot are readable.
451 os.umask(0o22)
452
Don Garrett86fec482017-05-17 18:13:33 -0700453 # These variables can interfere with LANG / locale behavior.
454 unwanted_local_vars = [
455 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
456 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
457 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
458 ]
459 for v in unwanted_local_vars:
460 os.environ.pop(v, None)
461
462 # This variable is required for repo sync's to work in all cases.
463 os.environ['LANG'] = 'en_US.UTF-8'
464
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000465
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600466def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700467 """main method of script.
468
469 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600470 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700471 argv: All command line arguments to pass as list of strings.
472
473 Returns:
474 Return code of cbuildbot as an integer.
475 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700476 branchname = options.branch or 'master'
477 root = options.buildroot
478 buildroot = os.path.join(root, 'repository')
Don Garrettb497f552018-07-09 16:01:13 -0700479 workspace = os.path.join(root, 'workspace')
Don Garretta50bf492017-09-28 18:33:02 -0700480 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700481
Ben Pastenec8e4e792018-05-14 20:20:25 +0000482 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600483 with metrics.Presence(METRIC_ACTIVE), \
484 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700485
Ben Pastenec8e4e792018-05-14 20:20:25 +0000486 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600487 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000488 if options.enable_buildbot_tags:
489 logging.EnableBuildbotMarkers()
490 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800491
Ben Pastenec8e4e792018-05-14 20:20:25 +0000492 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600493 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600494 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000495 repo = repository.RepoRepository(manifest_url, buildroot,
496 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000497 git_cache_dir=options.git_cache_dir)
Ben Pastenec8e4e792018-05-14 20:20:25 +0000498 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800499
Ben Pastenec8e4e792018-05-14 20:20:25 +0000500 # Clean up the buildroot to a safe state.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600501 with metrics.SecondsTimer(METRIC_CLEAN):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000502 build_state = GetCurrentBuildState(options, branchname)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800503 CleanBuildRoot(root, repo, options.cache_dir, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700504
Ben Pastenec8e4e792018-05-14 20:20:25 +0000505 # Get a checkout close enough to the branch that cbuildbot can handle it.
506 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600507 with metrics.SecondsTimer(METRIC_INITIAL):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000508 InitialCheckout(repo)
Don Garrettacbb2392017-05-11 18:27:41 -0700509
Ben Pastenec8e4e792018-05-14 20:20:25 +0000510 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600511 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
512 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000513 if previous_build_state.is_valid():
514 argv.append('--previous-build-state')
515 argv.append(base64.b64encode(previous_build_state.to_json()))
Don Garrettb497f552018-07-09 16:01:13 -0700516 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600517
Don Garrett61ce1ee2019-02-26 16:20:25 -0800518 if not options.cache_dir_specified:
519 argv.extend(['--cache-dir', options.cache_dir])
520
Ben Pastenec8e4e792018-05-14 20:20:25 +0000521 result = Cbuildbot(buildroot, depot_tools_path, argv)
522 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600523
Ben Pastenec8e4e792018-05-14 20:20:25 +0000524 build_state.status = (
525 constants.BUILDER_STATUS_PASSED
526 if result == 0 else constants.BUILDER_STATUS_FAILED)
527 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600528
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600529 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Mike Frysinger7a63ee72019-09-03 14:24:06 -0400530 CleanupChroot(buildroot)
Don Garrett91a8cfc2018-06-22 15:39:36 -0700531
Ben Pastenec8e4e792018-05-14 20:20:25 +0000532 return result
533
Don Garrettacbb2392017-05-11 18:27:41 -0700534
535def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600536 options = PreParseArguments(argv)
537 metric_fields = {
538 'branch_name': options.branch or 'master',
539 'build_config': options.build_config_name,
540 'tryjob': options.remote_trybot,
541 }
542
Ben Pastenec8e4e792018-05-14 20:20:25 +0000543 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600544 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
545 common_metric_fields=metric_fields,
546 indirect=True):
547 return _main(options, argv)