blob: d0d2ad78f4e319e91e368bfb220782ec1d735038 [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
252 logging.info('Finished cleaning cache_dir.')
253
254
Don Garrett125d4dc2017-04-25 16:26:03 -0700255@StageDecorator
Don Garrett61ce1ee2019-02-26 16:20:25 -0800256def CleanBuildRoot(root, repo, cache_dir, build_state):
Don Garrett7ade05a2017-02-17 13:31:47 -0800257 """Some kinds of branch transitions break builds.
258
Don Garrettbf90cdf2017-05-19 15:54:02 -0700259 This method ensures that cbuildbot's buildroot is a clean checkout on the
260 given branch when it starts. If necessary (a branch transition) it will wipe
261 assorted state that cannot be safely reused from the previous build.
Don Garrett7ade05a2017-02-17 13:31:47 -0800262
Don Garrett7ade05a2017-02-17 13:31:47 -0800263 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700264 root: Root directory owned by cbuildbot_launch.
Don Garrettf324bc32017-05-23 14:00:53 -0700265 repo: repository.RepoRepository instance.
Don Garrett61ce1ee2019-02-26 16:20:25 -0800266 cache_dir: Cache directory.
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600267 build_state: BuildSummary object containing the current build state that
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600268 will be saved into the cleaned root. The distfiles_ts property will
269 be updated if the distfiles cache is cleaned.
Don Garrett7ade05a2017-02-17 13:31:47 -0800270 """
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600271 previous_state = GetLastBuildState(root)
Don Garrett4166d182018-12-17 12:52:02 -0800272 SetLastBuildState(root, build_state)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800273 SanitizeCacheDir(cache_dir)
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600274 build_state.distfiles_ts = _MaybeCleanDistfiles(
Don Garrett61ce1ee2019-02-26 16:20:25 -0800275 cache_dir, previous_state.distfiles_ts)
Don Garrette17e1d92017-04-12 15:28:19 -0700276
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600277 if previous_state.buildroot_layout != BUILDROOT_BUILDROOT_LAYOUT:
Don Garrett125d4dc2017-04-25 16:26:03 -0700278 logging.PrintBuildbotStepText('Unknown layout: Wiping buildroot.')
Don Garrettb5fc08b2017-11-20 21:51:16 +0000279 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700280 fields=field({}, reason='layout_change'))
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700281 chroot_dir = os.path.join(root, constants.DEFAULT_CHROOT_DIR)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000282 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700283 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrettb5fc08b2017-11-20 21:51:16 +0000284 osutils.RmDir(root, ignore_missing=True, sudo=True)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800285 osutils.RmDir(cache_dir, ignore_missing=True, sudo=True)
Don Garrettf324bc32017-05-23 14:00:53 -0700286 else:
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600287 if previous_state.branch != repo.branch:
Don Garrettf324bc32017-05-23 14:00:53 -0700288 logging.PrintBuildbotStepText('Branch change: Cleaning buildroot.')
Benjamin Gordon8b6d4122018-04-26 13:38:39 -0600289 logging.info('Unmatched branch: %s -> %s', previous_state.branch,
290 repo.branch)
Don Garrettacbb2392017-05-11 18:27:41 -0700291 metrics.Counter(METRIC_BRANCH_CLEANUP).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700292 fields=field({}, old_branch=previous_state.branch))
Don Garrett39963602017-02-27 14:41:58 -0800293
Don Garrettf324bc32017-05-23 14:00:53 -0700294 logging.info('Remove Chroot.')
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700295 chroot_dir = os.path.join(repo.directory, constants.DEFAULT_CHROOT_DIR)
Benjamin Gordon59ba2f82017-08-28 15:31:06 -0600296 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700297 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=True)
Don Garrett7ade05a2017-02-17 13:31:47 -0800298
Don Garrettf324bc32017-05-23 14:00:53 -0700299 logging.info('Remove Chrome checkout.')
Don Garrettbf90cdf2017-05-19 15:54:02 -0700300 osutils.RmDir(os.path.join(repo.directory, '.cache', 'distfiles'),
Don Garrettf324bc32017-05-23 14:00:53 -0700301 ignore_missing=True, sudo=True)
302
Don Garrett1241c4e2018-08-06 17:33:41 -0700303 try:
304 # If there is any failure doing the cleanup, wipe everything.
305 # The previous run might have been killed in the middle leaving stale git
306 # locks. Clean those up, first.
307 repo.PreLoad()
Don Garrett4166d182018-12-17 12:52:02 -0800308
309 # If the previous build didn't exit normally, run an expensive step to
310 # cleanup abandoned git locks.
311 if previous_state.status not in (constants.BUILDER_STATUS_FAILED,
312 constants.BUILDER_STATUS_PASSED):
313 repo.CleanStaleLocks()
314
Don Garrett1241c4e2018-08-06 17:33:41 -0700315 repo.BuildRootGitCleanup(prune_all=True)
316 except Exception:
317 logging.info('Checkout cleanup failed, wiping buildroot:', exc_info=True)
318 metrics.Counter(METRIC_CLOBBER).increment(
Dhanya Ganesh0d909202019-02-20 12:56:46 -0700319 fields=field({}, reason='repo_cleanup_failure'))
Don Garrett1241c4e2018-08-06 17:33:41 -0700320 repository.ClearBuildRoot(repo.directory)
Don Garrett39963602017-02-27 14:41:58 -0800321
Don Garrettbf90cdf2017-05-19 15:54:02 -0700322 # Ensure buildroot exists. Save the state we are prepped for.
323 osutils.SafeMakedirs(repo.directory)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600324 SetLastBuildState(root, build_state)
Don Garrett7ade05a2017-02-17 13:31:47 -0800325
326
Don Garrett125d4dc2017-04-25 16:26:03 -0700327@StageDecorator
Don Garrettf324bc32017-05-23 14:00:53 -0700328def InitialCheckout(repo):
Don Garrett86881cb2017-02-15 15:41:55 -0800329 """Preliminary ChromeOS checkout.
330
331 Perform a complete checkout of ChromeOS on the specified branch. This does NOT
332 match what the build needs, but ensures the buildroot both has a 'hot'
333 checkout, and is close enough that the branched cbuildbot can successfully get
334 the right checkout.
335
336 This checks out full ChromeOS, even if a ChromiumOS build is going to be
337 performed. This is because we have no knowledge of the build config to be
338 used.
Don Garrettc4114cc2016-11-01 20:04:06 -0700339
340 Args:
Don Garrettf324bc32017-05-23 14:00:53 -0700341 repo: repository.RepoRepository instance.
Don Garrettc4114cc2016-11-01 20:04:06 -0700342 """
Don Garrettf324bc32017-05-23 14:00:53 -0700343 logging.PrintBuildbotStepText('Branch: %s' % repo.branch)
Don Garrett7ade05a2017-02-17 13:31:47 -0800344 logging.info('Bootstrap script starting initial sync on branch: %s',
Don Garrettf324bc32017-05-23 14:00:53 -0700345 repo.branch)
Don Garrett5516acb2018-11-15 16:02:59 -0800346 repo.PreLoad('/preload/chromeos')
Don Garrett76496912017-05-11 16:59:11 -0700347 repo.Sync(detach=True)
Don Garrettc4114cc2016-11-01 20:04:06 -0700348
349
Lann Martin8c4c6802018-05-23 11:09:46 -0600350def ShouldFixBotoCerts(options):
351 """Decide if FixBotoCerts should be applied for this branch."""
352 try:
353 # Only apply to factory and firmware branches.
354 branch = options.branch or ''
355 prefix = branch.split('-')[0]
356 if prefix not in ('factory', 'firmware'):
357 return False
358
359 # Only apply to "old" branches.
360 if branch.endswith('.B'):
361 version = branch[:-2].split('-')[-1]
362 major = int(version.split('.')[0])
363 return major <= 9667 # This is the newest known to be failing.
364
365 return False
Mike Frysinger9f470262018-08-03 15:09:58 -0400366 except Exception as e:
Lann Martin8c4c6802018-05-23 11:09:46 -0600367 logging.warning(' failed: %s', e)
368 # Conservatively continue without the fix.
369 return False
370
371
Don Garrett066e6f52017-09-28 19:14:01 -0700372@StageDecorator
Don Garrett6e5c6b92018-04-06 17:58:49 -0700373def Cbuildbot(buildroot, depot_tools_path, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700374 """Start cbuildbot in specified directory with all arguments.
375
376 Args:
Don Garrettbf90cdf2017-05-19 15:54:02 -0700377 buildroot: Directory to be passed to cbuildbot with --buildroot.
Don Garretta50bf492017-09-28 18:33:02 -0700378 depot_tools_path: Directory for depot_tools to be used by cbuildbot.
Don Garrettd1d90dd2017-06-13 17:35:52 -0700379 argv: Command line options passed to cbuildbot_launch.
Don Garrettc4114cc2016-11-01 20:04:06 -0700380
381 Returns:
382 Return code of cbuildbot as an integer.
383 """
Don Garrettbf90cdf2017-05-19 15:54:02 -0700384 logging.info('Bootstrap cbuildbot in: %s', buildroot)
Don Garrettbf90cdf2017-05-19 15:54:02 -0700385
Don Garrettd1d90dd2017-06-13 17:35:52 -0700386 # Fixup buildroot parameter.
387 argv = argv[:]
388 for i in xrange(len(argv)):
389 if argv[i] in ('-r', '--buildroot'):
390 argv[i+1] = buildroot
Don Garrett597ddff2017-02-17 18:29:37 -0800391
Don Garrettd1d90dd2017-06-13 17:35:52 -0700392 # This filters out command line arguments not supported by older versions
393 # of cbuildbot.
394 parser = cbuildbot.CreateParser()
Mike Frysinger80bba8a2017-08-18 15:28:36 -0400395 options = cbuildbot.ParseCommandLine(parser, argv)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700396 cbuildbot_path = os.path.join(buildroot, 'chromite', 'bin', 'cbuildbot')
Don Garrett597ddff2017-02-17 18:29:37 -0800397 cmd = sync_stages.BootstrapStage.FilterArgsForTargetCbuildbot(
Don Garrettbf90cdf2017-05-19 15:54:02 -0700398 buildroot, cbuildbot_path, options)
Don Garrett597ddff2017-02-17 18:29:37 -0800399
Don Garretta50bf492017-09-28 18:33:02 -0700400 # We want cbuildbot to use branched depot_tools scripts from our manifest,
401 # so that depot_tools is branched to match cbuildbot.
402 logging.info('Adding depot_tools into PATH: %s', depot_tools_path)
403 extra_env = {'PATH': PrependPath(depot_tools_path)}
404
Lann Martinebae73d2018-05-21 17:12:00 -0600405 # TODO(crbug.com/845304): Remove once underlying boto issues are resolved.
Lann Martin8c4c6802018-05-23 11:09:46 -0600406 fix_boto = ShouldFixBotoCerts(options)
Lann Martinebae73d2018-05-21 17:12:00 -0600407
408 with boto_compat.FixBotoCerts(activate=fix_boto):
409 result = cros_build_lib.RunCommand(
410 cmd, extra_env=extra_env, error_code_ok=True, cwd=buildroot)
411
Don Garrettacbb2392017-05-11 18:27:41 -0700412 return result.returncode
Don Garrettc4114cc2016-11-01 20:04:06 -0700413
Don Garrett60967922017-04-12 18:51:44 -0700414
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700415@StageDecorator
416def CleanupChroot(buildroot):
417 """Unmount/clean up an image-based chroot without deleting the backing image.
418
419 Args:
420 buildroot: Directory containing the chroot to be cleaned up.
421 """
422 chroot_dir = os.path.join(buildroot, constants.DEFAULT_CHROOT_DIR)
423 logging.info('Cleaning up chroot at %s', chroot_dir)
424 if os.path.exists(chroot_dir) or os.path.exists(chroot_dir + '.img'):
Don Garrett36650112018-06-28 15:54:34 -0700425 cros_sdk_lib.CleanupChrootMount(chroot_dir, delete=False)
Benjamin Gordonaee36b82018-02-05 14:25:26 -0700426
427
Don Garrettf15d65b2017-04-12 12:39:55 -0700428def ConfigureGlobalEnvironment():
429 """Setup process wide environmental changes."""
Don Garrettf15d65b2017-04-12 12:39:55 -0700430 # Set umask to 022 so files created by buildbot are readable.
431 os.umask(0o22)
432
Don Garrett86fec482017-05-17 18:13:33 -0700433 # These variables can interfere with LANG / locale behavior.
434 unwanted_local_vars = [
435 'LC_ALL', 'LC_CTYPE', 'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC',
436 'LC_MONETARY', 'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
437 'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
438 ]
439 for v in unwanted_local_vars:
440 os.environ.pop(v, None)
441
442 # This variable is required for repo sync's to work in all cases.
443 os.environ['LANG'] = 'en_US.UTF-8'
444
Aviv Keshetc38eebe2018-09-27 23:19:58 +0000445
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600446def _main(options, argv):
Don Garrettc4114cc2016-11-01 20:04:06 -0700447 """main method of script.
448
449 Args:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600450 options: preparsed options object for the build.
Don Garrettc4114cc2016-11-01 20:04:06 -0700451 argv: All command line arguments to pass as list of strings.
452
453 Returns:
454 Return code of cbuildbot as an integer.
455 """
Don Garrettd1d90dd2017-06-13 17:35:52 -0700456 branchname = options.branch or 'master'
457 root = options.buildroot
458 buildroot = os.path.join(root, 'repository')
Don Garrettb497f552018-07-09 16:01:13 -0700459 workspace = os.path.join(root, 'workspace')
Don Garretta50bf492017-09-28 18:33:02 -0700460 depot_tools_path = os.path.join(buildroot, constants.DEPOT_TOOLS_SUBPATH)
Don Garrettd1d90dd2017-06-13 17:35:52 -0700461
Ben Pastenec8e4e792018-05-14 20:20:25 +0000462 # Does the entire build pass or fail.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600463 with metrics.Presence(METRIC_ACTIVE), \
464 metrics.SuccessCounter(METRIC_COMPLETED) as s_fields:
Don Garrettc4114cc2016-11-01 20:04:06 -0700465
Ben Pastenec8e4e792018-05-14 20:20:25 +0000466 # Preliminary set, mostly command line parsing.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600467 with metrics.SuccessCounter(METRIC_INVOKED):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000468 if options.enable_buildbot_tags:
469 logging.EnableBuildbotMarkers()
470 ConfigureGlobalEnvironment()
Don Garrett86881cb2017-02-15 15:41:55 -0800471
Ben Pastenec8e4e792018-05-14 20:20:25 +0000472 # Prepare the buildroot with source for the build.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600473 with metrics.SuccessCounter(METRIC_PREP):
Alex Klein2ab29cc2018-07-19 12:01:00 -0600474 manifest_url = config_lib.GetSiteParams().MANIFEST_INT_URL
Ben Pastenec8e4e792018-05-14 20:20:25 +0000475 repo = repository.RepoRepository(manifest_url, buildroot,
476 branch=branchname,
Don Garrett33872502018-08-03 22:30:40 +0000477 git_cache_dir=options.git_cache_dir)
Ben Pastenec8e4e792018-05-14 20:20:25 +0000478 previous_build_state = GetLastBuildState(root)
Don Garrett86881cb2017-02-15 15:41:55 -0800479
Ben Pastenec8e4e792018-05-14 20:20:25 +0000480 # Clean up the buildroot to a safe state.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600481 with metrics.SecondsTimer(METRIC_CLEAN):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000482 build_state = GetCurrentBuildState(options, branchname)
Don Garrett61ce1ee2019-02-26 16:20:25 -0800483 CleanBuildRoot(root, repo, options.cache_dir, build_state)
Don Garrettacbb2392017-05-11 18:27:41 -0700484
Ben Pastenec8e4e792018-05-14 20:20:25 +0000485 # Get a checkout close enough to the branch that cbuildbot can handle it.
486 if options.sync:
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600487 with metrics.SecondsTimer(METRIC_INITIAL):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000488 InitialCheckout(repo)
Don Garrettacbb2392017-05-11 18:27:41 -0700489
Ben Pastenec8e4e792018-05-14 20:20:25 +0000490 # Run cbuildbot inside the full ChromeOS checkout, on the specified branch.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600491 with metrics.SecondsTimer(METRIC_CBUILDBOT), \
492 metrics.SecondsInstanceTimer(METRIC_CBUILDBOT_INSTANCE):
Ben Pastenec8e4e792018-05-14 20:20:25 +0000493 if previous_build_state.is_valid():
494 argv.append('--previous-build-state')
495 argv.append(base64.b64encode(previous_build_state.to_json()))
Don Garrettb497f552018-07-09 16:01:13 -0700496 argv.extend(['--workspace', workspace])
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600497
Don Garrett61ce1ee2019-02-26 16:20:25 -0800498 if not options.cache_dir_specified:
499 argv.extend(['--cache-dir', options.cache_dir])
500
Ben Pastenec8e4e792018-05-14 20:20:25 +0000501 result = Cbuildbot(buildroot, depot_tools_path, argv)
502 s_fields['success'] = (result == 0)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600503
Ben Pastenec8e4e792018-05-14 20:20:25 +0000504 build_state.status = (
505 constants.BUILDER_STATUS_PASSED
506 if result == 0 else constants.BUILDER_STATUS_FAILED)
507 SetLastBuildState(root, build_state)
Benjamin Gordon90b2dd92018-04-12 14:04:21 -0600508
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600509 with metrics.SecondsTimer(METRIC_CHROOT_CLEANUP):
Don Garrett91a8cfc2018-06-22 15:39:36 -0700510 CleanupChroot(buildroot)
511
Ben Pastenec8e4e792018-05-14 20:20:25 +0000512 return result
513
Don Garrettacbb2392017-05-11 18:27:41 -0700514
515def main(argv):
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600516 options = PreParseArguments(argv)
517 metric_fields = {
518 'branch_name': options.branch or 'master',
519 'build_config': options.build_config_name,
520 'tryjob': options.remote_trybot,
521 }
522
Ben Pastenec8e4e792018-05-14 20:20:25 +0000523 # Enable Monarch metrics gathering.
Dhanya Ganesh95c5c152018-10-08 16:48:29 -0600524 with ts_mon_config.SetupTsMonGlobalState('cbuildbot_launch',
525 common_metric_fields=metric_fields,
526 indirect=True):
527 return _main(options, argv)