Update libjuju
- fix licensing quirks
- refresh against libjuju master
Signed-off-by: Adam Israel <adam.israel@canonical.com>
diff --git a/modules/libjuju/juju/client/_client.py b/modules/libjuju/juju/client/_client.py
index d510e11..2ef0ffd 100644
--- a/modules/libjuju/juju/client/_client.py
+++ b/modules/libjuju/juju/client/_client.py
@@ -1,7 +1,7 @@
# DO NOT CHANGE THIS FILE! This file is auto-generated by facade.py.
# Changes will be overwritten/lost when the file is regenerated.
-from juju.client._definitions import *
+from juju.client._definitions import * # noqa
from juju.client import _client1, _client2, _client3, _client4, _client5
@@ -15,22 +15,21 @@
}
-
def lookup_facade(name, version):
"""
Given a facade name and version, attempt to pull that facade out
of the correct client<version>.py file.
"""
- try:
- facade = getattr(CLIENTS[str(version)], name)
- except KeyError:
- raise ImportError("No facades found for version {}".format(version))
- except AttributeError:
- raise ImportError(
- "No facade with name '{}' in version {}".format(name, version))
- return facade
-
+ for _version in range(int(version), 0, -1):
+ try:
+ facade = getattr(CLIENTS[str(_version)], name)
+ return facade
+ except (KeyError, AttributeError):
+ continue
+ else:
+ raise ImportError("No supported version for facade: "
+ "{}".format(name))
class TypeFactory:
@@ -363,5 +362,3 @@
class VolumeAttachmentsWatcherFacade(TypeFactory):
pass
-
-
diff --git a/modules/libjuju/juju/client/client.py b/modules/libjuju/juju/client/client.py
index 89b5248..2f3e49d 100644
--- a/modules/libjuju/juju/client/client.py
+++ b/modules/libjuju/juju/client/client.py
@@ -5,7 +5,7 @@
for o in overrides.__all__:
- if not "Facade" in o:
+ if "Facade" not in o:
# Override stuff in _definitions, which is all imported
# into _client. We Monkey patch both the original class and
# the ref in _client (import shenanigans are fun!)
diff --git a/modules/libjuju/juju/client/connection.py b/modules/libjuju/juju/client/connection.py
index 7457391..c09468c 100644
--- a/modules/libjuju/juju/client/connection.py
+++ b/modules/libjuju/juju/client/connection.py
@@ -413,7 +413,7 @@
endpoints.extend(new_endpoints)
else:
# ran out of endpoints without a successful login
- raise Exception("Couldn't authenticate to {}".format(
+ raise JujuConnectionError("Couldn't authenticate to {}".format(
self._endpoint))
response = result['response']
@@ -584,6 +584,38 @@
def accounts(self):
return self._load_yaml('accounts.yaml', 'controllers')
+ def credentials(self):
+ return self._load_yaml('credentials.yaml', 'credentials')
+
+ def load_credential(self, cloud, name=None):
+ """Load a local credential.
+
+ :param str cloud: Name of cloud to load credentials from.
+ :param str name: Name of credential. If None, the default credential
+ will be used, if available.
+ :returns: A CloudCredential instance, or None.
+ """
+ try:
+ cloud = tag.untag('cloud-', cloud)
+ creds_data = self.credentials()[cloud]
+ if not name:
+ default_credential = creds_data.pop('default-credential', None)
+ default_region = creds_data.pop('default-region', None) # noqa
+ if default_credential:
+ name = creds_data['default-credential']
+ elif len(creds_data) == 1:
+ name = list(creds_data)[0]
+ else:
+ return None, None
+ cred_data = creds_data[name]
+ auth_type = cred_data.pop('auth-type')
+ return name, client.CloudCredential(
+ auth_type=auth_type,
+ attrs=cred_data,
+ )
+ except (KeyError, FileNotFoundError):
+ return None, None
+
def _load_yaml(self, filename, key):
filepath = os.path.join(self.path, filename)
with io.open(filepath, 'rt') as f:
diff --git a/modules/libjuju/juju/client/facade.py b/modules/libjuju/juju/client/facade.py
index c959e01..c015c5f 100644
--- a/modules/libjuju/juju/client/facade.py
+++ b/modules/libjuju/juju/client/facade.py
@@ -44,19 +44,19 @@
# Classes and helper functions that we'll write to _client.py
LOOKUP_FACADE = '''
def lookup_facade(name, version):
- """
- Given a facade name and version, attempt to pull that facade out
- of the correct client<version>.py file.
+ """
+ Given a facade name and version, attempt to pull that facade out
+ of the correct client<version>.py file.
- """
- try:
- facade = getattr(CLIENTS[str(version)], name)
- except KeyError:
- raise ImportError("No facades found for version {}".format(version))
- except AttributeError:
- raise ImportError(
- "No facade with name '{}' in version {}".format(name, version))
- return facade
+ """
+ try:
+ facade = getattr(CLIENTS[str(version)], name)
+ except KeyError:
+ raise ImportError("No facades found for version {}".format(version))
+ except AttributeError:
+ raise ImportError(
+ "No facade with name '{}' in version {}".format(name, version))
+ return facade
'''
@@ -127,6 +127,7 @@
return self[refname]
+
_types = TypeRegistry()
_registry = KindRegistry()
CLASSES = {}
@@ -257,7 +258,7 @@
for kind in sorted((k for k in _types if not isinstance(k, str)),
key=lambda x: str(x)):
name = _types[kind]
- if name in capture and not name in NAUGHTY_CLASSES:
+ if name in capture and name not in NAUGHTY_CLASSES:
continue
args = Args(kind)
# Write Factory class for _client.py
@@ -277,9 +278,7 @@
pprint.pformat(args.SchemaToPyMapping(), width=999),
", " if args else "",
args.as_kwargs(),
- textwrap.indent(args.get_doc(), INDENT * 2))
- ]
- assignments = args._get_arg_str(False, False)
+ textwrap.indent(args.get_doc(), INDENT * 2))]
if not args:
source.append("{}pass".format(INDENT * 2))
@@ -289,7 +288,9 @@
arg_type = arg[1]
arg_type_name = strcast(arg_type)
if arg_type in basic_types:
- source.append("{}self.{} = {}".format(INDENT * 2, arg_name, arg_name))
+ source.append("{}self.{} = {}".format(INDENT * 2,
+ arg_name,
+ arg_name))
elif issubclass(arg_type, typing.Sequence):
value_type = (
arg_type_name.__parameters__[0]
@@ -297,10 +298,16 @@
else None
)
if type(value_type) is typing.TypeVar:
- source.append("{}self.{} = [{}.from_json(o) for o in {} or []]".format(
- INDENT * 2, arg_name, strcast(value_type), arg_name))
+ source.append(
+ "{}self.{} = [{}.from_json(o) "
+ "for o in {} or []]".format(INDENT * 2,
+ arg_name,
+ strcast(value_type),
+ arg_name))
else:
- source.append("{}self.{} = {}".format(INDENT * 2, arg_name, arg_name))
+ source.append("{}self.{} = {}".format(INDENT * 2,
+ arg_name,
+ arg_name))
elif issubclass(arg_type, typing.Mapping):
value_type = (
arg_type_name.__parameters__[1]
@@ -308,15 +315,28 @@
else None
)
if type(value_type) is typing.TypeVar:
- source.append("{}self.{} = {{k: {}.from_json(v) for k, v in ({} or dict()).items()}}".format(
- INDENT * 2, arg_name, strcast(value_type), arg_name))
+ source.append(
+ "{}self.{} = {{k: {}.from_json(v) "
+ "for k, v in ({} or dict()).items()}}".format(
+ INDENT * 2,
+ arg_name,
+ strcast(value_type),
+ arg_name))
else:
- source.append("{}self.{} = {}".format(INDENT * 2, arg_name, arg_name))
+ source.append("{}self.{} = {}".format(INDENT * 2,
+ arg_name,
+ arg_name))
elif type(arg_type) is typing.TypeVar:
- source.append("{}self.{} = {}.from_json({}) if {} else None".format(
- INDENT * 2, arg_name, arg_type_name, arg_name, arg_name))
+ source.append("{}self.{} = {}.from_json({}) "
+ "if {} else None".format(INDENT * 2,
+ arg_name,
+ arg_type_name,
+ arg_name,
+ arg_name))
else:
- source.append("{}self.{} = {}".format(INDENT * 2, arg_name, arg_name))
+ source.append("{}self.{} = {}".format(INDENT * 2,
+ arg_name,
+ arg_name))
source = "\n".join(source)
capture.clear(name)
@@ -435,7 +455,10 @@
'''
# map input types to rpc msg
_params = dict()
- msg = dict(type='{cls.name}', request='{name}', version={cls.version}, params=_params)
+ msg = dict(type='{cls.name}',
+ request='{name}',
+ version={cls.version},
+ params=_params)
{assignments}
reply = {await}self.rpc(msg)
return reply
@@ -539,7 +562,7 @@
return d
def to_json(self):
- return json.dumps(self.serialize())
+ return json.dumps(self.serialize(), cls=TypeEncoder, sort_keys=True)
class Schema(dict):
@@ -576,7 +599,7 @@
if not defs:
return
for d, data in defs.items():
- if d in _registry and not d in NAUGHTY_CLASSES:
+ if d in _registry and d not in NAUGHTY_CLASSES:
continue
node = self.deref(data, d)
kind = node.get("type")
@@ -762,6 +785,7 @@
return captures
+
def setup():
parser = argparse.ArgumentParser()
parser.add_argument("-s", "--schema", default="juju/client/schemas*")
@@ -769,6 +793,7 @@
options = parser.parse_args()
return options
+
def main():
options = setup()
@@ -780,5 +805,6 @@
write_definitions(captures, options, last_version)
write_client(captures, options)
+
if __name__ == '__main__':
main()
diff --git a/modules/libjuju/juju/client/overrides.py b/modules/libjuju/juju/client/overrides.py
index f439adb..5e98e56 100644
--- a/modules/libjuju/juju/client/overrides.py
+++ b/modules/libjuju/juju/client/overrides.py
@@ -11,6 +11,7 @@
'Number',
'Binary',
'ConfigValue',
+ 'Resource',
]
__patches__ = [
@@ -273,3 +274,47 @@
return '<{} source={} value={}>'.format(type(self).__name__,
repr(self.source),
repr(self.value))
+
+
+class Resource(Type):
+ _toSchema = {'application': 'application',
+ 'charmresource': 'CharmResource',
+ 'id_': 'id',
+ 'pending_id': 'pending-id',
+ 'timestamp': 'timestamp',
+ 'username': 'username',
+ 'name': 'name',
+ 'origin': 'origin'}
+ _toPy = {'CharmResource': 'charmresource',
+ 'application': 'application',
+ 'id': 'id_',
+ 'pending-id': 'pending_id',
+ 'timestamp': 'timestamp',
+ 'username': 'username',
+ 'name': 'name',
+ 'origin': 'origin'}
+
+ def __init__(self, charmresource=None, application=None, id_=None,
+ pending_id=None, timestamp=None, username=None, name=None,
+ origin=None, **unknown_fields):
+ '''
+ charmresource : CharmResource
+ application : str
+ id_ : str
+ pending_id : str
+ timestamp : str
+ username : str
+ name: str
+ origin : str
+ '''
+ if charmresource:
+ self.charmresource = _client.CharmResource.from_json(charmresource)
+ else:
+ self.charmresource = None
+ self.application = application
+ self.id_ = id_
+ self.pending_id = pending_id
+ self.timestamp = timestamp
+ self.username = username
+ self.name = name
+ self.origin = origin
diff --git a/modules/libjuju/juju/client/runner.py b/modules/libjuju/juju/client/runner.py
index 61f2963..6545bc4 100644
--- a/modules/libjuju/juju/client/runner.py
+++ b/modules/libjuju/juju/client/runner.py
@@ -1,6 +1,4 @@
-
-
class AsyncRunner:
async def __call__(self, facade_method, *args, **kwargs):
await self.connection.rpc(facade_method(*args, **kwargs))
@@ -15,14 +13,9 @@
# This could let us fake the protocol we want
# while decoupling the protocol from the RPC and the IO/Process context
-# The problem is leaking the runtime impl details to the top levels of the API with
-# async def
-# By handling the Marshal/Unmarshal side of RPC as a protocol we can leave the RPC running to a specific
-# delegate without altering the method signatures.
-# This still isn't quite right though as async is co-op multitasking and the methods still need to know
-# not to block or they will pause other execution
-
-
-
-
-
+# The problem is leaking the runtime impl details to the top levels of the API
+# with async def By handling the Marshal/Unmarshal side of RPC as a protocol we
+# can leave the RPC running to a specific delegate without altering the method
+# signatures. This still isn't quite right though as async is co-op
+# multitasking and the methods still need to know not to block or they will
+# pause other execution