1 # Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Module with utilities for archiving functionality."""
7 from __future__ import print_function
11 from chromite.cbuildbot import cbuildbot_config
12 from chromite.cbuildbot import commands
13 from chromite.cbuildbot import constants
15 from chromite.lib import cros_build_lib
16 from chromite.lib import gs
17 from chromite.lib import osutils
20 def GetBaseUploadURI(config, archive_base=None, bot_id=None,
22 """Get the base URL where artifacts from this builder are uploaded.
24 Each build run stores its artifacts in a subdirectory of the base URI.
25 We also have LATEST files under the base URI which help point to the
26 latest build available for a given builder.
29 config: The build config to examine.
30 archive_base: Optional. The root URL under which objects from all
31 builders are uploaded. If not specified, we use the default archive
33 bot_id: The bot ID to archive files under.
34 remote_trybot: Whether this is a remote trybot run. This is used to
35 make sure that uploads from remote trybot runs do not conflict with
36 uploads from production builders.
39 Google Storage URI (i.e. 'gs://...') under which all archived files
40 should be uploaded. In other words, a path like a directory, even
41 through GS has no real directories.
44 bot_id = config.GetBotId(remote_trybot=remote_trybot)
47 return '%s/%s' % (archive_base, bot_id)
48 elif remote_trybot or config.gs_path == cbuildbot_config.GS_PATH_DEFAULT:
49 return '%s/%s' % (constants.DEFAULT_ARCHIVE_BUCKET, bot_id)
54 def GetUploadACL(config):
55 """Get the ACL we should use to upload artifacts for a given config."""
57 # Use the bucket default ACL.
63 class Archive(object):
64 """Class to represent the archive for one builder run.
66 An Archive object is a read-only object with attributes and methods useful
67 for archive purposes. Most of the attributes are supported as properties
68 because they depend on the ChromeOS version and if they are calculated too
69 soon (i.e. before the sync stage) they will raise an exception.
72 archive_path: The full local path where output from this builder is stored.
73 download_url: The URL where we can download artifacts.
74 upload_url: The Google Storage location where we should upload artifacts.
75 version: The ChromeOS version for this archive.
78 _BUILDBOT_ARCHIVE = 'buildbot_archive'
79 _TRYBOT_ARCHIVE = 'trybot_archive'
81 def __init__(self, bot_id, version_getter, options, config):
85 bot_id: The bot id associated with this archive.
86 version_getter: Functor that should return the ChromeOS version for
87 this run when called, if the version is known. Typically, this
88 is BuilderRun.GetVersion.
89 options: The command options object for this run.
90 config: The build config for this run.
92 self._options = options
94 self._version_getter = version_getter
101 if self._version is None:
102 self._version = self._version_getter()
107 def archive_path(self):
108 return os.path.join(self.GetLocalArchiveRoot(), self.bot_id, self.version)
111 def upload_url(self):
112 base_upload_url = GetBaseUploadURI(
114 archive_base=self._options.archive_base,
116 remote_trybot=self._options.remote_trybot)
117 return '%s/%s' % (base_upload_url, self.version)
120 def upload_acl(self):
121 """Get the ACL we should use to upload artifacts for a given config."""
122 return GetUploadACL(self._config)
125 def download_url(self):
126 if self._options.buildbot or self._options.remote_trybot:
127 # Translate the gs:// URI to the URL for downloading the same files.
128 return self.upload_url.replace('gs://', gs.PRIVATE_BASE_HTTPS_URL)
130 return self.archive_path
132 def GetLocalArchiveRoot(self, trybot=None):
133 """Return the location on disk where archive images are kept."""
134 buildroot = os.path.abspath(self._options.buildroot)
137 trybot = not self._options.buildbot or self._options.debug
139 archive_base = self._TRYBOT_ARCHIVE if trybot else self._BUILDBOT_ARCHIVE
140 return os.path.join(buildroot, archive_base)
142 def SetupArchivePath(self):
143 """Create a fresh directory for archiving a build."""
144 cros_build_lib.Info('Preparing local archive directory at "%s".',
146 if self._options.buildbot:
147 # Buildbot: Clear out any leftover build artifacts, if present, for
148 # this particular run. The Clean stage is responsible for trimming
149 # back the number of archive paths to the last X runs.
150 osutils.RmDir(self.archive_path, ignore_missing=True)
152 # Clear the list of uploaded file if it exists. In practice, the Clean
153 # stage deletes everything in the archive root, so this may not be
154 # doing anything at all.
155 osutils.SafeUnlink(os.path.join(self.archive_path,
156 commands.UPLOADED_LIST_FILENAME))
158 osutils.SafeMakedirs(self.archive_path)
160 def UpdateLatestMarkers(self, manifest_branch, debug, upload_urls=None):
161 """Update the LATEST markers in GS archive area.
164 manifest_branch: The name of the branch in the manifest for this run.
165 debug: Boolean debug value for this run.
166 upload_urls: Google storage urls to upload the Latest Markers to.
169 upload_urls = [self.upload_url]
170 # self.version will be one of these forms, shown through examples:
171 # R35-1234.5.6 or R35-1234.5.6-b123. In either case, we want "1234.5.6".
172 version_marker = self.version.split('-')[1]
174 filenames = ('LATEST-%s' % manifest_branch,
175 'LATEST-%s' % version_marker)
176 base_archive_path = os.path.dirname(self.archive_path)
177 base_upload_urls = [os.path.dirname(url) for url in upload_urls]
178 for base_upload_url in base_upload_urls:
179 for filename in filenames:
180 latest_path = os.path.join(base_archive_path, filename)
181 osutils.WriteFile(latest_path, self.version, mode='w')
182 commands.UploadArchivedFile(
183 base_archive_path, [base_upload_url], filename,
184 debug, acl=self.upload_acl)