@@ -1392,7 +1392,7 @@ async def deploy(
13921392
13931393 TODO::
13941394
1395- - support local resources
1395+ - support local file resources
13961396
13971397 """
13981398 if storage :
@@ -1474,10 +1474,16 @@ async def deploy(
14741474 "Pass a 'series' kwarg to Model.deploy()." .format (
14751475 charm_dir ))
14761476 entity_id = await self .add_local_charm_dir (charm_dir , series )
1477+ resources = await self ._add_local_resources (application_name ,
1478+ entity_id ,
1479+ metadata ,
1480+ resources = resources )
1481+
14771482 if config is None :
14781483 config = {}
14791484 if trust :
14801485 config ["trust" ] = "true"
1486+
14811487 return await self ._deploy (
14821488 charm_url = entity_id ,
14831489 application = application_name ,
@@ -1537,6 +1543,71 @@ async def _add_store_resources(self, application, entity_url,
15371543 in zip (resources , response .pending_ids )}
15381544 return resource_map
15391545
1546+ async def _add_local_resources (self , application , entity_url , metadata , resources ):
1547+ if not resources :
1548+ return None
1549+
1550+ resource_map = dict ()
1551+
1552+ for name , path in resources .items ():
1553+ resource_type = metadata ["resources" ][name ]["type" ]
1554+ if resource_type != "oci-image" :
1555+ # For now only oci-images are supported
1556+ log .info ("Resource {} of type {} is not supported" .format (name , resource_type ))
1557+ continue
1558+
1559+ charmresource = {
1560+ 'description' : '' ,
1561+ 'fingerprint' : '' ,
1562+ 'name' : name ,
1563+ 'path' : path ,
1564+ 'revision' : 0 ,
1565+ 'size' : 0 ,
1566+ 'type_' : 'oci-image' ,
1567+ 'origin' : 'upload' ,
1568+ }
1569+
1570+ resources_facade = client .ResourcesFacade .from_connection (
1571+ self .connection ())
1572+ response = await resources_facade .AddPendingResources (
1573+ application_tag = tag .application (application ),
1574+ charm_url = entity_url ,
1575+ resources = [client .CharmResource (** charmresource )])
1576+ pending_id = response .pending_ids [0 ]
1577+ resource_map [name ] = pending_id
1578+
1579+ # TODO Docker Image validation and support for local images.
1580+ docker_image_details = {
1581+ 'registrypath' : path ,
1582+ 'username' : '' ,
1583+ 'password' : '' ,
1584+ }
1585+
1586+ data = yaml .dump (docker_image_details )
1587+
1588+ charmresource ['fingerprint' ] = hashlib .sha3_384 (bytes (data , 'utf-8' )).digest ()
1589+
1590+ conn , headers , path_prefix = self .connection ().https_connection ()
1591+
1592+ query = "?pendingid={}" .format (pending_id )
1593+ url = "{}/applications/{}/resources/{}{}" .format (
1594+ path_prefix , application , name , query )
1595+ disp = "multipart/form-data; filename=\" {}\" " .format (path )
1596+
1597+ headers ['Content-Type' ] = 'application/octet-stream'
1598+ headers ['Content-Length' ] = len (data )
1599+ headers ['Content-Sha384' ] = charmresource ['fingerprint' ].hex ()
1600+ headers ['Content-Disposition' ] = disp
1601+
1602+ conn .request ('PUT' , url , data , headers )
1603+
1604+ response = conn .getresponse ()
1605+ result = response .read ().decode ()
1606+ if not response .status == 200 :
1607+ raise JujuError (result )
1608+
1609+ return resource_map
1610+
15401611 async def _deploy (self , charm_url , application , series , config ,
15411612 constraints , endpoint_bindings , resources , storage ,
15421613 channel = None , num_units = None , placement = None ,
0 commit comments