c908bb3b6b0deea252e845beb3f75bc3fe3802b7
[osm/SO.git] / rwlaunchpad / plugins / rwlaunchpadtasklet / rift / tasklets / rwlaunchpad / uploader.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 import abc
18 import asyncio
19 import collections
20 import os
21 import tempfile
22 import threading
23 import uuid
24 import zlib
25
26 import tornado
27 import tornado.escape
28 import tornado.ioloop
29 import tornado.web
30 import tornado.httputil
31 import tornadostreamform.multipart_streamer as multipart_streamer
32
33 import requests
34
35 # disable unsigned certificate warning
36 from requests.packages.urllib3.exceptions import InsecureRequestWarning
37 requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
38
39 import gi
40 gi.require_version('RwLaunchpadYang', '1.0')
41 gi.require_version('NsdYang', '1.0')
42 gi.require_version('VnfdYang', '1.0')
43
44 from gi.repository import (
45 NsdYang,
46 VnfdYang,
47 )
48 import rift.mano.cloud
49
50 import rift.package.charm
51 import rift.package.checksums
52 import rift.package.config
53 import rift.package.convert
54 import rift.package.handler as pkg_handler
55 import rift.package.icon
56 import rift.package.package
57 import rift.package.script
58 import rift.package.store
59
60 from gi.repository import (
61 RwDts as rwdts,
62 RwPkgMgmtYang)
63 import rift.downloader as downloader
64 import rift.mano.dts as mano_dts
65 import rift.tasklets
66
67 from . import (
68 export,
69 extract,
70 image,
71 message,
72 onboard,
73 state,
74 )
75
76 from .message import (
77 MessageException,
78
79 # Onboard Error Messages
80 OnboardChecksumMismatch,
81 OnboardDescriptorError,
82 OnboardDescriptorExistsError,
83 OnboardDescriptorFormatError,
84 OnboardError,
85 OnboardExtractionError,
86 OnboardImageUploadError,
87 OnboardMissingContentBoundary,
88 OnboardMissingContentType,
89 OnboardMissingTerminalBoundary,
90 OnboardUnreadableHeaders,
91 OnboardUnreadablePackage,
92 OnboardUnsupportedMediaType,
93
94 # Onboard Status Messages
95 OnboardDescriptorOnboard,
96 OnboardFailure,
97 OnboardImageUpload,
98 OnboardPackageUpload,
99 OnboardPackageValidation,
100 OnboardStart,
101 OnboardSuccess,
102
103 DownloadError,
104 DownloadSuccess,
105
106 # Update Error Messages
107 UpdateChecksumMismatch,
108 UpdateDescriptorError,
109 UpdateDescriptorFormatError,
110 UpdateError,
111 UpdateExtractionError,
112 UpdateImageUploadError,
113 UpdateMissingContentBoundary,
114 UpdateMissingContentType,
115 UpdatePackageNotFoundError,
116 UpdateUnreadableHeaders,
117 UpdateUnreadablePackage,
118 UpdateUnsupportedMediaType,
119
120 # Update Status Messages
121 UpdateDescriptorUpdate,
122 UpdateDescriptorUpdated,
123 UpdatePackageUpload,
124 UpdateStart,
125 UpdateSuccess,
126 UpdateFailure,
127 )
128
129 from .tosca import ExportTosca
130
131 MB = 1024 * 1024
132 GB = 1024 * MB
133
134 MAX_STREAMED_SIZE = 5 * GB
135
136 # Shortcuts
137 RPC_PACKAGE_CREATE_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageCreate
138 RPC_PACKAGE_UPDATE_ENDPOINT = RwPkgMgmtYang.YangOutput_RwPkgMgmt_PackageUpdate
139
140
141
142 class HttpMessageError(Exception):
143 def __init__(self, code, msg):
144 self.code = code
145 self.msg = msg
146
147
148 class UploadRpcHandler(mano_dts.AbstractRpcHandler):
149 def __init__(self, log, dts, loop, application):
150 """
151 Args:
152 application: UploaderApplication
153 """
154 super().__init__(log, dts, loop)
155 self.application = application
156
157 @property
158 def xpath(self):
159 return "/rw-pkg-mgmt:package-create"
160
161 @asyncio.coroutine
162 def callback(self, ks_path, msg):
163 transaction_id = str(uuid.uuid4())
164 log = self.application.get_logger(transaction_id)
165 log.message(OnboardStart())
166
167
168 auth = None
169 if msg.username is not None:
170 auth = (msg.username, msg.password)
171
172 self.application.onboard(
173 msg.external_url,
174 transaction_id,
175 auth=auth
176 )
177
178 rpc_op = RPC_PACKAGE_CREATE_ENDPOINT.from_dict({
179 "transaction_id": transaction_id})
180
181 return rpc_op
182
183
184 class UpdateRpcHandler(mano_dts.AbstractRpcHandler):
185 def __init__(self, log, dts, loop, application):
186 """
187 Args:
188 application: UploaderApplication
189 """
190 super().__init__(log, dts, loop)
191 self.application = application
192
193 @property
194 def xpath(self):
195 return "/rw-pkg-mgmt:package-update"
196
197 @asyncio.coroutine
198 def callback(self, ks_path, msg):
199
200 transaction_id = str(uuid.uuid4())
201 log = self.application.get_logger(transaction_id)
202 log.message(UpdateStart())
203
204 auth = None
205 if msg.username is not None:
206 auth = (msg.username, msg.password)
207
208 self.application.update(
209 msg.external_url,
210 transaction_id,
211 auth=auth
212 )
213
214 rpc_op = RPC_PACKAGE_UPDATE_ENDPOINT.from_dict({
215 "transaction_id": transaction_id})
216
217 return rpc_op
218
219
220 class UploadStateHandler(state.StateHandler):
221 STARTED = OnboardStart
222 SUCCESS = OnboardSuccess
223 FAILURE = OnboardFailure
224
225
226 class UpdateStateHandler(state.StateHandler):
227 STARTED = UpdateStart
228 SUCCESS = UpdateSuccess
229 FAILURE = UpdateFailure
230
231
232 class UpdatePackage(downloader.DownloaderProtocol):
233
234 def __init__(self, log, loop, url, auth,
235 onboarder, uploader, package_store_map):
236 super().__init__()
237 self.log = log
238 self.loop = loop
239 self.url = url
240 self.auth = auth
241 self.onboarder = onboarder
242 self.uploader = uploader
243 self.package_store_map = package_store_map
244
245
246 def _update_package(self, packages):
247
248 # Extract package could return multiple packages if
249 # the package is converted
250 for pkg in packages:
251 with pkg as temp_package:
252 package_checksums = self.validate_package(temp_package)
253 stored_package = self.update_package(temp_package)
254 self.validate_vnfd_fields(temp_package)
255
256 try:
257 self.extract_charms(temp_package)
258 self.extract_scripts(temp_package)
259 self.extract_configs(temp_package)
260 self.extract_icons(temp_package)
261
262 self.update_descriptors(temp_package)
263
264 except Exception:
265 self.delete_stored_package(stored_package)
266 raise
267
268 else:
269 self.upload_images(temp_package, package_checksums)
270
271 def extract(self, packages):
272 try:
273 self._update_package(packages)
274 self.log.message(UpdateSuccess())
275
276 except MessageException as e:
277 self.log.message(e.msg)
278 self.log.message(UpdateFailure())
279
280 except Exception as e:
281 self.log.exception(e)
282 if str(e):
283 self.log.message(UpdateError(str(e)))
284 self.log.message(UpdateFailure())
285
286 def on_download_succeeded(self, job):
287 self.log.message(DownloadSuccess("Package downloaded."))
288
289 extractor = extract.UploadPackageExtractor(self.log)
290 file_backed_packages = extractor.create_packages_from_upload(
291 job.filename, job.filepath
292 )
293
294 self.extract(file_backed_packages)
295
296 def on_download_failed(self, job):
297 self.log.error(job.detail)
298 self.log.message(DownloadError("Package download failed. {}".format(job.detail)))
299 self.log.message(UpdateFailure())
300
301 def download_package(self):
302
303 _, filename = tempfile.mkstemp()
304 url_downloader = downloader.UrlDownloader(
305 self.url,
306 auth=self.auth,
307 file_obj=filename,
308 decompress_on_fly=True,
309 log=self.log)
310 url_downloader.delegate = self
311 url_downloader.download()
312
313 def get_package_store(self, package):
314 return self.package_store_map[package.descriptor_type]
315
316 def update_package(self, package):
317 store = self.get_package_store(package)
318
319 try:
320 store.update_package(package)
321 except rift.package.store.PackageNotFoundError as e:
322 # If the package doesn't exist, then it is possible the descriptor was onboarded
323 # out of band. In that case, just store the package as is
324 self.log.warning("Package not found, storing new package instead.")
325 store.store_package(package)
326
327 stored_package = store.get_package(package.descriptor_id)
328
329 return stored_package
330
331 def delete_stored_package(self, package):
332 self.log.info("Deleting stored package: %s", package)
333 store = self.get_package_store(package)
334 try:
335 store.delete_package(package.descriptor_id)
336 except Exception as e:
337 self.log.warning("Failed to delete package from store: %s", str(e))
338
339 def upload_images(self, package, package_checksums):
340 image_file_map = rift.package.image.get_package_image_files(package)
341 name_hdl_map = {name: package.open(image_file_map[name]) for name in image_file_map}
342 if not image_file_map:
343 return
344
345 try:
346 for image_name, image_hdl in name_hdl_map.items():
347 image_file = image_file_map[image_name]
348 if image_file in package_checksums:
349 image_checksum = package_checksums[image_file]
350 else:
351 self.log.warning("checksum not provided for image %s. Calculating checksum",
352 image_file)
353 image_checksum = rift.package.checksums.checksum(
354 package.open(image_file_map[image_name])
355 )
356 try:
357 self.uploader.upload_image(image_name, image_checksum, image_hdl)
358 self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum)
359
360 except image.ImageUploadError as e:
361 self.log.exception("Failed to upload image: %s", image_name)
362 raise MessageException(OnboardImageUploadError(str(e))) from e
363
364 finally:
365 _ = [image_hdl.close() for image_hdl in name_hdl_map.values()]
366
367 def extract_charms(self, package):
368 try:
369 charm_extractor = rift.package.charm.PackageCharmExtractor(self.log)
370 charm_extractor.extract_charms(package)
371 except rift.package.charm.CharmExtractionError as e:
372 raise MessageException(UpdateExtractionError()) from e
373
374 def extract_scripts(self, package):
375 try:
376 script_extractor = rift.package.script.PackageScriptExtractor(self.log)
377 script_extractor.extract_scripts(package)
378 except rift.package.script.ScriptExtractionError as e:
379 raise MessageException(UpdateExtractionError()) from e
380
381 def extract_configs(self, package):
382 try:
383 config_extractor = rift.package.config.PackageConfigExtractor(self.log)
384 config_extractor.extract_configs(package)
385 except rift.package.config.ConfigExtractionError as e:
386 raise MessageException(UpdateExtractionError()) from e
387
388 def extract_icons(self, package):
389 try:
390 icon_extractor = rift.package.icon.PackageIconExtractor(self.log)
391 icon_extractor.extract_icons(package)
392 except rift.package.icon.IconExtractionError as e:
393 raise MessageException(UpdateExtractionError()) from e
394
395 def validate_vnfd_fields(self, package):
396 # We can add more VNFD validations here. Currently we are validating only cloud-init
397 if package.descriptor_msg is not None:
398 self.validate_cloud_init_file(package)
399
400 def validate_cloud_init_file(self, package):
401 """ This validation is for VNFDs with associated VDUs. """
402 if 'vdu' in package.descriptor_msg.as_dict():
403 for vdu in package.descriptor_msg.as_dict()['vdu']:
404 if 'cloud_init_file' in vdu:
405 cloud_init_file = vdu['cloud_init_file']
406 for file in package.files:
407 if file.endswith('/' + cloud_init_file) is True:
408 return
409 raise MessageException(
410 OnboardError("Cloud-Init file reference in VNFD does not match with cloud-init file"))
411
412 def validate_package(self, package):
413 checksum_validator = rift.package.package.PackageChecksumValidator(self.log)
414
415 try:
416 file_checksums = checksum_validator.validate(package)
417 except rift.package.package.PackageFileChecksumError as e:
418 raise MessageException(UpdateChecksumMismatch(e.filename)) from e
419 except rift.package.package.PackageValidationError as e:
420 raise MessageException(UpdateUnreadablePackage()) from e
421
422 return file_checksums
423
424 def update_descriptors(self, package):
425 descriptor_msg = package.descriptor_msg
426
427 self.log.message(UpdateDescriptorUpdate())
428
429 try:
430 self.onboarder.update(descriptor_msg)
431 except onboard.UpdateError as e:
432 raise MessageException(UpdateDescriptorError(package.descriptor_file)) from e
433
434
435 class OnboardPackage(downloader.DownloaderProtocol):
436
437 def __init__(self, log, loop, url, auth,
438 onboarder, uploader, package_store_map):
439 self.log = log
440 self.loop = loop
441 self.url = url
442 self.auth = auth
443 self.onboarder = onboarder
444 self.uploader = uploader
445 self.package_store_map = package_store_map
446
447 def _onboard_package(self, packages):
448 # Extract package could return multiple packages if
449 # the package is converted
450 for pkg in packages:
451 with pkg as temp_package:
452 package_checksums = self.validate_package(temp_package)
453 stored_package = self.store_package(temp_package)
454 self.validate_vnfd_fields(temp_package)
455
456 try:
457 self.extract_charms(temp_package)
458 self.extract_scripts(temp_package)
459 self.extract_configs(temp_package)
460 self.extract_icons(temp_package)
461
462 self.onboard_descriptors(temp_package)
463
464 except Exception:
465 self.delete_stored_package(stored_package)
466 raise
467
468 else:
469 self.upload_images(temp_package, package_checksums)
470
471 def extract(self, packages):
472 try:
473 self._onboard_package(packages)
474 self.log.message(OnboardSuccess())
475
476 except MessageException as e:
477 self.log.message(e.msg)
478 self.log.message(OnboardFailure())
479
480 except Exception as e:
481 self.log.exception(e)
482 if str(e):
483 self.log.message(OnboardError(str(e)))
484 self.log.message(OnboardFailure())
485
486 def on_download_succeeded(self, job):
487 self.log.message(DownloadSuccess("Package downloaded."))
488
489 extractor = extract.UploadPackageExtractor(self.log)
490 file_backed_packages = extractor.create_packages_from_upload(
491 job.filename, job.filepath
492 )
493
494 self.extract(file_backed_packages)
495
496 def on_download_failed(self, job):
497 self.log.error(job.detail)
498 self.log.message(DownloadError("Package download failed. {}".format(job.detail)))
499 self.log.message(OnboardFailure())
500
501 def download_package(self):
502
503 _, filename = tempfile.mkstemp()
504 url_downloader = downloader.UrlDownloader(
505 self.url,
506 auth=self.auth,
507 file_obj=filename,
508 decompress_on_fly=True,
509 log=self.log)
510 url_downloader.delegate = self
511 url_downloader.download()
512
513 def get_package_store(self, package):
514 return self.package_store_map[package.descriptor_type]
515
516 def store_package(self, package):
517 store = self.get_package_store(package)
518
519 try:
520 store.store_package(package)
521 except rift.package.store.PackageExistsError as e:
522 store.update_package(package)
523
524 stored_package = store.get_package(package.descriptor_id)
525
526 return stored_package
527
528 def delete_stored_package(self, package):
529 self.log.info("Deleting stored package: %s", package)
530 store = self.get_package_store(package)
531 try:
532 store.delete_package(package.descriptor_id)
533 except Exception as e:
534 self.log.warning("Failed to delete package from store: %s", str(e))
535
536 def upload_images(self, package, package_checksums):
537 image_file_map = rift.package.image.get_package_image_files(package)
538 if not image_file_map:
539 return
540
541 name_hdl_map = {name: package.open(image_file_map[name]) for name in image_file_map}
542 try:
543 for image_name, image_hdl in name_hdl_map.items():
544 image_file = image_file_map[image_name]
545 if image_file in package_checksums:
546 image_checksum = package_checksums[image_file]
547 else:
548 self.log.warning("checksum not provided for image %s. Calculating checksum",
549 image_file)
550 image_checksum = rift.package.checksums.checksum(
551 package.open(image_file_map[image_name])
552 )
553 try:
554 self.uploader.upload_image(image_name, image_checksum, image_hdl)
555 self.uploader.upload_image_to_cloud_accounts(image_name, image_checksum)
556
557 except image.ImageUploadError as e:
558 raise MessageException(OnboardImageUploadError()) from e
559
560 finally:
561 _ = [image_hdl.close() for image_hdl in name_hdl_map.values()]
562
563 def extract_charms(self, package):
564 try:
565 charm_extractor = rift.package.charm.PackageCharmExtractor(self.log)
566 charm_extractor.extract_charms(package)
567 except rift.package.charm.CharmExtractionError as e:
568 raise MessageException(OnboardExtractionError()) from e
569
570 def extract_scripts(self, package):
571 try:
572 script_extractor = rift.package.script.PackageScriptExtractor(self.log)
573 script_extractor.extract_scripts(package)
574 except rift.package.script.ScriptExtractionError as e:
575 raise MessageException(OnboardExtractionError()) from e
576
577 def extract_configs(self, package):
578 try:
579 config_extractor = rift.package.config.PackageConfigExtractor(self.log)
580 config_extractor.extract_configs(package)
581 except rift.package.config.ConfigExtractionError as e:
582 raise MessageException(OnboardExtractionError()) from e
583
584 def extract_icons(self, package):
585 try:
586 icon_extractor = rift.package.icon.PackageIconExtractor(self.log)
587 icon_extractor.extract_icons(package)
588 except rift.package.icon.IconExtractionError as e:
589 raise MessageException(OnboardExtractionError()) from e
590
591 def validate_vnfd_fields(self, package):
592 # We can add more VNFD validations here. Currently we are validating only cloud-init
593 if package.descriptor_msg is not None:
594 self.validate_cloud_init_file(package)
595
596 def validate_cloud_init_file(self, package):
597 """ This validation is for VNFDs with associated VDUs. """
598 if 'vdu' in package.descriptor_msg.as_dict():
599 for vdu in package.descriptor_msg.as_dict()['vdu']:
600 if 'cloud_init_file' in vdu:
601 cloud_init_file = vdu['cloud_init_file']
602 for file in package.files:
603 if file.endswith('/' + cloud_init_file) is True:
604 return
605 raise MessageException(
606 OnboardError("Cloud-Init file reference in VNFD does not match with cloud-init file"))
607
608 def validate_package(self, package):
609 checksum_validator = rift.package.package.PackageChecksumValidator(self.log)
610
611 try:
612 file_checksums = checksum_validator.validate(package)
613 except rift.package.package.PackageFileChecksumError as e:
614 raise MessageException(OnboardChecksumMismatch(e.filename)) from e
615 except rift.package.package.PackageValidationError as e:
616 raise MessageException(OnboardUnreadablePackage()) from e
617
618 return file_checksums
619
620 def onboard_descriptors(self, package):
621 descriptor_msg = package.descriptor_msg
622
623 self.log.message(OnboardDescriptorOnboard())
624
625 try:
626 self.onboarder.onboard(descriptor_msg)
627 except onboard.OnboardError as e:
628 raise MessageException(OnboardDescriptorError(package.descriptor_file)) from e
629
630
631 class UploaderApplication(tornado.web.Application):
632
633 @classmethod
634 def from_tasklet(cls, tasklet):
635 manifest = tasklet.tasklet_info.get_pb_manifest()
636 use_ssl = manifest.bootstrap_phase.rwsecurity.use_ssl
637 ssl_cert = manifest.bootstrap_phase.rwsecurity.cert
638 ssl_key = manifest.bootstrap_phase.rwsecurity.key
639 return cls(
640 tasklet.log,
641 tasklet.dts,
642 tasklet.loop,
643 ssl=(ssl_cert, ssl_key),
644 vnfd_store=tasklet.vnfd_package_store,
645 nsd_store=tasklet.nsd_package_store,
646 vnfd_catalog=tasklet.vnfd_catalog,
647 nsd_catalog=tasklet.nsd_catalog)
648
649 def __init__(
650 self,
651 log,
652 dts,
653 loop,
654 ssl=None,
655 vnfd_store=None,
656 nsd_store=None,
657 vnfd_catalog=None,
658 nsd_catalog=None):
659
660 self.log = log
661 self.loop = loop
662 self.dts = dts
663
664 self.use_ssl = False
665 self.ssl_cert, self.ssl_key = None, None
666 if ssl:
667 self.use_ssl = True
668 self.ssl_cert, self.ssl_key = ssl
669
670 if not vnfd_store:
671 vnfd_store = rift.package.store.VnfdPackageFilesystemStore(self.log)
672
673 if not nsd_store:
674 nsd_store = rift.package.store.NsdPackageFilesystemStore(self.log)
675
676 self.accounts = []
677 self.messages = collections.defaultdict(list)
678 self.export_dir = os.path.join(os.environ['RIFT_ARTIFACTS'], 'launchpad/exports')
679
680 self.uploader = image.ImageUploader(self.log, self.loop, self.dts)
681 self.onboarder = onboard.DescriptorOnboarder(
682 self.log, "127.0.0.1", 8008, self.use_ssl, self.ssl_cert, self.ssl_key
683 )
684 self.package_store_map = {
685 "vnfd": vnfd_store,
686 "nsd": nsd_store
687 }
688
689 self.exporter = export.DescriptorPackageArchiveExporter(self.log)
690 self.loop.create_task(export.periodic_export_cleanup(self.log, self.loop, self.export_dir))
691
692 self.vnfd_catalog = vnfd_catalog
693 self.nsd_catalog = nsd_catalog
694 catalog_map = {
695 "vnfd": self.vnfd_catalog,
696 "nsd": self.nsd_catalog
697 }
698
699 self.upload_handler = UploadRpcHandler(self.log, self.dts, self.loop, self)
700 self.update_handler = UpdateRpcHandler(self.log, self.dts, self.loop, self)
701 self.export_handler = export.ExportRpcHandler(
702 self.log,
703 self.dts,
704 self.loop,
705 self,
706 store_map=self.package_store_map,
707 exporter=self.exporter,
708 catalog_map=catalog_map
709 )
710
711 attrs = dict(log=self.log, loop=self.loop)
712
713 super(UploaderApplication, self).__init__([
714 (r"/api/package/vnfd/(.*)", pkg_handler.FileRestApiHandler, {
715 'path': vnfd_store.root_dir}),
716 (r"/api/package/nsd/(.*)", pkg_handler.FileRestApiHandler, {
717 'path': nsd_store.root_dir}),
718
719 (r"/api/upload/([^/]+)/state", UploadStateHandler, attrs),
720 (r"/api/update/([^/]+)/state", UpdateStateHandler, attrs),
721 (r"/api/export/([^/]+)/state", export.ExportStateHandler, attrs),
722
723 (r"/api/export/([^/]+.tar.gz)", tornado.web.StaticFileHandler, {
724 "path": self.export_dir,
725 }),
726 (r"/api/export/([^/]+.zip)", tornado.web.StaticFileHandler, {
727 "path": self.export_dir,
728 }),
729 ])
730
731 @asyncio.coroutine
732 def register(self):
733 yield from self.upload_handler.register()
734 yield from self.update_handler.register()
735 yield from self.export_handler.register()
736
737 def get_logger(self, transaction_id):
738 return message.Logger(self.log, self.messages[transaction_id])
739
740 def onboard(self, url, transaction_id, auth=None):
741 log = message.Logger(self.log, self.messages[transaction_id])
742
743 onboard_package = OnboardPackage(
744 log,
745 self.loop,
746 url,
747 auth,
748 self.onboarder,
749 self.uploader,
750 self.package_store_map,
751 )
752
753 self.loop.run_in_executor(None, onboard_package.download_package)
754
755 def update(self, url, transaction_id, auth=None):
756 log = message.Logger(self.log, self.messages[transaction_id])
757
758 update_package = UpdatePackage(
759 log,
760 self.loop,
761 url,
762 auth,
763 self.onboarder,
764 self.uploader,
765 self.package_store_map,
766 )
767
768 self.loop.run_in_executor(None, update_package.download_package)
769