blob: 16c4544f84e431d68562c33a875a48edc76003a9 [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
Don Garrettacbb2392017-05-11 18:27:41 -070035from chromite.lib import ts_mon_config
Don Garrett86881cb2017-02-15 15:41:55 -080036from chromite.scripts import cbuildbot
Don Garrettc4114cc2016-11-01 20:04:06 -070037
Don Garrett60967922017-04-12 18:51:44 -070038# This number should be incremented when we change the layout of the buildroot
39# in a non-backwards compatible way. This wipes all buildroots.
Don Garrettbf90cdf2017-05-19 15:54:02 -070040BUILDROOT_BUILDROOT_LAYOUT = 2
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -070041_DISTFILES_CACHE_EXPIRY_HOURS = 8 * 24
Don Garrett60967922017-04-12 18:51:44 -070042
Don Garrettacbb2392017-05-11 18:27:41 -070043# Metrics reported to Monarch.
Mike Nicholscb29fbd2018-07-24 11:09:33 -060044METRIC_PREFIX = 'chromeos/chromite/cbuildbot_launch/'
45METRIC_ACTIVE = METRIC_PREFIX + 'active'
46METRIC_INVOKED = METRIC_PREFIX + 'invoked'
47METRIC_COMPLETED = METRIC_PREFIX + 'completed'
48METRIC_PREP = METRIC_PREFIX + 'prep_completed'
49METRIC_CLEAN = METRIC_PREFIX + 'clean_buildroot_durations'
50METRIC_INITIAL = METRIC_PREFIX + 'initial_checkout_durations'
51METRIC_CBUILDBOT = METRIC_PREFIX + 'cbuildbot_durations'
52METRIC_CBUILDBOT_INSTANCE = METRIC_PREFIX + 'cbuildbot_instance_durations'
53METRIC_CLOBBER = METRIC_PREFIX + 'clobber'
54METRIC_BRANCH_CLEANUP = METRIC_PREFIX + 'branch_cleanup'
55METRIC_DISTFILES_CLEANUP = METRIC_PREFIX + 'distfiles_cleanup'
56METRIC_CHROOT_CLEANUP = METRIC_PREFIX + 'chroot_cleanup'
Don Garrettacbb2392017-05-11 18:27:41 -070057
Benjamin Gordon90b2dd92018-04-12 14:04:21 -060058# Builder state
59BUILDER_STATE_FILENAME = '.cbuildbot_build_state.json'
60
Don Garrett60967922017-04-12 18:51:44 -070061
Don Garrett125d4dc2017-04-25 16:26:03 -070062def StageDecorator(functor):
63 """A Decorator that adds buildbot stage tags around a method.
64
Don Garrettacbb2392017-05-11 18:27:41 -070065 It uses the method name as the stage name, and assumes failure on a true
66 return value, or an exception.
Don Garrett125d4dc2017-04-25 16:26:03 -070067 """
68 @functools.wraps(functor)
69 def wrapped_functor(*args, **kwargs):
70 try:
71 logging.PrintBuildbotStepName(functor.__name__)
Don Garrettacbb2392017-05-11 18:27:41 -070072 result = functor(*args, **kwargs)
Don Garrett125d4dc2017-04-25 16:26:03 -070073 except Exception:
74 logging.PrintBuildbotStepFailure()
75 raise
76
Don Garrettacbb2392017-05-11 18:27:41 -070077 if result:
78 logging.PrintBuildbotStepFailure()
79 return result
80
Don Garrett125d4dc2017-04-25 16:26:03 -070081 return wrapped_functor
82
83
Don Garrettb5fc08b2017-11-20 21:51:16 +000084def field(fields, **kwargs):
Don Garrettacbb2392017-05-11 18:27:41 -070085 """Helper for inserting more fields into a metrics fields dictionary.
86
87 Args:
88 fields: Dictionary of metrics fields.
89 kwargs: Each argument is a key/value pair to insert into dict.
90
91 Returns:
92 Copy of original dictionary with kwargs set as fields.
93 """
94 f = fields.copy()
95 f.update(kwargs)
96 return f
97
Don Garretta50bf492017-09-28 18:33:02 -070098
99def PrependPath(prepend):
100 """Generate path with new directory at the beginning.
101
102 Args:
103 prepend: Directory to add at the beginning of the path.
104
105 Returns:
106 Extended path as a string.
107 """
108 return os.pathsep.join([prepend, os.environ.get('PATH', os.defpath)])
109
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000110
Don Garrett86881cb2017-02-15 15:41:55 -0800111def PreParseArguments(argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700112 """Extract the branch name from cbuildbot command line arguments.
113
Don Garrettc4114cc2016-11-01 20:04:06 -0700114 Args:
115 argv: The command line arguments to parse.
116
117 Returns:
118 Branch as a string ('master' if nothing is specified).
119 """
Don Garrett86881cb2017-02-15 15:41:55 -0800120 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400121 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800122
123 if not options.cache_dir:
124 options.cache_dir = os.path.join(options.buildroot,
125 'repository', '.cache')
126
Don Garrettd1d90dd2017-06-13 17:35:52 -0700127 options.Freeze()
Don Garrett86881cb2017-02-15 15:41:55 -0800128
129 # This option isn't required for cbuildbot, but is for us.
130 if not options.buildroot:
131 cros_build_lib.Die('--buildroot is a required option.')
132
133 return options
Don Garrettc4114cc2016-11-01 20:04:06 -0700134
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000135
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600136def GetCurrentBuildState(options, branch):
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600137 """Extract information about the current build state from command-line args.
138
139 Args:
140 options: A parsed options object from a cbuildbot parser.
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600141 branch: The name of the branch this builder was called with.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600142
143 Returns:
144 A BuildSummary object describing the current build.
145 """
146 build_state = build_summary.BuildSummary(
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600147 status=constants.BUILDER_STATUS_INFLIGHT,
148 buildroot_layout=BUILDROOT_BUILDROOT_LAYOUT,
149 branch=branch)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600150 if options.buildnumber:
151 build_state.build_number = options.buildnumber
152 if options.buildbucket_id:
153 build_state.buildbucket_id = options.buildbucket_id
154 if options.master_build_id:
155 build_state.master_build_id = options.master_build_id
156 return build_state
157
158
159def GetLastBuildState(root):
160 """Fetch the state of the last build run from |root|.
161
162 If the saved state file can't be read or doesn't contain valid JSON, a default
163 state will be returned.
164
165 Args:
166 root: Root of the working directory tree as a string.
167
168 Returns:
169 A BuildSummary object representing the previous build.
170 """
171 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
172
173 state = build_summary.BuildSummary()
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600174
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600175 try:
176 state_raw = osutils.ReadFile(state_file)
177 state.from_json(state_raw)
178 except IOError as e:
179 logging.warning('Unable to read %s: %s', state_file, e)
180 return state
181 except ValueError as e:
182 logging.warning('Saved state file %s is not valid JSON: %s', state_file, e)
183 return state
184
185 if not state.is_valid():
186 logging.warning('Previous build state is not valid. Ignoring.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600187 state = build_summary.BuildSummary()
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600188
189 return state
190
191
192def SetLastBuildState(root, new_state):
193 """Save the state of the last build under |root|.
194
195 Args:
196 root: Root of the working directory tree as a string.
197 new_state: BuildSummary object containing the state to be saved.
198 """
199 state_file = os.path.join(root, BUILDER_STATE_FILENAME)
200 osutils.WriteFile(state_file, new_state.to_json())
201
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600202 # Remove old state file. Its contents have been migrated into the new file.
203 old_state_file = os.path.join(root, '.cbuildbot_launch_state')
204 osutils.SafeUnlink(old_state_file)
205
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600206
Don Garrett61ce1ee2019-02-26 16:20:25 -0800207def _MaybeCleanDistfiles(cache_dir, distfiles_ts):
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700208 """Cleans the distfiles directory if too old.
209
210 Args:
Don Garrett61ce1ee2019-02-26 16:20:25 -0800211 cache_dir: Directory of the cache, as a string.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700212 distfiles_ts: A timestamp str for the last time distfiles was cleaned. May
213 be None.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700214
215 Returns:
216 The new distfiles_ts to persist in state.
217 """
Don Garrette3f1a472018-06-19 13:03:21 -0700218 # distfiles_ts can be None for a fresh environment, which means clean.
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700219 if distfiles_ts is None:
Don Garrette3f1a472018-06-19 13:03:21 -0700220 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700221
222 distfiles_age = (time.time() - distfiles_ts) / 3600.0
223 if distfiles_age < _DISTFILES_CACHE_EXPIRY_HOURS:
224 return distfiles_ts
225
226 logging.info('Remove old distfiles cache (cache expiry %d hours)',
227 _DISTFILES_CACHE_EXPIRY_HOURS)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800228 osutils.RmDir(os.path.join(cache_dir, 'distfiles'),
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700229 ignore_missing=True, sudo=True)
230 metrics.Counter(METRIC_DISTFILES_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700231 fields=field({}, reason='cache_expired'))
Don Garrette3f1a472018-06-19 13:03:21 -0700232
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700233 # Cleaned cache, so reset distfiles_ts
Don Garrette3f1a472018-06-19 13:03:21 -0700234 return time.time()
Prathmesh Prabhuc41a0f52018-04-03 13:26:52 -0700235
236
Don Garrett61ce1ee2019-02-26 16:20:25 -0800237def SanitizeCacheDir(cache_dir):
238 """Make certain the .cache directory is valid.
239
240 Args:
241 cache_dir: Directory of the cache, as a string.
242 """
243 logging.info('Cleaning up cache dir at %s', cache_dir)
244 # Verify that .cache is writable by the current user.
245 try:
246 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
247 except IOError:
248 logging.info('Bad Permissions for cache dir, wiping: %s', cache_dir)
249 osutils.RmDir(cache_dir, sudo=True)
250 osutils.Touch(os.path.join(cache_dir, '.cbuildbot_launch'), makedirs=True)
251
Don Garrett14a41b32019-02-28 12:54:14 -0800252 osutils.RmDir(os.path.join(cache_dir, 'paygen_cache'),
253 ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800254 logging.info('Finished cleaning cache_dir.')
255
256
Don Garrett125d4dc2017-04-25 16:26:03 -0700257@StageDecorator
Don Garrett61ce1ee2019-02-26 16:20:25 -0800258def CleanBuildRoot(root, repo, cache_dir, build_state):
Don Garrett7ade05a2017-02-17 13:31:47 -0800259 """Some kinds of branch transitions break builds.
260
Don Garrettbf90cdf2017-05-19 15:54:02 -0700261 This method ensures that cbuildbot's buildroot is a clean checkout on the
262 given branch when it starts. If necessary (a branch transition) it will wipe
263 assorted state that cannot be safely reused from the previous build.
Don Garrett7ade05a2017-02-17 13:31:47 -0800264
Don Garrett7ade05a2017-02-17 13:31:47 -0800265 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700266 root: Root directory owned by cbuildbot_launch.
Don Garrettf324bc32017-05-23 14:00:53 -0700267 repo: repository.RepoRepository instance.
Don Garrett61ce1ee2019-02-26 16:20:25 -0800268 cache_dir: Cache directory.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600269 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600270 will be saved into the cleaned root. The distfiles_ts property will
271 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800272 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600273 previous_state = GetLastBuildState(root)
Don Garrett4166d182018-12-17 12:52:02 -0800274 SetLastBuildState(root, build_state)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800275 SanitizeCacheDir(cache_dir)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600276 build_state.distfiles_ts = _MaybeCleanDistfiles(
Don Garrett61ce1ee2019-02-26 16:20:25 -0800277 cache_dir, previous_state.distfiles_ts)
Don Garrette17e1d92017-04-12 15:28:19 -0700278
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600279 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700280 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrettb5fc08b2017-11-20 21:51:16 +0000281 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700282 fields=field({}, reason='layout_change'))
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700283 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000284 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700285 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000286 osutils.RmDir(root, ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800287 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
Don Garrettf324bc32017-05-23 14:00:53 -0700288 else:
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600289 if previous_state.branch != repo.branch:
Don Garrettf324bc32017-05-23 14:00:53 -0700290 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600291 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
292 repo.branch)
Don Garrettacbb2392017-05-11 18:27:41 -0700293 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700294 fields=field({}, old_branch=previous_state.branch))
Don Garrett39963602017-02-27 14:41:58 -0800295
Don Garrettf324bc32017-05-23 14:00:53 -0700296 logging.info('Remove Chroot.')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700297 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
Benjamin Gordon59ba2f82017-08-28 15:31:06 -0600298 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700299 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800300
Don Garrettf324bc32017-05-23 14:00:53 -0700301 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700302 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700303 ignore_missing=True, sudo=True)
304
Don Garrett1241c4e2018-08-06 17:33:41 -0700305 try:
306 # If there is any failure doing the cleanup, wipe everything.
307 # The previous run might have been killed in the middle leaving stale git
308 # locks. Clean those up, first.
309 repo.PreLoad()
Don Garrett4166d182018-12-17 12:52:02 -0800310
311 # If the previous build didn't exit normally, run an expensive step to
312 # cleanup abandoned git locks.
313 if previous_state.status not in (constants.BUILDER_STATUS_FAILED,
314 constants.BUILDER_STATUS_PASSED):
315 repo.CleanStaleLocks()
316
Don Garrett1241c4e2018-08-06 17:33:41 -0700317 repo.BuildRootGitCleanup(prune_all=True)
318 except Exception:
319 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
320 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700321 fields=field({}, reason='repo_cleanup_failure'))
Don Garrett1241c4e2018-08-06 17:33:41 -0700322 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800323
Don Garrettbf90cdf2017-05-19 15:54:02 -0700324 # Ensure buildroot exists. Save the state we are prepped for.
325 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600326 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800327
328
Don Garrett125d4dc2017-04-25 16:26:03 -0700329@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700330def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800331 """Preliminary ChromeOS checkout.
332
333 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
334 match what the build needs, but ensures the buildroot both has a 'hot'
335 checkout, and is close enough that the branched cbuildbot can successfully get
336 the right checkout.
337
338 This checks out full ChromeOS, even if a ChromiumOS build is going to be
339 performed. This is because we have no knowledge of the build config to be
340 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700341
342 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700343 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700344 """
Don Garrettf324bc32017-05-23 14:00:53 -0700345 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800346 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700347 repo.branch)
Don Garrett5516acb2018-11-15 16:02:59 -0800348 repo.PreLoad('/preload/chromeos')
Don Garrett76496912017-05-11 16:59:11 -0700349 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700350
351
Lann Martin8c4c6802018-05-23 11:09:46 -0600352def ShouldFixBotoCerts(options):
353 """Decide if FixBotoCerts should be applied for this branch."""
354 try:
355 # Only apply to factory and firmware branches.
356 branch = options.branch or ''
357 prefix = branch.split('-')[0]
358 if prefix not in ('factory', 'firmware'):
359 return False
360
361 # Only apply to "old" branches.
362 if branch.endswith('.B'):
363 version = branch[:-2].split('-')[-1]
364 major = int(version.split('.')[0])
365 return major <= 9667 # This is the newest known to be failing.
366
367 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400368 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600369 logging.warning(' failed: %s', e)
370 # Conservatively continue without the fix.
371 return False
372
373
Don Garrett066e6f52017-09-28 19:14:01 -0700374@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700375def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700376 """Start cbuildbot in specified directory with all arguments.
377
378 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700379 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700380 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700381 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700382
383 Returns:
384 Return code of cbuildbot as an integer.
385 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700386 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700387
Don Garrettd1d90dd2017-06-13 17:35:52 -0700388 # Fixup buildroot parameter.
389 argv = argv[:]
Mike Frysinger79cca962019-06-13 15:26:53 -0400390 for i, arg in enumerate(argv):
391 if arg in ('-r', '--buildroot'):
392 argv[i + 1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800393
Don Garrettd1d90dd2017-06-13 17:35:52 -0700394 # This filters out command line arguments not supported by older versions
395 # of cbuildbot.
396 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400397 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700398 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800399 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700400 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800401
Don Garretta50bf492017-09-28 18:33:02 -0700402 # We want cbuildbot to use branched depot_tools scripts from our manifest,
403 # so that depot_tools is branched to match cbuildbot.
404 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
405 extra_env = {'PATH': PrependPath(depot_tools_path)}
406
Lann Martinebae73d2018-05-21 17:12:00 -0600407 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600408 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600409
410 with boto_compat.FixBotoCerts(activate=fix_boto):
411 result = cros_build_lib.RunCommand(
412 cmd, extra_env=extra_env, error_code_ok=True, cwd=buildroot)
413
Don Garrettacbb2392017-05-11 18:27:41 -0700414 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700415
Don Garrett60967922017-04-12 18:51:44 -0700416
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700417@StageDecorator
418def CleanupChroot(buildroot):
419 """Unmount/clean up an image-based chroot without deleting the backing image.
420
421 Args:
422 buildroot: Directory containing the chroot to be cleaned up.
423 """
424 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
425 logging.info('Cleaning up chroot at %s', chroot_dir)
426 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700427 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700428
429
Don Garrettf15d65b2017-04-12 12:39:55 -0700430def ConfigureGlobalEnvironment():
431 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700432 # Set umask to 022 so files created by buildbot are readable.
433 os.umask(0o22)
434
Don Garrett86fec482017-05-17 18:13:33 -0700435 # These variables can interfere with LANG / locale behavior.
436 unwanted_local_vars = [
437 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
438 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
439 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
440 ]
441 for v in unwanted_local_vars:
442 os.environ.pop(v, None)
443
444 # This variable is required for repo sync's to work in all cases.
445 os.environ['LANG'] = 'en_US.UTF-8'
446
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000447
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600448def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700449 """main method of script.
450
451 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600452 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700453 argv: All command line arguments to pass as list of strings.
454
455 Returns:
456 Return code of cbuildbot as an integer.
457 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700458 branchname = options.branch or 'master'
459 root = options.buildroot
460 buildroot = os.path.join(root, 'repository')
Don Garrettb497f552018-07-09 16:01:13 -0700461 workspace = os.path.join(root, 'workspace')
Don Garretta50bf492017-09-28 18:33:02 -0700462 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700463
Ben Pastenec8e4e792018-05-14 20:20:25 +0000464 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600465 with metrics.Presence(METRIC_ACTIVE), \
466 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700467
Ben Pastenec8e4e792018-05-14 20:20:25 +0000468 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600469 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000470 if options.enable_buildbot_tags:
471 logging.EnableBuildbotMarkers()
472 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800473
Ben Pastenec8e4e792018-05-14 20:20:25 +0000474 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600475 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600476 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000477 repo = repository.RepoRepository(manifest_url, buildroot,
478 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000479 git_cache_dir=options.git_cache_dir)
Ben Pastenec8e4e792018-05-14 20:20:25 +0000480 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800481
Ben Pastenec8e4e792018-05-14 20:20:25 +0000482 # Clean up the buildroot to a safe state.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600483 with metrics.SecondsTimer(METRIC_CLEAN):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000484 build_state = GetCurrentBuildState(options, branchname)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800485 CleanBuildRoot(root, repo, options.cache_dir, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700486
Ben Pastenec8e4e792018-05-14 20:20:25 +0000487 # Get a checkout close enough to the branch that cbuildbot can handle it.
488 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600489 with metrics.SecondsTimer(METRIC_INITIAL):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000490 InitialCheckout(repo)
Don Garrettacbb2392017-05-11 18:27:41 -0700491
Ben Pastenec8e4e792018-05-14 20:20:25 +0000492 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600493 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
494 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000495 if previous_build_state.is_valid():
496 argv.append('--previous-build-state')
497 argv.append(base64.b64encode(previous_build_state.to_json()))
Don Garrettb497f552018-07-09 16:01:13 -0700498 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600499
Don Garrett61ce1ee2019-02-26 16:20:25 -0800500 if not options.cache_dir_specified:
501 argv.extend(['--cache-dir', options.cache_dir])
502
Ben Pastenec8e4e792018-05-14 20:20:25 +0000503 result = Cbuildbot(buildroot, depot_tools_path, argv)
504 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600505
Ben Pastenec8e4e792018-05-14 20:20:25 +0000506 build_state.status = (
507 constants.BUILDER_STATUS_PASSED
508 if result == 0 else constants.BUILDER_STATUS_FAILED)
509 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600510
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600511 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Don Garrett91a8cfc2018-06-22 15:39:36 -0700512 CleanupChroot(buildroot)
513
Ben Pastenec8e4e792018-05-14 20:20:25 +0000514 return result
515
Don Garrettacbb2392017-05-11 18:27:41 -0700516
517def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600518 options = PreParseArguments(argv)
519 metric_fields = {
520 'branch_name': options.branch or 'master',
521 'build_config': options.build_config_name,
522 'tryjob': options.remote_trybot,
523 }
524
Ben Pastenec8e4e792018-05-14 20:20:25 +0000525 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600526 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
527 common_metric_fields=metric_fields,
528 indirect=True):
529 return _main(options, argv)