blob: 968cb073c6f9e462820c36cf68495f279c03ff2c [file] [log] [blame]
Alex Klein2966e302019-01-17 13:29:38 -07001# Copyright 2018 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.
4
5"""Image API Service.
6
7The image related API endpoints should generally be found here.
8"""
9
Jack Neus5e56fef2021-06-18 16:57:28 +000010import functools
Chris McDonald1672ddb2021-07-21 11:48:23 -060011import logging
Alex Klein2966e302019-01-17 13:29:38 -070012import os
13
Alex Klein8cb365a2019-05-15 16:24:53 -060014from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060015from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060016from chromite.api import validate
Alex Kleine9a7dbf2020-10-06 18:12:12 -060017from chromite.api.controller import controller_util
David Burgerb171d652019-05-13 16:07:00 -060018from chromite.api.gen.chromiumos import common_pb2
Will Bradley9bc85452019-10-10 10:48:21 -060019from chromite.api.metrics import deserialize_metrics_log
George Engelbrechtc9a8e812021-06-16 18:14:17 -060020from chromite.lib import build_target_lib
21from chromite.lib import chroot_lib
Alex Klein56355682019-02-07 10:36:54 -070022from chromite.lib import constants
Chris McDonald1672ddb2021-07-21 11:48:23 -060023from chromite.lib import cros_build_lib
Alex Klein56355682019-02-07 10:36:54 -070024from chromite.lib import image_lib
George Engelbrechtc9a8e812021-06-16 18:14:17 -060025from chromite.lib import sysroot_lib
Jack Neus761e1842020-12-01 18:20:11 +000026from chromite.scripts import pushimage
Alex Kleinb7cdbe62019-02-22 11:41:32 -070027from chromite.service import image
Will Bradley9bc85452019-10-10 10:48:21 -060028from chromite.utils import metrics
Alex Klein2966e302019-01-17 13:29:38 -070029
Chris McDonald1672ddb2021-07-21 11:48:23 -060030
Alex Klein56355682019-02-07 10:36:54 -070031# The image.proto ImageType enum ids.
George Engelbrechtc55d6312021-05-05 12:11:13 -060032_BASE_ID = common_pb2.IMAGE_TYPE_BASE
33_DEV_ID = common_pb2.IMAGE_TYPE_DEV
34_TEST_ID = common_pb2.IMAGE_TYPE_TEST
35_BASE_VM_ID = common_pb2.IMAGE_TYPE_BASE_VM
36_TEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_VM
37_RECOVERY_ID = common_pb2.IMAGE_TYPE_RECOVERY
38_FACTORY_ID = common_pb2.IMAGE_TYPE_FACTORY
39_FIRMWARE_ID = common_pb2.IMAGE_TYPE_FIRMWARE
40_BASE_GUEST_VM_ID = common_pb2.IMAGE_TYPE_BASE_GUEST_VM
41_TEST_GUEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_GUEST_VM
Alex Klein56355682019-02-07 10:36:54 -070042
43# Dict to allow easily translating names to enum ids and vice versa.
44_IMAGE_MAPPING = {
45 _BASE_ID: constants.IMAGE_TYPE_BASE,
46 constants.IMAGE_TYPE_BASE: _BASE_ID,
47 _DEV_ID: constants.IMAGE_TYPE_DEV,
48 constants.IMAGE_TYPE_DEV: _DEV_ID,
49 _TEST_ID: constants.IMAGE_TYPE_TEST,
50 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060051 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
52 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
53 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY,
54 constants.IMAGE_TYPE_FACTORY: _FACTORY_ID,
55 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
56 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Alex Klein56355682019-02-07 10:36:54 -070057}
58
George Engelbrecht9f4f8322021-03-08 12:04:17 -070059# Dict to describe the prerequisite built images for each VM image type.
Alex Klein21b95022019-05-09 14:14:46 -060060_VM_IMAGE_MAPPING = {
61 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
62 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Trent Begin008cade2019-10-31 13:40:59 -060063 _BASE_GUEST_VM_ID: _IMAGE_MAPPING[_BASE_ID],
64 _TEST_GUEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Alex Klein21b95022019-05-09 14:14:46 -060065}
66
George Engelbrecht9f4f8322021-03-08 12:04:17 -070067# Dict to describe the prerequisite built images for each mod image type.
68_MOD_IMAGE_MAPPING = {
69 _RECOVERY_ID: _IMAGE_MAPPING[_BASE_ID],
70}
71
Jack Neus761e1842020-12-01 18:20:11 +000072# Supported image types for PushImage.
73SUPPORTED_IMAGE_TYPES = {
74 common_pb2.IMAGE_TYPE_RECOVERY: constants.IMAGE_TYPE_RECOVERY,
75 common_pb2.IMAGE_TYPE_FACTORY: constants.IMAGE_TYPE_FACTORY,
76 common_pb2.IMAGE_TYPE_FIRMWARE: constants.IMAGE_TYPE_FIRMWARE,
77 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD: constants.IMAGE_TYPE_ACCESSORY_USBPD,
78 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG: constants.IMAGE_TYPE_ACCESSORY_RWSIG,
79 common_pb2.IMAGE_TYPE_BASE: constants.IMAGE_TYPE_BASE,
George Engelbrecht9f4f8322021-03-08 12:04:17 -070080 common_pb2.IMAGE_TYPE_GSC_FIRMWARE: constants.IMAGE_TYPE_GSC_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000081}
82
Alex Klein56355682019-02-07 10:36:54 -070083
George Engelbrecht9f4f8322021-03-08 12:04:17 -070084def _add_image_to_proto(output_proto, path, image_type, board):
85 """Quick helper function to add a new image to the output proto."""
86 new_image = output_proto.images.add()
87 new_image.path = path
88 new_image.type = image_type
89 new_image.build_target.name = board
90
91
George Engelbrechtc9a8e812021-06-16 18:14:17 -060092def ExampleGetResponse():
93 """Give an example response to assemble upstream in caller artifacts."""
94 uabs = common_pb2.UploadedArtifactsByService
95 cabs = common_pb2.ArtifactsByService
96 return uabs.Sysroot(artifacts=[
97 uabs.Image.ArtifactPaths(
98 artifact_type=cabs.Image.ArtifactType.DLC_IMAGE,
99 paths=[
100 common_pb2.Path(
101 path='/tmp/dlc/dlc.img', location=common_pb2.Path.OUTSIDE)
102 ])
103 ])
104
105
106def GetArtifacts(in_proto: common_pb2.ArtifactsByService.Image,
107 chroot: chroot_lib.Chroot, sysroot_class: sysroot_lib.Sysroot,
Jack Neus5e56fef2021-06-18 16:57:28 +0000108 build_target: build_target_lib.BuildTarget, output_dir) -> list:
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600109 """Builds and copies images to specified output_dir.
110
111 Copies (after optionally bundling) all required images into the output_dir,
112 returning a mapping of image type to a list of (output_dir) paths to
113 the desired files. Note that currently it is only processing one image (DLC),
114 but the future direction is to process all required images. Required images
115 are located within output_artifact.artifact_type.
116
117 Args:
118 in_proto: Proto request defining reqs.
119 chroot: The chroot proto used for these artifacts.
120 sysroot_class: The sysroot proto used for these artifacts.
121 build_target: The build target used for these artifacts.
122 output_dir: The path to write artifacts to.
123
124 Returns:
125 A list of dictionary mappings of ArtifactType to list of paths.
126 """
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000127 base_path = chroot.full_path(sysroot_class.path)
Jack Neus5e56fef2021-06-18 16:57:28 +0000128 board = build_target.name
129
130 generated = []
131 dlc_func = functools.partial(image.copy_dlc_image, base_path)
132 license_func = functools.partial(image.copy_license_credits, board)
133 artifact_types = {
134 in_proto.ArtifactType.DLC_IMAGE: dlc_func,
135 in_proto.ArtifactType.LICENSE_CREDITS: license_func,
136 }
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600137
138 for output_artifact in in_proto.output_artifacts:
Jack Neus5e56fef2021-06-18 16:57:28 +0000139 for artifact_type, func in artifact_types.items():
140 if artifact_type in output_artifact.artifact_types:
141 result = func(output_dir)
142 if result:
143 generated.append({
144 'paths': [result] if isinstance(result, str) else result,
145 'type': artifact_type,
146 })
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600147
Jack Neus5e56fef2021-06-18 16:57:28 +0000148 return generated
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000149
Michael Mortensen10146cf2019-11-19 19:59:22 -0700150def _CreateResponse(_input_proto, output_proto, _config):
151 """Set output_proto success field on a successful Create response."""
152 output_proto.success = True
153
154
155@faux.success(_CreateResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700156@faux.empty_completed_unsuccessfully_error
Alex Klein2b236722019-06-19 15:44:26 -0600157@validate.require('build_target.name')
Alex Klein231d2da2019-07-22 16:44:45 -0600158@validate.validation_complete
Will Bradley9bc85452019-10-10 10:48:21 -0600159@metrics.collect_metrics
Alex Klein231d2da2019-07-22 16:44:45 -0600160def Create(input_proto, output_proto, _config):
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700161 """Build images.
Alex Klein56355682019-02-07 10:36:54 -0700162
163 Args:
164 input_proto (image_pb2.CreateImageRequest): The input message.
165 output_proto (image_pb2.CreateImageResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600166 _config (api_config.ApiConfig): The API call config.
Alex Klein56355682019-02-07 10:36:54 -0700167 """
168 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -0700169
Alex Klein56355682019-02-07 10:36:54 -0700170 # Build the base image if no images provided.
171 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -0700172
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700173 image_types, vm_types, mod_image_types = _ParseImagesToCreate(to_build)
Alex Klein21b95022019-05-09 14:14:46 -0600174 build_config = _ParseCreateBuildConfig(input_proto)
Alex Klein56355682019-02-07 10:36:54 -0700175
176 # Sorted isn't really necessary here, but it's much easier to test.
Jack Neus761e1842020-12-01 18:20:11 +0000177 result = image.Build(
178 board=board, images=sorted(list(image_types)), config=build_config)
Alex Klein56355682019-02-07 10:36:54 -0700179
Alex Klein1bcd9882019-03-19 13:25:24 -0600180 output_proto.success = result.success
Will Bradley29a49c22019-10-21 11:50:08 -0600181
Alex Klein1bcd9882019-03-19 13:25:24 -0600182 if result.success:
183 # Success -- we need to list out the images we built in the output.
184 _PopulateBuiltImages(board, image_types, output_proto)
Will Bradley29a49c22019-10-21 11:50:08 -0600185
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700186 for vm_type in vm_types:
187 is_test = vm_type in [_TEST_VM_ID, _TEST_GUEST_VM_ID]
188 try:
189 if vm_type in [_BASE_GUEST_VM_ID, _TEST_GUEST_VM_ID]:
190 vm_path = image.CreateGuestVm(board, is_test=is_test)
191 else:
192 vm_path = image.CreateVm(
193 board, disk_layout=build_config.disk_layout, is_test=is_test)
194 except image.ImageToVmError as e:
195 cros_build_lib.Die(e)
Will Bradley29a49c22019-10-21 11:50:08 -0600196
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700197 _add_image_to_proto(output_proto, vm_path, vm_type, board)
198
199 for mod_type in mod_image_types:
200 if mod_type == _RECOVERY_ID:
201 base_image_path = _GetBaseImagePath(output_proto)
202 result = image.BuildRecoveryImage(board=board,
203 image_path=base_image_path)
204 if result.success:
205 _PopulateBuiltImages(board, [_IMAGE_MAPPING[mod_type]], output_proto)
206 else:
207 cros_build_lib.Die('Failed to create recovery image.')
208 else:
209 cros_build_lib.Die('_RECOVERY_ID is the only mod_image_type.')
Will Bradley29a49c22019-10-21 11:50:08 -0600210
211 # Read metric events log and pipe them into output_proto.events.
212 deserialize_metrics_log(output_proto.events, prefix=board)
213 return controller.RETURN_CODE_SUCCESS
214
Alex Klein1bcd9882019-03-19 13:25:24 -0600215 else:
Alex Klein2557b4f2019-07-11 14:34:00 -0600216 # Failure, include all of the failed packages in the output when available.
217 if not result.failed_packages:
218 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
219
Alex Klein1bcd9882019-03-19 13:25:24 -0600220 for package in result.failed_packages:
221 current = output_proto.failed_packages.add()
Alex Kleine9a7dbf2020-10-06 18:12:12 -0600222 controller_util.serialize_package_info(package, current)
Alex Klein1bcd9882019-03-19 13:25:24 -0600223
Alex Klein8cb365a2019-05-15 16:24:53 -0600224 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -0600225
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700226def _GetBaseImagePath(output_proto):
227 """From image_pb2.CreateImageResult, return base image path or None."""
228 ret = None
229 for i in output_proto.images:
230 if i.type == _BASE_ID:
231 ret = i.path
232 return ret
233
Alex Klein21b95022019-05-09 14:14:46 -0600234
235def _ParseImagesToCreate(to_build):
236 """Helper function to parse the image types to build.
237
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700238 This function expresses the dependencies of each image type and adds
239 the requisite image types if they're not explicitly defined.
Alex Klein21b95022019-05-09 14:14:46 -0600240
241 Args:
242 to_build (list[int]): The image type list.
243
244 Returns:
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700245 (set, set, set): The image, vm, and mod_image types that need to be built.
Alex Klein21b95022019-05-09 14:14:46 -0600246 """
247 image_types = set()
248 vm_types = set()
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700249 mod_image_types = set()
Alex Klein21b95022019-05-09 14:14:46 -0600250 for current in to_build:
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700251 # Find out if it's a special case (vm, img mod), or just any old image.
252 if current in _VM_IMAGE_MAPPING:
Alex Klein21b95022019-05-09 14:14:46 -0600253 vm_types.add(current)
254 # Make sure we build the image required to build the VM.
255 image_types.add(_VM_IMAGE_MAPPING[current])
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700256 elif current in _MOD_IMAGE_MAPPING:
257 mod_image_types.add(current)
258 image_types.add(_MOD_IMAGE_MAPPING[current])
259 elif current in _IMAGE_MAPPING:
260 image_types.add(_IMAGE_MAPPING[current])
Alex Klein21b95022019-05-09 14:14:46 -0600261 else:
262 # Not expected, but at least it will be obvious if this comes up.
263 cros_build_lib.Die(
264 "The service's known image types do not match those in image.proto. "
265 'Unknown Enum ID: %s' % current)
266
Trent Begin008cade2019-10-31 13:40:59 -0600267 # We can only build one type of these images at a time since image_to_vm.sh
268 # uses the default path if a name is not provided.
269 if vm_types.issuperset({_BASE_VM_ID, _TEST_VM_ID}):
Alex Klein21b95022019-05-09 14:14:46 -0600270 cros_build_lib.Die('Cannot create more than one VM.')
271
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700272 return image_types, vm_types, mod_image_types
Alex Klein21b95022019-05-09 14:14:46 -0600273
274
275def _ParseCreateBuildConfig(input_proto):
276 """Helper to parse the image build config for Create."""
277 enable_rootfs_verification = not input_proto.disable_rootfs_verification
278 version = input_proto.version or None
279 disk_layout = input_proto.disk_layout or None
280 builder_path = input_proto.builder_path or None
281 return image.BuildConfig(
Jack Neus761e1842020-12-01 18:20:11 +0000282 enable_rootfs_verification=enable_rootfs_verification,
283 replace=True,
284 version=version,
285 disk_layout=disk_layout,
286 builder_path=builder_path,
Alex Klein21b95022019-05-09 14:14:46 -0600287 )
288
Alex Klein1bcd9882019-03-19 13:25:24 -0600289
290def _PopulateBuiltImages(board, image_types, output_proto):
291 """Helper to list out built images for Create."""
Alex Klein56355682019-02-07 10:36:54 -0700292 # Build out the ImageType->ImagePath mapping in the output.
293 # We're using the default path, so just fetch that, but read the symlink so
294 # the path we're returning is somewhat more permanent.
295 latest_link = image_lib.GetLatestImageLink(board)
Alex Klein4f0eb432019-05-02 13:56:04 -0600296 base_path = os.path.realpath(latest_link)
Alex Klein56355682019-02-07 10:36:54 -0700297
298 for current in image_types:
299 type_id = _IMAGE_MAPPING[current]
300 path = os.path.join(base_path, constants.IMAGE_TYPE_TO_NAME[current])
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700301 _add_image_to_proto(output_proto, path, type_id, board)
Alex Klein4f0eb432019-05-02 13:56:04 -0600302
303
Michael Mortensen10146cf2019-11-19 19:59:22 -0700304def _SignerTestResponse(_input_proto, output_proto, _config):
305 """Set output_proto success field on a successful SignerTest response."""
306 output_proto.success = True
307 return controller.RETURN_CODE_SUCCESS
308
309
310@faux.success(_SignerTestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700311@faux.empty_completed_unsuccessfully_error
Michael Mortensenc83c9952019-08-05 12:15:12 -0600312@validate.exists('image.path')
Alex Klein231d2da2019-07-22 16:44:45 -0600313@validate.validation_complete
314def SignerTest(input_proto, output_proto, _config):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600315 """Run image tests.
316
317 Args:
318 input_proto (image_pb2.ImageTestRequest): The input message.
319 output_proto (image_pb2.ImageTestResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600320 _config (api_config.ApiConfig): The API call config.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600321 """
Michael Mortensenc83c9952019-08-05 12:15:12 -0600322 image_path = input_proto.image.path
323
Alex Klein231d2da2019-07-22 16:44:45 -0600324 result = image_lib.SecurityTest(image=image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600325 output_proto.success = result
326 if result:
327 return controller.RETURN_CODE_SUCCESS
328 else:
329 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
330
Alex Klein076841b2019-08-29 15:19:39 -0600331
Michael Mortensen10146cf2019-11-19 19:59:22 -0700332def _TestResponse(_input_proto, output_proto, _config):
333 """Set output_proto success field on a successful Test response."""
334 output_proto.success = True
335 return controller.RETURN_CODE_SUCCESS
336
337
338@faux.success(_TestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700339@faux.empty_completed_unsuccessfully_error
Alex Klein2b236722019-06-19 15:44:26 -0600340@validate.require('build_target.name', 'result.directory')
341@validate.exists('image.path')
Alex Klein231d2da2019-07-22 16:44:45 -0600342def Test(input_proto, output_proto, config):
Alex Klein2966e302019-01-17 13:29:38 -0700343 """Run image tests.
344
345 Args:
346 input_proto (image_pb2.ImageTestRequest): The input message.
347 output_proto (image_pb2.ImageTestResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600348 config (api_config.ApiConfig): The API call config.
Alex Klein2966e302019-01-17 13:29:38 -0700349 """
350 image_path = input_proto.image.path
351 board = input_proto.build_target.name
352 result_directory = input_proto.result.directory
353
Alex Klein2966e302019-01-17 13:29:38 -0700354 if not os.path.isfile(image_path) or not image_path.endswith('.bin'):
Alex Klein4f0eb432019-05-02 13:56:04 -0600355 cros_build_lib.Die(
Alex Klein2966e302019-01-17 13:29:38 -0700356 'The image.path must be an existing image file with a .bin extension.')
357
Alex Klein231d2da2019-07-22 16:44:45 -0600358 if config.validate_only:
359 return controller.RETURN_CODE_VALID_INPUT
360
Alex Klein8cb365a2019-05-15 16:24:53 -0600361 success = image.Test(board, result_directory, image_dir=image_path)
362 output_proto.success = success
363
364 if success:
365 return controller.RETURN_CODE_SUCCESS
366 else:
367 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jack Neus761e1842020-12-01 18:20:11 +0000368
369
370@faux.empty_success
371@faux.empty_completed_unsuccessfully_error
372@validate.require('gs_image_dir', 'sysroot.build_target.name')
373def PushImage(input_proto, _output_proto, config):
374 """Push artifacts from the archive bucket to the release bucket.
375
376 Wraps chromite/scripts/pushimage.py.
377
378 Args:
379 input_proto (PushImageRequest): Input proto.
380 _output_proto (PushImageResponse): Output proto.
381 config (api.config.ApiConfig): The API call config.
382
383 Returns:
384 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
385 """
386 sign_types = []
387 if input_proto.sign_types:
388 for sign_type in input_proto.sign_types:
389 if sign_type not in SUPPORTED_IMAGE_TYPES:
390 logging.error('unsupported sign type %g', sign_type)
391 return controller.RETURN_CODE_INVALID_INPUT
392 sign_types.append(SUPPORTED_IMAGE_TYPES[sign_type])
393
394 # If configured for validation only we're done here.
395 if config.validate_only:
396 return controller.RETURN_CODE_VALID_INPUT
397
Jack Neus485a9d22020-12-21 03:15:15 +0000398 kwargs = {}
399 if input_proto.profile.name:
400 kwargs['profile'] = input_proto.profile.name
401 if input_proto.dest_bucket:
402 kwargs['dest_bucket'] = input_proto.dest_bucket
Jack Neus761e1842020-12-01 18:20:11 +0000403 try:
404 pushimage.PushImage(
405 input_proto.gs_image_dir,
406 input_proto.sysroot.build_target.name,
407 dry_run=input_proto.dryrun,
Jack Neus485a9d22020-12-21 03:15:15 +0000408 sign_types=sign_types,
409 **kwargs)
Jack Neus761e1842020-12-01 18:20:11 +0000410 return controller.RETURN_CODE_SUCCESS
411 except Exception:
Jack Neuse3150a42021-01-29 17:16:36 +0000412 logging.error('PushImage failed: ', exc_info=True)
Jack Neus761e1842020-12-01 18:20:11 +0000413 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY