Skip to content

Commit

Permalink
Merge pull request juju#949 from cderici/deploy-from-repository-new-e…
Browse files Browse the repository at this point in the history
…ndpoint

juju#949

#### Description

This allows pylibjuju to use the new `DeployFromRepository` endpoint, introduced in the `Application` facade `v19`.

`PendingUploadResources` will be handled in a separate PR.

This also fixes what juju/juju@22e3fc0#diff-cd1b6b10813a1b0ebe7fe9a04f11c401dfeec0574ffb00058bfec98b6bb1d255 seems to be breaking by returning a non-empty error list `(Pdb) self.plan.errors
['', '', '', '', '', '', '', '', '']` from `GetChangesMapArgs`.

#### QA Steps

There's two parts for QAing this. Making sure deploy still works for everything `< 3.3`. CI tests will cover most of that part of the QA, as we use deploy in almost every single integration test, and it's being tested against `latest/stable` (which as of today is `3.1.5`). So it is advisable to do the manual QA (see below) against juju `3.2`, just to be sure.

Second part is obviously making sure that the deploy works for `3.3` with the new endpoint. To do that, I simply 

```sh
 $ juju version
3.3-beta2-ubuntu-amd64
 $ juju bootstrap localhost lxd33 && juju add-model test
```

Then just manually deployed the `ubuntu` charm using repl.

```python
python -m asyncio
asyncio REPL 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> from juju import model;m=model.Model();await m.connect();await m.deploy('ubuntu')
<Application entity_id="ubuntu">
>>>
exiting asyncio REPL...
```

All CI tests need to pass.

#### Notes & Discussion

JUJU-3637
  • Loading branch information
jujubot authored Sep 22, 2023
2 parents 54c49e2 + 9672620 commit 48570bb
Show file tree
Hide file tree
Showing 15 changed files with 55,573 additions and 86 deletions.
2 changes: 1 addition & 1 deletion juju/bundle.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ async def fetch_plan(self, bundle_url, origin, overlays=[]):
bundleurl=entity_id,
yaml=yaml_data)

if self.plan.errors:
if self.plan.errors and any(self.plan.errors):
raise JujuError(self.plan.errors)

async def _download_bundle(self, charm_url, origin):
Expand Down
5 changes: 3 additions & 2 deletions juju/client/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from juju.client._definitions import *


from juju.client import _client7, _client1, _client3, _client4, _client2, _client17, _client6, _client11, _client10, _client5, _client9, _client18
from juju.client import _client7, _client1, _client3, _client4, _client2, _client17, _client6, _client11, _client10, _client5, _client9, _client18, _client19


CLIENTS = {
Expand All @@ -19,7 +19,8 @@
"10": _client10,
"5": _client5,
"9": _client9,
"18": _client18
"18": _client18,
"19": _client19
}


Expand Down
1 change: 0 additions & 1 deletion juju/client/_client10.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ class MachineManagerFacade(Type):
'type': 'array'},
'cost': {'type': 'integer'},
'cpu-cores': {'type': 'integer'},
'deprecated': {'type': 'boolean'},
'memory': {'type': 'integer'},
'name': {'type': 'string'},
'root-disk': {'type': 'integer'},
Expand Down
1,813 changes: 1,813 additions & 0 deletions juju/client/_client19.py

Large diffs are not rendered by default.

354 changes: 354 additions & 0 deletions juju/client/_client2.py

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions juju/client/_client6.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class BundleFacade(Type):
'required': ['message', 'code'],
'type': 'object'},
'ExportBundleParams': {'additionalProperties': False,
'properties': {'include-charm-defaults': {'type': 'boolean'}},
'properties': {'include-charm-defaults': {'type': 'boolean'},
'include-series': {'type': 'boolean'}},
'type': 'object'},
'StringResult': {'additionalProperties': False,
'properties': {'error': {'$ref': '#/definitions/Error'},
Expand Down Expand Up @@ -109,23 +110,28 @@ class BundleFacade(Type):


@ReturnMapping(StringResult)
async def ExportBundle(self, include_charm_defaults=None):
async def ExportBundle(self, include_charm_defaults=None, include_series=None):
'''
ExportBundle exports the current model configuration as bundle.
include_charm_defaults : bool
include_series : bool
Returns -> StringResult
'''
if include_charm_defaults is not None and not isinstance(include_charm_defaults, bool):
raise Exception("Expected include_charm_defaults to be a bool, received: {}".format(type(include_charm_defaults)))

if include_series is not None and not isinstance(include_series, bool):
raise Exception("Expected include_series to be a bool, received: {}".format(type(include_series)))

# map input types to rpc msg
_params = dict()
msg = dict(type='Bundle',
request='ExportBundle',
version=6,
params=_params)
_params['include-charm-defaults'] = include_charm_defaults
_params['include-series'] = include_series
reply = await self.rpc(msg)
return reply

Expand Down
647 changes: 646 additions & 1 deletion juju/client/_client7.py

Large diffs are not rendered by default.

160 changes: 131 additions & 29 deletions juju/client/_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9488,19 +9488,25 @@ def __init__(self, results=None, **unknown_fields):


class ExportBundleParams(Type):
_toSchema = {'include_charm_defaults': 'include-charm-defaults'}
_toPy = {'include-charm-defaults': 'include_charm_defaults'}
def __init__(self, include_charm_defaults=None, **unknown_fields):
_toSchema = {'include_charm_defaults': 'include-charm-defaults', 'include_series': 'include-series'}
_toPy = {'include-charm-defaults': 'include_charm_defaults', 'include-series': 'include_series'}
def __init__(self, include_charm_defaults=None, include_series=None, **unknown_fields):
'''
include_charm_defaults : bool
include_series : bool
'''
include_charm_defaults_ = include_charm_defaults
include_series_ = include_series

# Validate arguments against known Juju API types.
if include_charm_defaults_ is not None and not isinstance(include_charm_defaults_, bool):
raise Exception("Expected include_charm_defaults_ to be a bool, received: {}".format(type(include_charm_defaults_)))

if include_series_ is not None and not isinstance(include_series_, bool):
raise Exception("Expected include_series_ to be a bool, received: {}".format(type(include_series_)))

self.include_charm_defaults = include_charm_defaults_
self.include_series = include_series_
self.unknown_fields = unknown_fields


Expand Down Expand Up @@ -11062,6 +11068,30 @@ def __init__(self, args=None, **unknown_fields):



class GrantRevokeUserSecretArg(Type):
_toSchema = {'applications': 'applications', 'uri': 'uri'}
_toPy = {'applications': 'applications', 'uri': 'uri'}
def __init__(self, applications=None, uri=None, **unknown_fields):
'''
applications : typing.Sequence[str]
uri : str
'''
applications_ = applications
uri_ = uri

# Validate arguments against known Juju API types.
if applications_ is not None and not isinstance(applications_, (bytes, str, list)):
raise Exception("Expected applications_ to be a Sequence, received: {}".format(type(applications_)))

if uri_ is not None and not isinstance(uri_, (bytes, str)):
raise Exception("Expected uri_ to be a str, received: {}".format(type(uri_)))

self.applications = applications_
self.uri = uri_
self.unknown_fields = unknown_fields



class HardwareCharacteristics(Type):
_toSchema = {'arch': 'arch', 'availability_zone': 'availability-zone', 'cpu_cores': 'cpu-cores', 'cpu_power': 'cpu-power', 'mem': 'mem', 'root_disk': 'root-disk', 'root_disk_source': 'root-disk-source', 'tags': 'tags', 'virt_type': 'virt-type'}
_toPy = {'arch': 'arch', 'availability-zone': 'availability_zone', 'cpu-cores': 'cpu_cores', 'cpu-power': 'cpu_power', 'mem': 'mem', 'root-disk': 'root_disk', 'root-disk-source': 'root_disk_source', 'tags': 'tags', 'virt-type': 'virt_type'}
Expand Down Expand Up @@ -11717,14 +11747,13 @@ def __init__(self, characteristics=None, charm_profiles=None, display_name=None,


class InstanceType(Type):
_toSchema = {'arches': 'arches', 'cost': 'cost', 'cpu_cores': 'cpu-cores', 'deprecated': 'deprecated', 'memory': 'memory', 'name': 'name', 'root_disk': 'root-disk', 'virt_type': 'virt-type'}
_toPy = {'arches': 'arches', 'cost': 'cost', 'cpu-cores': 'cpu_cores', 'deprecated': 'deprecated', 'memory': 'memory', 'name': 'name', 'root-disk': 'root_disk', 'virt-type': 'virt_type'}
def __init__(self, arches=None, cost=None, cpu_cores=None, deprecated=None, memory=None, name=None, root_disk=None, virt_type=None, **unknown_fields):
_toSchema = {'arches': 'arches', 'cost': 'cost', 'cpu_cores': 'cpu-cores', 'memory': 'memory', 'name': 'name', 'root_disk': 'root-disk', 'virt_type': 'virt-type'}
_toPy = {'arches': 'arches', 'cost': 'cost', 'cpu-cores': 'cpu_cores', 'memory': 'memory', 'name': 'name', 'root-disk': 'root_disk', 'virt-type': 'virt_type'}
def __init__(self, arches=None, cost=None, cpu_cores=None, memory=None, name=None, root_disk=None, virt_type=None, **unknown_fields):
'''
arches : typing.Sequence[str]
cost : int
cpu_cores : int
deprecated : bool
memory : int
name : str
root_disk : int
Expand All @@ -11733,7 +11762,6 @@ def __init__(self, arches=None, cost=None, cpu_cores=None, deprecated=None, memo
arches_ = arches
cost_ = cost
cpu_cores_ = cpu_cores
deprecated_ = deprecated
memory_ = memory
name_ = name
root_disk_ = root_disk
Expand All @@ -11749,9 +11777,6 @@ def __init__(self, arches=None, cost=None, cpu_cores=None, deprecated=None, memo
if cpu_cores_ is not None and not isinstance(cpu_cores_, int):
raise Exception("Expected cpu_cores_ to be a int, received: {}".format(type(cpu_cores_)))

if deprecated_ is not None and not isinstance(deprecated_, bool):
raise Exception("Expected deprecated_ to be a bool, received: {}".format(type(deprecated_)))

if memory_ is not None and not isinstance(memory_, int):
raise Exception("Expected memory_ to be a int, received: {}".format(type(memory_)))

Expand All @@ -11767,7 +11792,6 @@ def __init__(self, arches=None, cost=None, cpu_cores=None, deprecated=None, memo
self.arches = arches_
self.cost = cost_
self.cpu_cores = cpu_cores_
self.deprecated = deprecated_
self.memory = memory_
self.name = name_
self.root_disk = root_disk_
Expand Down Expand Up @@ -18057,19 +18081,17 @@ def __init__(self, results=None, **unknown_fields):


class PendingResourceUpload(Type):
_toSchema = {'filename': 'Filename', 'name': 'Name', 'pending_id': 'pending-id', 'type_': 'Type'}
_toPy = {'Filename': 'filename', 'Name': 'name', 'Type': 'type_', 'pending-id': 'pending_id'}
def __init__(self, filename=None, name=None, type_=None, pending_id=None, **unknown_fields):
_toSchema = {'filename': 'Filename', 'name': 'Name', 'type_': 'Type'}
_toPy = {'Filename': 'filename', 'Name': 'name', 'Type': 'type_'}
def __init__(self, filename=None, name=None, type_=None, **unknown_fields):
'''
filename : str
name : str
type_ : str
pending_id : str
'''
filename_ = filename
name_ = name
type__ = type_
pending_id_ = pending_id

# Validate arguments against known Juju API types.
if filename_ is not None and not isinstance(filename_, (bytes, str)):
Expand All @@ -18081,13 +18103,9 @@ def __init__(self, filename=None, name=None, type_=None, pending_id=None, **unkn
if type__ is not None and not isinstance(type__, (bytes, str)):
raise Exception("Expected type__ to be a str, received: {}".format(type(type__)))

if pending_id_ is not None and not isinstance(pending_id_, (bytes, str)):
raise Exception("Expected pending_id_ to be a str, received: {}".format(type(pending_id_)))

self.filename = filename_
self.name = name_
self.type_ = type__
self.pending_id = pending_id_
self.unknown_fields = unknown_fields


Expand Down Expand Up @@ -20904,18 +20922,18 @@ def __init__(self, charm_origin=None, reference=None, switch_charm=None, **unkno


class ResolveCharmWithChannelResult(Type):
_toSchema = {'charm_origin': 'charm-origin', 'error': 'error', 'supported_series': 'supported-series', 'url': 'url'}
_toPy = {'charm-origin': 'charm_origin', 'error': 'error', 'supported-series': 'supported_series', 'url': 'url'}
def __init__(self, charm_origin=None, error=None, supported_series=None, url=None, **unknown_fields):
_toSchema = {'charm_origin': 'charm-origin', 'error': 'error', 'supported_bases': 'supported-bases', 'url': 'url'}
_toPy = {'charm-origin': 'charm_origin', 'error': 'error', 'supported-bases': 'supported_bases', 'url': 'url'}
def __init__(self, charm_origin=None, error=None, supported_bases=None, url=None, **unknown_fields):
'''
charm_origin : CharmOrigin
error : Error
supported_series : typing.Sequence[str]
supported_bases : typing.Sequence[~Base]
url : str
'''
charm_origin_ = CharmOrigin.from_json(charm_origin) if charm_origin else None
error_ = Error.from_json(error) if error else None
supported_series_ = supported_series
supported_bases_ = [Base.from_json(o) for o in supported_bases or []]
url_ = url

# Validate arguments against known Juju API types.
Expand All @@ -20925,15 +20943,15 @@ def __init__(self, charm_origin=None, error=None, supported_series=None, url=Non
if error_ is not None and not isinstance(error_, (dict, Error)):
raise Exception("Expected error_ to be a Error, received: {}".format(type(error_)))

if supported_series_ is not None and not isinstance(supported_series_, (bytes, str, list)):
raise Exception("Expected supported_series_ to be a Sequence, received: {}".format(type(supported_series_)))
if supported_bases_ is not None and not isinstance(supported_bases_, (bytes, str, list)):
raise Exception("Expected supported_bases_ to be a Sequence, received: {}".format(type(supported_bases_)))

if url_ is not None and not isinstance(url_, (bytes, str)):
raise Exception("Expected url_ to be a str, received: {}".format(type(url_)))

self.charm_origin = charm_origin_
self.error = error_
self.supported_series = supported_series_
self.supported_bases = supported_bases_
self.url = url_
self.unknown_fields = unknown_fields

Expand Down Expand Up @@ -26194,6 +26212,90 @@ def __init__(self, args=None, **unknown_fields):



class UpdateUserSecretArg(Type):
_toSchema = {'auto_prune': 'auto-prune', 'content': 'content', 'description': 'description', 'expire_time': 'expire-time', 'label': 'label', 'params': 'params', 'rotate_policy': 'rotate-policy', 'upsertsecretarg': 'UpsertSecretArg', 'uri': 'uri'}
_toPy = {'UpsertSecretArg': 'upsertsecretarg', 'auto-prune': 'auto_prune', 'content': 'content', 'description': 'description', 'expire-time': 'expire_time', 'label': 'label', 'params': 'params', 'rotate-policy': 'rotate_policy', 'uri': 'uri'}
def __init__(self, upsertsecretarg=None, auto_prune=None, content=None, description=None, expire_time=None, label=None, params=None, rotate_policy=None, uri=None, **unknown_fields):
'''
upsertsecretarg : UpsertSecretArg
auto_prune : bool
content : SecretContentParams
description : str
expire_time : str
label : str
params : typing.Mapping[str, typing.Any]
rotate_policy : str
uri : str
'''
upsertsecretarg_ = UpsertSecretArg.from_json(upsertsecretarg) if upsertsecretarg else None
auto_prune_ = auto_prune
content_ = SecretContentParams.from_json(content) if content else None
description_ = description
expire_time_ = expire_time
label_ = label
params_ = params
rotate_policy_ = rotate_policy
uri_ = uri

# Validate arguments against known Juju API types.
if upsertsecretarg_ is not None and not isinstance(upsertsecretarg_, (dict, UpsertSecretArg)):
raise Exception("Expected upsertsecretarg_ to be a UpsertSecretArg, received: {}".format(type(upsertsecretarg_)))

if auto_prune_ is not None and not isinstance(auto_prune_, bool):
raise Exception("Expected auto_prune_ to be a bool, received: {}".format(type(auto_prune_)))

if content_ is not None and not isinstance(content_, (dict, SecretContentParams)):
raise Exception("Expected content_ to be a SecretContentParams, received: {}".format(type(content_)))

if description_ is not None and not isinstance(description_, (bytes, str)):
raise Exception("Expected description_ to be a str, received: {}".format(type(description_)))

if expire_time_ is not None and not isinstance(expire_time_, (bytes, str)):
raise Exception("Expected expire_time_ to be a str, received: {}".format(type(expire_time_)))

if label_ is not None and not isinstance(label_, (bytes, str)):
raise Exception("Expected label_ to be a str, received: {}".format(type(label_)))

if params_ is not None and not isinstance(params_, dict):
raise Exception("Expected params_ to be a Mapping, received: {}".format(type(params_)))

if rotate_policy_ is not None and not isinstance(rotate_policy_, (bytes, str)):
raise Exception("Expected rotate_policy_ to be a str, received: {}".format(type(rotate_policy_)))

if uri_ is not None and not isinstance(uri_, (bytes, str)):
raise Exception("Expected uri_ to be a str, received: {}".format(type(uri_)))

self.upsertsecretarg = upsertsecretarg_
self.auto_prune = auto_prune_
self.content = content_
self.description = description_
self.expire_time = expire_time_
self.label = label_
self.params = params_
self.rotate_policy = rotate_policy_
self.uri = uri_
self.unknown_fields = unknown_fields



class UpdateUserSecretArgs(Type):
_toSchema = {'args': 'args'}
_toPy = {'args': 'args'}
def __init__(self, args=None, **unknown_fields):
'''
args : typing.Sequence[~UpdateUserSecretArg]
'''
args_ = [UpdateUserSecretArg.from_json(o) for o in args or []]

# Validate arguments against known Juju API types.
if args_ is not None and not isinstance(args_, (bytes, str, list)):
raise Exception("Expected args_ to be a Sequence, received: {}".format(type(args_)))

self.args = args_
self.unknown_fields = unknown_fields



class UpgradeModelParams(Type):
_toSchema = {'agent_stream': 'agent-stream', 'dry_run': 'dry-run', 'ignore_agent_versions': 'ignore-agent-versions', 'model_tag': 'model-tag', 'target_version': 'target-version'}
_toPy = {'agent-stream': 'agent_stream', 'dry-run': 'dry_run', 'ignore-agent-versions': 'ignore_agent_versions', 'model-tag': 'model_tag', 'target-version': 'target_version'}
Expand Down
2 changes: 1 addition & 1 deletion juju/client/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
'AllModelWatcher': {'versions': [2, 3, 4]},
'AllWatcher': {'versions': [1, 2, 3, 4]},
'Annotations': {'versions': [2]},
'Application': {'versions': [14, 15, 16, 17]},
'Application': {'versions': [14, 15, 16, 17, 19]},
'ApplicationOffers': {'versions': [1, 2, 4]},
'ApplicationScaler': {'versions': [1]},
'Backups': {'versions': [1, 2, 3]},
Expand Down
Loading

0 comments on commit 48570bb

Please sign in to comment.