11import asyncio
22import logging
33import os
4+ import zipfile
45from pathlib import Path
56
67import yaml
@@ -72,7 +73,7 @@ async def _validate_bundle(self, bundle):
7273 )
7374 return bundle
7475
75- async def _handle_local_charms (self , bundle ):
76+ async def _handle_local_charms (self , bundle , bundle_dir ):
7677 """Search for references to local charms (i.e. filesystem paths)
7778 in the bundle. Upload the local charms to the model, and replace
7879 the filesystem paths with appropriate 'local:' paths in the bundle.
@@ -89,9 +90,16 @@ async def _handle_local_charms(self, bundle):
8990 apps_dict = bundle .get ('applications' , bundle .get ('services' , {}))
9091 for app_name in self .applications :
9192 app_dict = apps_dict [app_name ]
92- charm_dir = os .path .abspath (os .path .expanduser (app_dict ['charm' ]))
93- if not os .path .isdir (charm_dir ):
94- continue
93+ charm_dir = app_dict ['charm' ]
94+ try :
95+ charm_path = (bundle_dir / charm_dir ).resolve ()
96+ if not (charm_path .is_dir () or
97+ (charm_path .is_file () and
98+ charm_path .suffix in ('.charm' , '.zip' ))):
99+ continue
100+ charm_dir = str (charm_path )
101+ except ValueError :
102+ pass
95103 series = (
96104 app_dict .get ('series' ) or
97105 default_series or
@@ -122,18 +130,24 @@ async def _handle_local_charms(self, bundle):
122130
123131 async def fetch_plan (self , entity_id ):
124132 is_store_url = entity_id .startswith ('cs:' )
133+ is_local = False
134+ bundle_dir = None
125135
126136 if not is_store_url and os .path .isfile (entity_id ):
127137 bundle_yaml = Path (entity_id ).read_text ()
138+ is_local = True
139+ bundle_dir = Path (entity_id ).parent
128140 elif not is_store_url and os .path .isdir (entity_id ):
129141 bundle_yaml = (Path (entity_id ) / "bundle.yaml" ).read_text ()
142+ bundle_dir = Path (entity_id )
130143 else :
131144 bundle_yaml = await self .charmstore .files (entity_id ,
132145 filename = 'bundle.yaml' ,
133146 read_file = True )
134147 self .bundle = yaml .safe_load (bundle_yaml )
135148 self .bundle = await self ._validate_bundle (self .bundle )
136- self .bundle = await self ._handle_local_charms (self .bundle )
149+ if is_local :
150+ self .bundle = await self ._handle_local_charms (self .bundle , bundle_dir )
137151
138152 self .plan = await self .bundle_facade .GetChanges (
139153 bundleurl = entity_id ,
@@ -179,11 +193,16 @@ def get_charm_series(path):
179193
180194 Returns None if no series can be determined.
181195 """
182- md = Path (path ) / "metadata.yaml"
183- if not md .exists ():
184- return None
185196 try :
186- data = yaml .safe_load (md .open ())
197+ if path .endswith ('.charm' ):
198+ md = "metadata.yaml in %s" % path
199+ with zipfile .ZipFile (path , 'r' ) as charm_file :
200+ data = yaml .safe_load (charm_file .read ('metadata.yaml' ))
201+ else :
202+ md = Path (path ) / "metadata.yaml"
203+ if not md .exists ():
204+ return None
205+ data = yaml .safe_load (md .open ())
187206 except yaml .YAMLError as exc :
188207 if hasattr (exc , "problem_mark" ):
189208 mark = exc .problem_mark
0 commit comments