blob: 8f4b330adac6f83dab77ba64a7230ece7998a486 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2019 The ChromiumOS Authors
George Engelbrechtfe63c8c2019-08-31 22:51:29 -06002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Payload API Service."""
6
Benjamin Shai3c1a2322023-02-10 17:29:46 +00007from typing import Dict, Tuple, TYPE_CHECKING
Kevin Shelton6fa94862022-04-08 20:46:23 -07008
George Engelbrechtfe63c8c2019-08-31 22:51:29 -06009from chromite.api import controller
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060010from chromite.api import faux
11from chromite.api import validate
Brian Norrisdd2e7e62023-06-16 14:07:32 -070012from chromite.api.controller import controller_util
Greg Edelston629468c2022-02-11 14:54:56 -070013from chromite.api.gen.chromite.api import payload_pb2
Brian Norris351745f2023-08-25 15:23:38 -070014from chromite.api.gen.chromiumos import common_pb2
Greg Edelston629468c2022-02-11 14:54:56 -070015from chromite.lib import cros_build_lib
16from chromite.lib.paygen import paygen_payload_lib
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060017from chromite.service import payload
18
19
Kevin Shelton6fa94862022-04-08 20:46:23 -070020if TYPE_CHECKING:
Alex Klein1699fab2022-09-08 08:46:06 -060021 from chromite.api import api_config
Kevin Shelton6fa94862022-04-08 20:46:23 -070022
Alex Klein1699fab2022-09-08 08:46:06 -060023_VALID_IMAGE_PAIRS = (
24 ("src_signed_image", "tgt_signed_image"),
25 ("src_unsigned_image", "tgt_unsigned_image"),
26 ("src_dlc_image", "tgt_dlc_image"),
27 ("full_update", "tgt_unsigned_image"),
28 ("full_update", "tgt_signed_image"),
29 ("full_update", "tgt_dlc_image"),
30)
31_VALID_MINIOS_PAIRS = (
32 ("src_signed_image", "tgt_signed_image"),
33 ("src_unsigned_image", "tgt_unsigned_image"),
34 ("full_update", "tgt_unsigned_image"),
35 ("full_update", "tgt_signed_image"),
36)
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060037
Alex Kleinc07a48b2022-08-26 15:58:44 -060038# TODO: Remove to use the standard cache directory if possible, otherwise
39# document why it cannot be used and preferably move outside of the repo.
Alex Klein1699fab2022-09-08 08:46:06 -060040_DEFAULT_PAYGEN_CACHE_DIR = ".paygen_cache"
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060041
Mike Frysinger16474792023-03-01 01:18:00 -050042
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060043# We have more fields we might validate however, they're either
44# 'oneof' or allowed to be the empty value by design. If @validate
45# gets more complex in the future we can add more here.
Michael Mortensen85d38402019-12-12 09:50:29 -070046@faux.empty_success
47@faux.empty_completed_unsuccessfully_error
Alex Klein1699fab2022-09-08 08:46:06 -060048@validate.require("bucket")
49def GeneratePayload(
50 input_proto: payload_pb2.GenerationRequest,
51 output_proto: payload_pb2.GenerationResponse,
52 config: "api_config.ApiConfig",
53) -> int:
54 """Generate a update payload ('do paygen').
George Engelbrechtd2348bb2019-11-25 21:18:14 -070055
Alex Klein1699fab2022-09-08 08:46:06 -060056 Args:
Alex Klein611dddd2022-10-11 17:02:01 -060057 input_proto: Input proto.
58 output_proto: Output proto.
59 config: The API call config.
George Engelbrechtd2348bb2019-11-25 21:18:14 -070060
Alex Klein1699fab2022-09-08 08:46:06 -060061 Returns:
Alex Klein611dddd2022-10-11 17:02:01 -060062 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
Alex Klein1699fab2022-09-08 08:46:06 -060063 """
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060064
Alex Klein1699fab2022-09-08 08:46:06 -060065 # Resolve the tgt image oneof.
66 tgt_name = input_proto.WhichOneof("tgt_image_oneof")
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060067 try:
Alex Klein1699fab2022-09-08 08:46:06 -060068 tgt_image = getattr(input_proto, tgt_name)
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060069 except AttributeError:
Alex Kleindf8ee502022-10-18 09:48:15 -060070 cros_build_lib.Die("%s is not a known tgt image type", tgt_name)
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060071
Alex Klein1699fab2022-09-08 08:46:06 -060072 # Resolve the src image oneof.
73 src_name = input_proto.WhichOneof("src_image_oneof")
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060074
Alex Klein1699fab2022-09-08 08:46:06 -060075 # If the source image is 'full_update' we lack a source entirely.
76 if src_name == "full_update":
77 src_image = None
78 # Otherwise we have an image.
79 else:
80 try:
81 src_image = getattr(input_proto, src_name)
82 except AttributeError:
Alex Kleindf8ee502022-10-18 09:48:15 -060083 cros_build_lib.Die("%s is not a known src image type", src_name)
Greg Edelston629468c2022-02-11 14:54:56 -070084
Alex Klein1699fab2022-09-08 08:46:06 -060085 # Ensure they are compatible oneofs.
86 if (src_name, tgt_name) not in _VALID_IMAGE_PAIRS:
87 cros_build_lib.Die(
Alex Kleindf8ee502022-10-18 09:48:15 -060088 "%s and %s are not valid image pairs", src_image, tgt_image
Alex Klein1699fab2022-09-08 08:46:06 -060089 )
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060090
Alex Klein1699fab2022-09-08 08:46:06 -060091 # Ensure that miniOS payloads are only requested for compatible image types.
92 if input_proto.minios and (src_name, tgt_name) not in _VALID_MINIOS_PAIRS:
93 cros_build_lib.Die(
Alex Kleindf8ee502022-10-18 09:48:15 -060094 "%s and %s are not valid image pairs for miniOS",
95 src_image,
96 tgt_image,
Alex Klein1699fab2022-09-08 08:46:06 -060097 )
George Engelbrechtfe63c8c2019-08-31 22:51:29 -060098
Jack Neusaeb58a42023-09-26 21:05:20 +000099 if input_proto.use_local_signing and not input_proto.docker_image:
100 cros_build_lib.Die(
101 "local signing enabled but no docker image specified"
102 )
103
Alex Klein1699fab2022-09-08 08:46:06 -0600104 # Find the value of bucket or default to 'chromeos-releases'.
105 destination_bucket = input_proto.bucket or "chromeos-releases"
George Engelbrechtfe63c8c2019-08-31 22:51:29 -0600106
Brian Norrisdd2e7e62023-06-16 14:07:32 -0700107 chroot = controller_util.ParseChroot(input_proto.chroot)
108
Alex Kleinab87ceb2023-01-24 12:00:51 -0700109 # There's a potential that some paygen_lib library might raise here, but
110 # since we're still involved in config we'll keep it before the
111 # validate_only.
Alex Klein1699fab2022-09-08 08:46:06 -0600112 payload_config = payload.PayloadConfig(
Brian Norrisdd2e7e62023-06-16 14:07:32 -0700113 chroot,
Alex Klein1699fab2022-09-08 08:46:06 -0600114 tgt_image,
115 src_image,
116 destination_bucket,
117 input_proto.minios,
118 input_proto.verify,
119 upload=not input_proto.dryrun,
120 cache_dir=_DEFAULT_PAYGEN_CACHE_DIR,
121 )
George Engelbrechtfe63c8c2019-08-31 22:51:29 -0600122
Alex Klein1699fab2022-09-08 08:46:06 -0600123 # If configured for validation only we're done here.
124 if config.validate_only:
125 return controller.RETURN_CODE_VALID_INPUT
126
127 # Do payload generation.
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000128 artifacts = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600129 try:
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000130 artifacts = payload_config.GeneratePayload()
Alex Klein1699fab2022-09-08 08:46:06 -0600131 except paygen_payload_lib.PayloadGenerationSkippedException as e:
132 # If paygen was skipped, provide a reason if possible.
Jack Neus2577e2d2023-04-06 16:14:01 +0000133 if isinstance(e, paygen_payload_lib.MiniOSException):
134 reason = e.return_code()
Alex Klein1699fab2022-09-08 08:46:06 -0600135 output_proto.failure_reason = reason
136
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000137 _SetGeneratePayloadOutputProto(output_proto, artifacts)
138 if _SuccessfulPaygen(artifacts, input_proto.dryrun):
Alex Klein1699fab2022-09-08 08:46:06 -0600139 return controller.RETURN_CODE_SUCCESS
140 elif output_proto.failure_reason:
141 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
142 else:
143 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
George Engelbrechtfe63c8c2019-08-31 22:51:29 -0600144
145
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000146def _SuccessfulPaygen(
147 artifacts: Dict[int, Tuple[str, str]], dryrun: bool
148) -> bool:
149 """Check to see if the payload generation was successful.
150
151 Args:
152 artifacts: a dict containing an artifact tuple keyed by its
153 version. Artifacts tuple is (local_path, remote_uri).
154 dryrun: whether or not this was a dry run job.
155 """
156 if not artifacts:
157 return False
158 for _, artifact in artifacts.items():
159 if not (artifact[1] or dryrun and artifact[0]):
160 return False
161 return True
162
163
Alex Klein1699fab2022-09-08 08:46:06 -0600164def _SetGeneratePayloadOutputProto(
165 output_proto: payload_pb2.GenerationResponse,
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000166 artifacts: Dict[int, Tuple[str, str]],
Alex Klein1699fab2022-09-08 08:46:06 -0600167):
168 """Set the output proto with the results from the service class.
George Engelbrechtd2348bb2019-11-25 21:18:14 -0700169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 Args:
Alex Klein611dddd2022-10-11 17:02:01 -0600171 output_proto: The output proto.
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000172 artifacts: a dict containing an artifact tuple keyed by its
173 version. Artifacts tuple is (local_path, remote_uri).
Alex Klein1699fab2022-09-08 08:46:06 -0600174 """
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000175 for version, artifact in artifacts.items():
176 versioned_artifact = output_proto.versioned_artifacts.add()
177 versioned_artifact.version = version
Brian Norris351745f2023-08-25 15:23:38 -0700178 if artifact[0]:
179 versioned_artifact.file_path.path = artifact[0]
180 versioned_artifact.file_path.location = common_pb2.Path.INSIDE
Benjamin Shai3c1a2322023-02-10 17:29:46 +0000181 versioned_artifact.remote_uri = artifact[1] or ""