blob: 87168e3580f324c2ed91772a3a82cf4fa84b7c56 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
Evan Hernandezd437b4e2019-03-25 13:48:30 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Portage Binhost operations."""
6
Michael Mortensenfc823882019-08-27 14:38:07 -06007import os
8import shutil
Kevin Shelton703b6882022-01-24 16:31:31 -08009from typing import TYPE_CHECKING
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040010import urllib.parse
Evan Hernandezd437b4e2019-03-25 13:48:30 -060011
Alex Klein231d2da2019-07-22 16:44:45 -060012from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060013from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060014from chromite.api import validate
Alex Kleinaf0e0452019-06-03 18:10:01 -060015from chromite.api.controller import controller_util
Evan Hernandezd437b4e2019-03-25 13:48:30 -060016from chromite.api.gen.chromite.api import binhost_pb2
Greg Edelston724c13d2023-04-07 16:19:24 -060017from chromite.lib import binpkg
LaMont Jonesc64ae212019-04-15 15:41:28 -060018from chromite.lib import constants
19from chromite.lib import cros_build_lib
Evan Hernandezd437b4e2019-03-25 13:48:30 -060020from chromite.lib import gs
Alex Kleinaf0e0452019-06-03 18:10:01 -060021from chromite.lib import sysroot_lib
Evan Hernandezd437b4e2019-03-25 13:48:30 -060022from chromite.service import binhost
23
Mike Frysinger1cc8f1f2022-04-28 22:40:40 -040024
Kevin Shelton703b6882022-01-24 16:31:31 -080025if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060026 from chromite.api import api_config
Mike Frysingeref94e4c2020-02-10 23:59:54 -050027
Alex Kleinaf0e0452019-06-03 18:10:01 -060028_OVERLAY_TYPE_TO_NAME = {
29 binhost_pb2.OVERLAYTYPE_PUBLIC: constants.PUBLIC_OVERLAYS,
30 binhost_pb2.OVERLAYTYPE_PRIVATE: constants.PRIVATE_OVERLAYS,
31 binhost_pb2.OVERLAYTYPE_BOTH: constants.BOTH_OVERLAYS,
Alex Klein1699fab2022-09-08 08:46:06 -060032 binhost_pb2.OVERLAYTYPE_NONE: None,
Alex Kleinaf0e0452019-06-03 18:10:01 -060033}
34
Arif Kasim6242cdd2022-10-19 17:51:00 +000035# Default maximum number of URIs to be stored in Binhost conf file.
36_DEFAULT_BINHOST_MAX_URIS = 1
37
Evan Hernandezd437b4e2019-03-25 13:48:30 -060038
Michael Mortensena0af77b2019-11-13 11:15:15 -070039def _GetBinhostsResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060040 """Add fake binhosts to a successful response."""
41 new_binhost = output_proto.binhosts.add()
42 new_binhost.uri = (
43 "gs://cr-prebuilt/board/amd64-generic/"
44 "paladin-R66-17.0.0-rc2/packages/"
45 )
46 new_binhost.package_index = "Packages"
Michael Mortensena0af77b2019-11-13 11:15:15 -070047
48
49@faux.success(_GetBinhostsResponse)
50@faux.empty_error
Alex Klein1699fab2022-09-08 08:46:06 -060051@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -060052@validate.validation_complete
53def GetBinhosts(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060054 """Get a list of binhosts."""
55 build_target = controller_util.ParseBuildTarget(input_proto.build_target)
Alex Klein7e40d252019-06-10 09:01:32 -060056
Alex Klein1699fab2022-09-08 08:46:06 -060057 binhosts = binhost.GetBinhosts(build_target)
Alex Klein7e40d252019-06-10 09:01:32 -060058
Alex Klein1699fab2022-09-08 08:46:06 -060059 for current in binhosts:
60 new_binhost = output_proto.binhosts.add()
61 new_binhost.uri = current
62 new_binhost.package_index = "Packages"
Alex Klein7e40d252019-06-10 09:01:32 -060063
64
Michael Mortensen42251f92019-11-14 11:01:43 -070065def _GetPrivatePrebuiltAclArgsResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060066 """Add fake acls to a successful response."""
67 new_arg = output_proto.args.add()
68 new_arg.arg = "-g"
69 new_arg.value = "group1:READ"
Michael Mortensen42251f92019-11-14 11:01:43 -070070
71
72@faux.success(_GetPrivatePrebuiltAclArgsResponse)
73@faux.empty_error
Alex Klein1699fab2022-09-08 08:46:06 -060074@validate.require("build_target.name")
Alex Klein231d2da2019-07-22 16:44:45 -060075@validate.validation_complete
76def GetPrivatePrebuiltAclArgs(input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060077 """Get the ACL args from the files in the private overlays."""
78 build_target = controller_util.ParseBuildTarget(input_proto.build_target)
Alex Kleina471f682019-05-31 11:23:45 -060079
Alex Klein1699fab2022-09-08 08:46:06 -060080 try:
81 args = binhost.GetPrebuiltAclArgs(build_target)
82 except binhost.Error as e:
83 cros_build_lib.Die(e)
Alex Kleina471f682019-05-31 11:23:45 -060084
Alex Klein1699fab2022-09-08 08:46:06 -060085 for arg, value in args:
86 new_arg = output_proto.args.add()
87 new_arg.arg = arg
88 new_arg.value = value
Alex Kleina471f682019-05-31 11:23:45 -060089
90
Michael Mortensen42251f92019-11-14 11:01:43 -070091def _PrepareBinhostUploadsResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -060092 """Add fake binhost upload targets to a successful response."""
93 output_proto.uploads_dir = "/upload/directory"
94 output_proto.upload_targets.add().path = "upload_target"
Michael Mortensen42251f92019-11-14 11:01:43 -070095
96
97@faux.success(_PrepareBinhostUploadsResponse)
98@faux.empty_error
Alex Klein1699fab2022-09-08 08:46:06 -060099@validate.require("uri")
Kevin Shelton703b6882022-01-24 16:31:31 -0800100def PrepareBinhostUploads(
101 input_proto: binhost_pb2.PrepareBinhostUploadsRequest,
102 output_proto: binhost_pb2.PrepareBinhostUploadsResponse,
Alex Klein1699fab2022-09-08 08:46:06 -0600103 config: "api_config.ApiConfig",
104):
105 """Return a list of files to upload to the binhost.
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600106
Alex Klein1699fab2022-09-08 08:46:06 -0600107 See BinhostService documentation in api/proto/binhost.proto.
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600108
Alex Klein1699fab2022-09-08 08:46:06 -0600109 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600110 input_proto: The input proto.
111 output_proto: The output proto.
112 config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600113 """
114 if input_proto.sysroot.build_target.name:
115 build_target_msg = input_proto.sysroot.build_target
116 else:
117 build_target_msg = input_proto.build_target
118 sysroot_path = input_proto.sysroot.path
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600119
Alex Klein1699fab2022-09-08 08:46:06 -0600120 if not sysroot_path and not build_target_msg.name:
121 cros_build_lib.Die("Sysroot.path is required.")
Alex Kleinaf0e0452019-06-03 18:10:01 -0600122
Alex Klein1699fab2022-09-08 08:46:06 -0600123 build_target = controller_util.ParseBuildTarget(build_target_msg)
124 chroot = controller_util.ParseChroot(input_proto.chroot)
Alex Kleinaf0e0452019-06-03 18:10:01 -0600125
Alex Klein1699fab2022-09-08 08:46:06 -0600126 if not sysroot_path:
127 sysroot_path = build_target.root
128 sysroot = sysroot_lib.Sysroot(sysroot_path)
Alex Kleinaf0e0452019-06-03 18:10:01 -0600129
Alex Klein1699fab2022-09-08 08:46:06 -0600130 uri = input_proto.uri
131 # For now, we enforce that all input URIs are Google Storage buckets.
132 if not gs.PathIsGs(uri):
133 raise ValueError("Upload URI %s must be Google Storage." % uri)
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600134
Alex Kleina0472ed2022-11-02 12:07:04 -0600135 package_index_paths = [f.path.path for f in input_proto.package_index_files]
136
Alex Klein1699fab2022-09-08 08:46:06 -0600137 if config.validate_only:
138 return controller.RETURN_CODE_VALID_INPUT
Alex Klein231d2da2019-07-22 16:44:45 -0600139
Alex Klein1699fab2022-09-08 08:46:06 -0600140 parsed_uri = urllib.parse.urlparse(uri)
141 upload_uri = gs.GetGsURL(parsed_uri.netloc, for_gsutil=True).rstrip("/")
142 upload_path = parsed_uri.path.lstrip("/")
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600143
Alex Klein1699fab2022-09-08 08:46:06 -0600144 # Read all packages and update the index. The index must be uploaded to the
145 # binhost for Portage to use it, so include it in upload_targets.
146 uploads_dir = binhost.GetPrebuiltsRoot(chroot, sysroot, build_target)
147 index_path = binhost.UpdatePackageIndex(
148 uploads_dir, upload_uri, upload_path, sudo=True
149 )
Alex Kleina0472ed2022-11-02 12:07:04 -0600150 upload_targets = binhost.GetPrebuiltsFiles(
151 uploads_dir, package_index_paths=package_index_paths, sudo=True
152 )
Alex Klein1699fab2022-09-08 08:46:06 -0600153 assert index_path.startswith(
154 uploads_dir
155 ), "expected index_path to start with uploads_dir"
156 upload_targets.append(index_path[len(uploads_dir) :])
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600157
Alex Klein1699fab2022-09-08 08:46:06 -0600158 output_proto.uploads_dir = uploads_dir
159 for upload_target in upload_targets:
160 output_proto.upload_targets.add().path = upload_target.strip("/")
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600161
Alex Klein076841b2019-08-29 15:19:39 -0600162
Alex Klein1699fab2022-09-08 08:46:06 -0600163def _PrepareDevInstallBinhostUploadsResponse(
164 _input_proto, output_proto, _config
165):
166 """Add fake binhost files to a successful response."""
167 output_proto.upload_targets.add().path = "app-arch/zip-3.0-r3.tbz2"
168 output_proto.upload_targets.add().path = "virtual/python-enum34-1.tbz2"
169 output_proto.upload_targets.add().path = "Packages"
Michael Mortensen42251f92019-11-14 11:01:43 -0700170
171
172@faux.success(_PrepareDevInstallBinhostUploadsResponse)
173@faux.empty_error
Alex Klein1699fab2022-09-08 08:46:06 -0600174@validate.require("uri", "sysroot.path")
175@validate.exists("uploads_dir")
Kevin Shelton703b6882022-01-24 16:31:31 -0800176def PrepareDevInstallBinhostUploads(
177 input_proto: binhost_pb2.PrepareDevInstallBinhostUploadsRequest,
178 output_proto: binhost_pb2.PrepareDevInstallBinhostUploadsResponse,
Alex Klein1699fab2022-09-08 08:46:06 -0600179 config: "api_config.ApiConfig",
180):
181 """Return a list of files to upload to the binhost"
Michael Mortensenfc823882019-08-27 14:38:07 -0600182
Alex Klein1699fab2022-09-08 08:46:06 -0600183 The files will also be copied to the uploads_dir.
184 See BinhostService documentation in api/proto/binhost.proto.
Michael Mortensenfc823882019-08-27 14:38:07 -0600185
Alex Klein1699fab2022-09-08 08:46:06 -0600186 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600187 input_proto: The input proto.
188 output_proto: The output proto.
189 config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600190 """
191 sysroot_path = input_proto.sysroot.path
Michael Mortensenfc823882019-08-27 14:38:07 -0600192
Alex Klein1699fab2022-09-08 08:46:06 -0600193 chroot = controller_util.ParseChroot(input_proto.chroot)
194 sysroot = sysroot_lib.Sysroot(sysroot_path)
Michael Mortensenfc823882019-08-27 14:38:07 -0600195
Alex Klein1699fab2022-09-08 08:46:06 -0600196 uri = input_proto.uri
197 # For now, we enforce that all input URIs are Google Storage buckets.
198 if not gs.PathIsGs(uri):
199 raise ValueError("Upload URI %s must be Google Storage." % uri)
Michael Mortensenfc823882019-08-27 14:38:07 -0600200
Alex Klein1699fab2022-09-08 08:46:06 -0600201 if config.validate_only:
202 return controller.RETURN_CODE_VALID_INPUT
Michael Mortensenfc823882019-08-27 14:38:07 -0600203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 parsed_uri = urllib.parse.urlparse(uri)
205 upload_uri = gs.GetGsURL(parsed_uri.netloc, for_gsutil=True).rstrip("/")
206 upload_path = parsed_uri.path.lstrip("/")
Michael Mortensenfc823882019-08-27 14:38:07 -0600207
Alex Klein1699fab2022-09-08 08:46:06 -0600208 # Calculate the filename for the to-be-created Packages file, which will
209 # contain only devinstall packages.
210 devinstall_package_index_path = os.path.join(
211 input_proto.uploads_dir, "Packages"
212 )
213 upload_targets_list = binhost.ReadDevInstallFilesToCreatePackageIndex(
214 chroot, sysroot, devinstall_package_index_path, upload_uri, upload_path
215 )
Michael Mortensenfc823882019-08-27 14:38:07 -0600216
Alex Klein1699fab2022-09-08 08:46:06 -0600217 package_dir = chroot.full_path(sysroot.path, "packages")
218 for upload_target in upload_targets_list:
219 # Copy each package to target/category/package
220 upload_target = upload_target.strip("/")
221 category = upload_target.split(os.sep)[0]
222 target_dir = os.path.join(input_proto.uploads_dir, category)
223 if not os.path.exists(target_dir):
224 os.makedirs(target_dir)
225 full_src_pkg_path = os.path.join(package_dir, upload_target)
226 full_target_src_path = os.path.join(
227 input_proto.uploads_dir, upload_target
228 )
229 shutil.copyfile(full_src_pkg_path, full_target_src_path)
230 output_proto.upload_targets.add().path = upload_target
231 output_proto.upload_targets.add().path = "Packages"
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600232
Alex Klein076841b2019-08-29 15:19:39 -0600233
Greg Edelston724c13d2023-04-07 16:19:24 -0600234def _UpdatePackageIndexResponse(_input_proto, _output_proto, _config):
235 """Set up a fake successful response."""
236
237
238@faux.success(_UpdatePackageIndexResponse)
239@faux.empty_error
240@validate.require("package_index_file")
241@validate.require_any("set_upload_location")
242@validate.validation_complete
243def UpdatePackageIndex(
244 input_proto: binhost_pb2.UpdatePackageIndexRequest,
245 _output_proto: binhost_pb2.UpdatePackageIndexResponse,
246 _config: "api_config.ApiConfig",
247):
248 """Implementation for the BinhostService/UpdatePackageIndex endpoint."""
249 # Load the index file.
250 index_path = controller_util.pb2_path_to_pathlib_path(
251 input_proto.package_index_file,
252 chroot=input_proto.chroot,
253 )
254 pkgindex = binpkg.PackageIndex()
255 pkgindex.ReadFilePath(index_path)
256
257 # Set the upload location for all packages.
258 if input_proto.set_upload_location:
259 if not input_proto.uri:
260 raise ValueError("set_upload_location is True, but no uri provided")
261 parsed_uri = urllib.parse.urlparse(input_proto.uri)
262 pkgindex.SetUploadLocation(
263 gs.GetGsURL(parsed_uri.netloc, for_gsutil=True).rstrip("/"),
264 parsed_uri.path.lstrip("/"),
265 )
266
267 # Write the updated index file back to its original location.
268 pkgindex.WriteFile(index_path)
269
270
Michael Mortensen42251f92019-11-14 11:01:43 -0700271def _SetBinhostResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600272 """Add fake binhost file to a successful response."""
273 output_proto.output_file = "/path/to/BINHOST.conf"
Michael Mortensen42251f92019-11-14 11:01:43 -0700274
275
276@faux.success(_SetBinhostResponse)
277@faux.empty_error
Alex Klein1699fab2022-09-08 08:46:06 -0600278@validate.require("build_target.name", "key", "uri")
Alex Klein231d2da2019-07-22 16:44:45 -0600279@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600280def SetBinhost(
281 input_proto: binhost_pb2.SetBinhostRequest,
282 output_proto: binhost_pb2.SetBinhostResponse,
283 _config: "api_config.ApiConfig",
284):
285 """Set the URI for a given binhost key and build target.
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 See BinhostService documentation in api/proto/binhost.proto.
Evan Hernandezd437b4e2019-03-25 13:48:30 -0600288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600290 input_proto: The input proto.
291 output_proto: The output proto.
292 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600293 """
294 target = input_proto.build_target.name
295 key = binhost_pb2.BinhostKey.Name(input_proto.key)
296 uri = input_proto.uri
297 private = input_proto.private
Arif Kasim6242cdd2022-10-19 17:51:00 +0000298 max_uris = input_proto.max_uris or _DEFAULT_BINHOST_MAX_URIS
Alex Klein6fb0eb82019-05-20 16:16:14 -0600299
Alex Klein1699fab2022-09-08 08:46:06 -0600300 output_proto.output_file = binhost.SetBinhost(
Arif Kasim6242cdd2022-10-19 17:51:00 +0000301 target, key, uri, private=private, max_uris=max_uris
Alex Klein1699fab2022-09-08 08:46:06 -0600302 )
LaMont Jonesc64ae212019-04-15 15:41:28 -0600303
LaMont Jonesc64ae212019-04-15 15:41:28 -0600304
Arif Kasima0467262022-11-11 17:08:14 +0000305def _GetBinhostConfPathResponse(_input_proto, output_proto, _config):
306 """Add fake binhost file to a successful response."""
307 output_proto.conf_path = "/path/to/BINHOST.conf"
308
309
310@faux.success(_GetBinhostConfPathResponse)
311@faux.empty_error
312@validate.require("build_target.name", "key")
313@validate.validation_complete
314def GetBinhostConfPath(
315 input_proto: binhost_pb2.GetBinhostConfPathRequest,
316 output_proto: binhost_pb2.GetBinhostConfPathResponse,
317 _config: "api_config.ApiConfig",
318):
319 target = input_proto.build_target.name
320 key = binhost_pb2.BinhostKey.Name(input_proto.key)
321 private = input_proto.private
322 output_proto.conf_path = str(
323 binhost.GetBinhostConfPath(target, key, private)
324 )
325
326
Michael Mortensen42251f92019-11-14 11:01:43 -0700327def _RegenBuildCacheResponse(_input_proto, output_proto, _config):
Alex Klein1699fab2022-09-08 08:46:06 -0600328 """Add fake binhosts cache path to a successful response."""
329 output_proto.modified_overlays.add().path = "/path/to/BuildCache"
Michael Mortensen42251f92019-11-14 11:01:43 -0700330
331
332@faux.success(_RegenBuildCacheResponse)
333@faux.empty_error
Alex Klein1699fab2022-09-08 08:46:06 -0600334@validate.require("overlay_type")
335@validate.is_in("overlay_type", _OVERLAY_TYPE_TO_NAME)
Alex Klein231d2da2019-07-22 16:44:45 -0600336@validate.validation_complete
Alex Klein1699fab2022-09-08 08:46:06 -0600337def RegenBuildCache(
338 input_proto: binhost_pb2.RegenBuildCacheRequest,
339 output_proto: binhost_pb2.RegenBuildCacheResponse,
340 _config: "api_config.ApiConfig",
341):
342 """Regenerate the Build Cache for a build target.
LaMont Jonesc64ae212019-04-15 15:41:28 -0600343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 See BinhostService documentation in api/proto/binhost.proto.
LaMont Jonesc64ae212019-04-15 15:41:28 -0600345
Alex Klein1699fab2022-09-08 08:46:06 -0600346 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600347 input_proto: The input proto.
348 output_proto: The output proto.
349 _config: The API call config.
Alex Klein1699fab2022-09-08 08:46:06 -0600350 """
351 chroot = controller_util.ParseChroot(input_proto.chroot)
352 overlay_type = input_proto.overlay_type
353 overlays = binhost.RegenBuildCache(
354 chroot, _OVERLAY_TYPE_TO_NAME[overlay_type]
355 )
Alex Klein82c85d42019-08-14 15:47:51 -0600356
Alex Klein1699fab2022-09-08 08:46:06 -0600357 for overlay in overlays:
358 output_proto.modified_overlays.add().path = overlay