blob: cb20b2852cb49ec75d73368d955fd522a9106db2 [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
Alex Klein2966e302019-01-17 13:29:38 -070011import os
12
Alex Klein8cb365a2019-05-15 16:24:53 -060013from chromite.api import controller
Alex Klein076841b2019-08-29 15:19:39 -060014from chromite.api import faux
Alex Klein2b236722019-06-19 15:44:26 -060015from chromite.api import validate
Alex Kleine9a7dbf2020-10-06 18:12:12 -060016from chromite.api.controller import controller_util
David Burgerb171d652019-05-13 16:07:00 -060017from chromite.api.gen.chromiumos import common_pb2
Will Bradley9bc85452019-10-10 10:48:21 -060018from chromite.api.metrics import deserialize_metrics_log
George Engelbrechtc9a8e812021-06-16 18:14:17 -060019from chromite.lib import build_target_lib
20from chromite.lib import chroot_lib
Alex Klein4f0eb432019-05-02 13:56:04 -060021from chromite.lib import cros_build_lib
Alex Klein56355682019-02-07 10:36:54 -070022from chromite.lib import constants
23from chromite.lib import image_lib
Jack Neus761e1842020-12-01 18:20:11 +000024from chromite.lib import cros_logging as logging
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
Alex Klein56355682019-02-07 10:36:54 -070030# The image.proto ImageType enum ids.
George Engelbrechtc55d6312021-05-05 12:11:13 -060031_BASE_ID = common_pb2.IMAGE_TYPE_BASE
32_DEV_ID = common_pb2.IMAGE_TYPE_DEV
33_TEST_ID = common_pb2.IMAGE_TYPE_TEST
34_BASE_VM_ID = common_pb2.IMAGE_TYPE_BASE_VM
35_TEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_VM
36_RECOVERY_ID = common_pb2.IMAGE_TYPE_RECOVERY
37_FACTORY_ID = common_pb2.IMAGE_TYPE_FACTORY
38_FIRMWARE_ID = common_pb2.IMAGE_TYPE_FIRMWARE
39_BASE_GUEST_VM_ID = common_pb2.IMAGE_TYPE_BASE_GUEST_VM
40_TEST_GUEST_VM_ID = common_pb2.IMAGE_TYPE_TEST_GUEST_VM
Alex Klein56355682019-02-07 10:36:54 -070041
42# Dict to allow easily translating names to enum ids and vice versa.
43_IMAGE_MAPPING = {
44 _BASE_ID: constants.IMAGE_TYPE_BASE,
45 constants.IMAGE_TYPE_BASE: _BASE_ID,
46 _DEV_ID: constants.IMAGE_TYPE_DEV,
47 constants.IMAGE_TYPE_DEV: _DEV_ID,
48 _TEST_ID: constants.IMAGE_TYPE_TEST,
49 constants.IMAGE_TYPE_TEST: _TEST_ID,
Michael Mortenseneefe8952019-08-12 15:37:15 -060050 _RECOVERY_ID: constants.IMAGE_TYPE_RECOVERY,
51 constants.IMAGE_TYPE_RECOVERY: _RECOVERY_ID,
52 _FACTORY_ID: constants.IMAGE_TYPE_FACTORY,
53 constants.IMAGE_TYPE_FACTORY: _FACTORY_ID,
54 _FIRMWARE_ID: constants.IMAGE_TYPE_FIRMWARE,
55 constants.IMAGE_TYPE_FIRMWARE: _FIRMWARE_ID,
Alex Klein56355682019-02-07 10:36:54 -070056}
57
George Engelbrecht9f4f8322021-03-08 12:04:17 -070058# Dict to describe the prerequisite built images for each VM image type.
Alex Klein21b95022019-05-09 14:14:46 -060059_VM_IMAGE_MAPPING = {
60 _BASE_VM_ID: _IMAGE_MAPPING[_BASE_ID],
61 _TEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Trent Begin008cade2019-10-31 13:40:59 -060062 _BASE_GUEST_VM_ID: _IMAGE_MAPPING[_BASE_ID],
63 _TEST_GUEST_VM_ID: _IMAGE_MAPPING[_TEST_ID],
Alex Klein21b95022019-05-09 14:14:46 -060064}
65
George Engelbrecht9f4f8322021-03-08 12:04:17 -070066# Dict to describe the prerequisite built images for each mod image type.
67_MOD_IMAGE_MAPPING = {
68 _RECOVERY_ID: _IMAGE_MAPPING[_BASE_ID],
69}
70
Jack Neus761e1842020-12-01 18:20:11 +000071# Supported image types for PushImage.
72SUPPORTED_IMAGE_TYPES = {
73 common_pb2.IMAGE_TYPE_RECOVERY: constants.IMAGE_TYPE_RECOVERY,
74 common_pb2.IMAGE_TYPE_FACTORY: constants.IMAGE_TYPE_FACTORY,
75 common_pb2.IMAGE_TYPE_FIRMWARE: constants.IMAGE_TYPE_FIRMWARE,
76 common_pb2.IMAGE_TYPE_ACCESSORY_USBPD: constants.IMAGE_TYPE_ACCESSORY_USBPD,
77 common_pb2.IMAGE_TYPE_ACCESSORY_RWSIG: constants.IMAGE_TYPE_ACCESSORY_RWSIG,
78 common_pb2.IMAGE_TYPE_BASE: constants.IMAGE_TYPE_BASE,
George Engelbrecht9f4f8322021-03-08 12:04:17 -070079 common_pb2.IMAGE_TYPE_GSC_FIRMWARE: constants.IMAGE_TYPE_GSC_FIRMWARE,
Jack Neus761e1842020-12-01 18:20:11 +000080}
81
Alex Klein56355682019-02-07 10:36:54 -070082
George Engelbrecht9f4f8322021-03-08 12:04:17 -070083def _add_image_to_proto(output_proto, path, image_type, board):
84 """Quick helper function to add a new image to the output proto."""
85 new_image = output_proto.images.add()
86 new_image.path = path
87 new_image.type = image_type
88 new_image.build_target.name = board
89
90
George Engelbrechtc9a8e812021-06-16 18:14:17 -060091def ExampleGetResponse():
92 """Give an example response to assemble upstream in caller artifacts."""
93 uabs = common_pb2.UploadedArtifactsByService
94 cabs = common_pb2.ArtifactsByService
95 return uabs.Sysroot(artifacts=[
96 uabs.Image.ArtifactPaths(
97 artifact_type=cabs.Image.ArtifactType.DLC_IMAGE,
98 paths=[
99 common_pb2.Path(
100 path='/tmp/dlc/dlc.img', location=common_pb2.Path.OUTSIDE)
101 ])
102 ])
103
104
105def GetArtifacts(in_proto: common_pb2.ArtifactsByService.Image,
106 chroot: chroot_lib.Chroot, sysroot_class: sysroot_lib.Sysroot,
Jack Neus5e56fef2021-06-18 16:57:28 +0000107 build_target: build_target_lib.BuildTarget, output_dir) -> list:
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600108 """Builds and copies images to specified output_dir.
109
110 Copies (after optionally bundling) all required images into the output_dir,
111 returning a mapping of image type to a list of (output_dir) paths to
112 the desired files. Note that currently it is only processing one image (DLC),
113 but the future direction is to process all required images. Required images
114 are located within output_artifact.artifact_type.
115
116 Args:
117 in_proto: Proto request defining reqs.
118 chroot: The chroot proto used for these artifacts.
119 sysroot_class: The sysroot proto used for these artifacts.
120 build_target: The build target used for these artifacts.
121 output_dir: The path to write artifacts to.
122
123 Returns:
124 A list of dictionary mappings of ArtifactType to list of paths.
125 """
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000126 base_path = chroot.full_path(sysroot_class.path)
Jack Neus5e56fef2021-06-18 16:57:28 +0000127 board = build_target.name
128
129 generated = []
130 dlc_func = functools.partial(image.copy_dlc_image, base_path)
131 license_func = functools.partial(image.copy_license_credits, board)
132 artifact_types = {
133 in_proto.ArtifactType.DLC_IMAGE: dlc_func,
134 in_proto.ArtifactType.LICENSE_CREDITS: license_func,
135 }
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600136
137 for output_artifact in in_proto.output_artifacts:
Jack Neus5e56fef2021-06-18 16:57:28 +0000138 for artifact_type, func in artifact_types.items():
139 if artifact_type in output_artifact.artifact_types:
140 result = func(output_dir)
141 if result:
142 generated.append({
143 'paths': [result] if isinstance(result, str) else result,
144 'type': artifact_type,
145 })
George Engelbrechtc9a8e812021-06-16 18:14:17 -0600146
Jack Neus5e56fef2021-06-18 16:57:28 +0000147 return generated
Pi-Hsun Shih8d9c8e42021-06-30 03:27:28 +0000148
Michael Mortensen10146cf2019-11-19 19:59:22 -0700149def _CreateResponse(_input_proto, output_proto, _config):
150 """Set output_proto success field on a successful Create response."""
151 output_proto.success = True
152
153
154@faux.success(_CreateResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700155@faux.empty_completed_unsuccessfully_error
Alex Klein2b236722019-06-19 15:44:26 -0600156@validate.require('build_target.name')
Alex Klein231d2da2019-07-22 16:44:45 -0600157@validate.validation_complete
Will Bradley9bc85452019-10-10 10:48:21 -0600158@metrics.collect_metrics
Alex Klein231d2da2019-07-22 16:44:45 -0600159def Create(input_proto, output_proto, _config):
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700160 """Build images.
Alex Klein56355682019-02-07 10:36:54 -0700161
162 Args:
163 input_proto (image_pb2.CreateImageRequest): The input message.
164 output_proto (image_pb2.CreateImageResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600165 _config (api_config.ApiConfig): The API call config.
Alex Klein56355682019-02-07 10:36:54 -0700166 """
167 board = input_proto.build_target.name
Alex Klein56355682019-02-07 10:36:54 -0700168
Alex Klein56355682019-02-07 10:36:54 -0700169 # Build the base image if no images provided.
170 to_build = input_proto.image_types or [_BASE_ID]
Alex Klein56355682019-02-07 10:36:54 -0700171
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700172 image_types, vm_types, mod_image_types = _ParseImagesToCreate(to_build)
Alex Klein21b95022019-05-09 14:14:46 -0600173 build_config = _ParseCreateBuildConfig(input_proto)
Alex Klein56355682019-02-07 10:36:54 -0700174
175 # Sorted isn't really necessary here, but it's much easier to test.
Jack Neus761e1842020-12-01 18:20:11 +0000176 result = image.Build(
177 board=board, images=sorted(list(image_types)), config=build_config)
Alex Klein56355682019-02-07 10:36:54 -0700178
Alex Klein1bcd9882019-03-19 13:25:24 -0600179 output_proto.success = result.success
Will Bradley29a49c22019-10-21 11:50:08 -0600180
Alex Klein1bcd9882019-03-19 13:25:24 -0600181 if result.success:
182 # Success -- we need to list out the images we built in the output.
183 _PopulateBuiltImages(board, image_types, output_proto)
Will Bradley29a49c22019-10-21 11:50:08 -0600184
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700185 for vm_type in vm_types:
186 is_test = vm_type in [_TEST_VM_ID, _TEST_GUEST_VM_ID]
187 try:
188 if vm_type in [_BASE_GUEST_VM_ID, _TEST_GUEST_VM_ID]:
189 vm_path = image.CreateGuestVm(board, is_test=is_test)
190 else:
191 vm_path = image.CreateVm(
192 board, disk_layout=build_config.disk_layout, is_test=is_test)
193 except image.ImageToVmError as e:
194 cros_build_lib.Die(e)
Will Bradley29a49c22019-10-21 11:50:08 -0600195
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700196 _add_image_to_proto(output_proto, vm_path, vm_type, board)
197
198 for mod_type in mod_image_types:
199 if mod_type == _RECOVERY_ID:
200 base_image_path = _GetBaseImagePath(output_proto)
201 result = image.BuildRecoveryImage(board=board,
202 image_path=base_image_path)
203 if result.success:
204 _PopulateBuiltImages(board, [_IMAGE_MAPPING[mod_type]], output_proto)
205 else:
206 cros_build_lib.Die('Failed to create recovery image.')
207 else:
208 cros_build_lib.Die('_RECOVERY_ID is the only mod_image_type.')
Will Bradley29a49c22019-10-21 11:50:08 -0600209
210 # Read metric events log and pipe them into output_proto.events.
211 deserialize_metrics_log(output_proto.events, prefix=board)
212 return controller.RETURN_CODE_SUCCESS
213
Alex Klein1bcd9882019-03-19 13:25:24 -0600214 else:
Alex Klein2557b4f2019-07-11 14:34:00 -0600215 # Failure, include all of the failed packages in the output when available.
216 if not result.failed_packages:
217 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
218
Alex Klein1bcd9882019-03-19 13:25:24 -0600219 for package in result.failed_packages:
220 current = output_proto.failed_packages.add()
Alex Kleine9a7dbf2020-10-06 18:12:12 -0600221 controller_util.serialize_package_info(package, current)
Alex Klein1bcd9882019-03-19 13:25:24 -0600222
Alex Klein8cb365a2019-05-15 16:24:53 -0600223 return controller.RETURN_CODE_UNSUCCESSFUL_RESPONSE_AVAILABLE
Alex Klein1bcd9882019-03-19 13:25:24 -0600224
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700225def _GetBaseImagePath(output_proto):
226 """From image_pb2.CreateImageResult, return base image path or None."""
227 ret = None
228 for i in output_proto.images:
229 if i.type == _BASE_ID:
230 ret = i.path
231 return ret
232
Alex Klein21b95022019-05-09 14:14:46 -0600233
234def _ParseImagesToCreate(to_build):
235 """Helper function to parse the image types to build.
236
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700237 This function expresses the dependencies of each image type and adds
238 the requisite image types if they're not explicitly defined.
Alex Klein21b95022019-05-09 14:14:46 -0600239
240 Args:
241 to_build (list[int]): The image type list.
242
243 Returns:
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700244 (set, set, set): The image, vm, and mod_image types that need to be built.
Alex Klein21b95022019-05-09 14:14:46 -0600245 """
246 image_types = set()
247 vm_types = set()
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700248 mod_image_types = set()
Alex Klein21b95022019-05-09 14:14:46 -0600249 for current in to_build:
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700250 # Find out if it's a special case (vm, img mod), or just any old image.
251 if current in _VM_IMAGE_MAPPING:
Alex Klein21b95022019-05-09 14:14:46 -0600252 vm_types.add(current)
253 # Make sure we build the image required to build the VM.
254 image_types.add(_VM_IMAGE_MAPPING[current])
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700255 elif current in _MOD_IMAGE_MAPPING:
256 mod_image_types.add(current)
257 image_types.add(_MOD_IMAGE_MAPPING[current])
258 elif current in _IMAGE_MAPPING:
259 image_types.add(_IMAGE_MAPPING[current])
Alex Klein21b95022019-05-09 14:14:46 -0600260 else:
261 # Not expected, but at least it will be obvious if this comes up.
262 cros_build_lib.Die(
263 "The service's known image types do not match those in image.proto. "
264 'Unknown Enum ID: %s' % current)
265
Trent Begin008cade2019-10-31 13:40:59 -0600266 # We can only build one type of these images at a time since image_to_vm.sh
267 # uses the default path if a name is not provided.
268 if vm_types.issuperset({_BASE_VM_ID, _TEST_VM_ID}):
Alex Klein21b95022019-05-09 14:14:46 -0600269 cros_build_lib.Die('Cannot create more than one VM.')
270
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700271 return image_types, vm_types, mod_image_types
Alex Klein21b95022019-05-09 14:14:46 -0600272
273
274def _ParseCreateBuildConfig(input_proto):
275 """Helper to parse the image build config for Create."""
276 enable_rootfs_verification = not input_proto.disable_rootfs_verification
277 version = input_proto.version or None
278 disk_layout = input_proto.disk_layout or None
279 builder_path = input_proto.builder_path or None
280 return image.BuildConfig(
Jack Neus761e1842020-12-01 18:20:11 +0000281 enable_rootfs_verification=enable_rootfs_verification,
282 replace=True,
283 version=version,
284 disk_layout=disk_layout,
285 builder_path=builder_path,
Alex Klein21b95022019-05-09 14:14:46 -0600286 )
287
Alex Klein1bcd9882019-03-19 13:25:24 -0600288
289def _PopulateBuiltImages(board, image_types, output_proto):
290 """Helper to list out built images for Create."""
Alex Klein56355682019-02-07 10:36:54 -0700291 # Build out the ImageType->ImagePath mapping in the output.
292 # We're using the default path, so just fetch that, but read the symlink so
293 # the path we're returning is somewhat more permanent.
294 latest_link = image_lib.GetLatestImageLink(board)
Alex Klein4f0eb432019-05-02 13:56:04 -0600295 base_path = os.path.realpath(latest_link)
Alex Klein56355682019-02-07 10:36:54 -0700296
297 for current in image_types:
298 type_id = _IMAGE_MAPPING[current]
299 path = os.path.join(base_path, constants.IMAGE_TYPE_TO_NAME[current])
George Engelbrecht9f4f8322021-03-08 12:04:17 -0700300 _add_image_to_proto(output_proto, path, type_id, board)
Alex Klein4f0eb432019-05-02 13:56:04 -0600301
302
Michael Mortensen10146cf2019-11-19 19:59:22 -0700303def _SignerTestResponse(_input_proto, output_proto, _config):
304 """Set output_proto success field on a successful SignerTest response."""
305 output_proto.success = True
306 return controller.RETURN_CODE_SUCCESS
307
308
309@faux.success(_SignerTestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700310@faux.empty_completed_unsuccessfully_error
Michael Mortensenc83c9952019-08-05 12:15:12 -0600311@validate.exists('image.path')
Alex Klein231d2da2019-07-22 16:44:45 -0600312@validate.validation_complete
313def SignerTest(input_proto, output_proto, _config):
Michael Mortensenc83c9952019-08-05 12:15:12 -0600314 """Run image tests.
315
316 Args:
317 input_proto (image_pb2.ImageTestRequest): The input message.
318 output_proto (image_pb2.ImageTestResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600319 _config (api_config.ApiConfig): The API call config.
Michael Mortensenc83c9952019-08-05 12:15:12 -0600320 """
Michael Mortensenc83c9952019-08-05 12:15:12 -0600321 image_path = input_proto.image.path
322
Alex Klein231d2da2019-07-22 16:44:45 -0600323 result = image_lib.SecurityTest(image=image_path)
Michael Mortensenc83c9952019-08-05 12:15:12 -0600324 output_proto.success = result
325 if result:
326 return controller.RETURN_CODE_SUCCESS
327 else:
328 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
329
Alex Klein076841b2019-08-29 15:19:39 -0600330
Michael Mortensen10146cf2019-11-19 19:59:22 -0700331def _TestResponse(_input_proto, output_proto, _config):
332 """Set output_proto success field on a successful Test response."""
333 output_proto.success = True
334 return controller.RETURN_CODE_SUCCESS
335
336
337@faux.success(_TestResponse)
Michael Mortensen85d38402019-12-12 09:50:29 -0700338@faux.empty_completed_unsuccessfully_error
Alex Klein2b236722019-06-19 15:44:26 -0600339@validate.require('build_target.name', 'result.directory')
340@validate.exists('image.path')
Alex Klein231d2da2019-07-22 16:44:45 -0600341def Test(input_proto, output_proto, config):
Alex Klein2966e302019-01-17 13:29:38 -0700342 """Run image tests.
343
344 Args:
345 input_proto (image_pb2.ImageTestRequest): The input message.
346 output_proto (image_pb2.ImageTestResult): The output message.
Alex Klein231d2da2019-07-22 16:44:45 -0600347 config (api_config.ApiConfig): The API call config.
Alex Klein2966e302019-01-17 13:29:38 -0700348 """
349 image_path = input_proto.image.path
350 board = input_proto.build_target.name
351 result_directory = input_proto.result.directory
352
Alex Klein2966e302019-01-17 13:29:38 -0700353 if not os.path.isfile(image_path) or not image_path.endswith('.bin'):
Alex Klein4f0eb432019-05-02 13:56:04 -0600354 cros_build_lib.Die(
Alex Klein2966e302019-01-17 13:29:38 -0700355 'The image.path must be an existing image file with a .bin extension.')
356
Alex Klein231d2da2019-07-22 16:44:45 -0600357 if config.validate_only:
358 return controller.RETURN_CODE_VALID_INPUT
359
Alex Klein8cb365a2019-05-15 16:24:53 -0600360 success = image.Test(board, result_directory, image_dir=image_path)
361 output_proto.success = success
362
363 if success:
364 return controller.RETURN_CODE_SUCCESS
365 else:
366 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY
Jack Neus761e1842020-12-01 18:20:11 +0000367
368
369@faux.empty_success
370@faux.empty_completed_unsuccessfully_error
371@validate.require('gs_image_dir', 'sysroot.build_target.name')
372def PushImage(input_proto, _output_proto, config):
373 """Push artifacts from the archive bucket to the release bucket.
374
375 Wraps chromite/scripts/pushimage.py.
376
377 Args:
378 input_proto (PushImageRequest): Input proto.
379 _output_proto (PushImageResponse): Output proto.
380 config (api.config.ApiConfig): The API call config.
381
382 Returns:
383 A controller return code (e.g. controller.RETURN_CODE_SUCCESS).
384 """
385 sign_types = []
386 if input_proto.sign_types:
387 for sign_type in input_proto.sign_types:
388 if sign_type not in SUPPORTED_IMAGE_TYPES:
389 logging.error('unsupported sign type %g', sign_type)
390 return controller.RETURN_CODE_INVALID_INPUT
391 sign_types.append(SUPPORTED_IMAGE_TYPES[sign_type])
392
393 # If configured for validation only we're done here.
394 if config.validate_only:
395 return controller.RETURN_CODE_VALID_INPUT
396
Jack Neus485a9d22020-12-21 03:15:15 +0000397 kwargs = {}
398 if input_proto.profile.name:
399 kwargs['profile'] = input_proto.profile.name
400 if input_proto.dest_bucket:
401 kwargs['dest_bucket'] = input_proto.dest_bucket
Jack Neus761e1842020-12-01 18:20:11 +0000402 try:
403 pushimage.PushImage(
404 input_proto.gs_image_dir,
405 input_proto.sysroot.build_target.name,
406 dry_run=input_proto.dryrun,
Jack Neus485a9d22020-12-21 03:15:15 +0000407 sign_types=sign_types,
408 **kwargs)
Jack Neus761e1842020-12-01 18:20:11 +0000409 return controller.RETURN_CODE_SUCCESS
410 except Exception:
Jack Neuse3150a42021-01-29 17:16:36 +0000411 logging.error('PushImage failed: ', exc_info=True)
Jack Neus761e1842020-12-01 18:20:11 +0000412 return controller.RETURN_CODE_COMPLETED_UNSUCCESSFULLY