From e0413adff0c40fd7ffb7ea6140f360a0d373679d Mon Sep 17 00:00:00 2001 From: Dima Tisnek Date: Fri, 1 Nov 2024 12:07:09 +0100 Subject: [PATCH] feat: let juju select best base Charms v7 no longer excluded --- .github/workflows/test.yaml | 8 ++--- juju/client/facade_versions.py | 4 +-- juju/model.py | 55 +++++++++++++++++++++++----------- juju/utils.py | 6 +++- juju/version.py | 17 ++++++++++- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c9827512..f3b7a7ed 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -84,12 +84,7 @@ jobs: - "3.3/stable" - "3.4/stable" - "3.5/stable" - # A bunch of tests fail with juju.errors.JujuError: base: ubuntu@15.04/stable - # * test_subordinate_units - # * test_destroy_unit - # * test_ssh - # * ... - # - "3.6/beta" + - "3.6/candidate" steps: - name: Check out code uses: actions/checkout@v4 @@ -149,6 +144,7 @@ jobs: - "3.3/stable" - "3.4/stable" - "3.5/stable" + - "3.6/candidate" steps: - name: Check out code uses: actions/checkout@v4 diff --git a/juju/client/facade_versions.py b/juju/client/facade_versions.py index a4f7cf29..b59b0fce 100644 --- a/juju/client/facade_versions.py +++ b/juju/client/facade_versions.py @@ -17,7 +17,7 @@ "Backups": (3,), "Block": (2,), "Bundle": (6,), - "Charms": (6,), + "Charms": (6, 7), "Client": (6, 7, 8), "Cloud": (7,), "Controller": (11, 12), @@ -45,7 +45,7 @@ } # Manual list of facades present in schemas + codegen which python-libjuju does not yet support -excluded_facade_versions: Dict[str, Sequence[int]] = {"Charms": (7,)} +excluded_facade_versions: Dict[str, Sequence[int]] = {} # We don't generate code for these, as we can never use them. diff --git a/juju/model.py b/juju/model.py index ada4ac29..065d9bef 100644 --- a/juju/model.py +++ b/juju/model.py @@ -24,7 +24,7 @@ import websockets import yaml -from . import jasyncio, provisioner, tag, utils +from . import jasyncio, provisioner, tag, utils, version from .annotationhelper import _get_annotations, _set_annotations from .bundle import BundleHandler, get_charm_series, is_local_charm from .charmhub import CharmHub @@ -56,7 +56,6 @@ from .secrets import create_secret_data, read_secret_data from .tag import application as application_tag from .url import URL, Schema -from .version import DEFAULT_ARCHITECTURE log = logging.getLogger(__name__) @@ -468,7 +467,7 @@ async def resolve( architecture, app_name=None, channel=None, - series=None, + series: str | None = None, revision=None, entity_url=None, force=False, @@ -1731,7 +1730,7 @@ async def deploy( force=False, num_units=1, overlays=[], - base=None, + base: str | None = None, resources=None, series=None, revision=None, @@ -1810,6 +1809,11 @@ async def deploy( if schema not in self.deploy_types: raise JujuError(f"unknown deploy type {schema}, expected charmhub or local") + if series and base: + derisked = base.split("/")[0] + if derisked != version.SERIES_TO_BASE.get(series): + raise JujuError(f"Incompatible {series=} and {base=}") + model_conf = await self.get_config() res = await self.deploy_types[schema].resolve( entity, @@ -1966,7 +1970,7 @@ async def _add_charm(self, charm_url, origin): ) async def _resolve_charm( - self, url, origin, force=False, series=None, model_config=None + self, url, origin, force=False, series: str | None = None, model_config=None ): """Calls Charms.ResolveCharms to resolve all the fields of the charm_origin and also the url and the supported_series @@ -2011,21 +2015,36 @@ async def _resolve_charm( if result.error: raise JujuError(f"resolving {url} : {result.error.message}") - # TODO (cderici) : supported_bases - supported_series = result.get( - "supported_series", result.unknown_fields["supported-series"] - ) resolved_origin = result.charm_origin charm_url = URL.parse(result.url) - # run the series selector to get a series for the base - selected_series = utils.series_selector( - series, charm_url, model_config, supported_series, force - ) - result.charm_origin.base = utils.get_base_from_origin_or_channel( - resolved_origin, selected_series - ) - charm_url.series = selected_series + if "supported-series" in result.unknown_fields: + # Legacy code path for Charms v6, that is Juju < 3.3.0 + supported_series: list[str] = result.unknown_fields["supported-series"] + # run the series selector to get a series for the base + selected_series = utils.series_selector( + series, charm_url, model_config, supported_series, force + ) + result.charm_origin.base = utils.get_base_from_origin_or_channel( + resolved_origin, selected_series + ) + charm_url.series = selected_series + else: + # FIXME use base instead + ... + + if series: + # FIXME: redo this + # Ugh this ugly, + # series is valid against Juju 3.0~3.2.x + # base is valid against Juju 3.3~4.x + # we should convert back and forth really... + warnings.warn( + "series= argument is deprecated, use base= instead", + DeprecationWarning, + stacklevel=4, + ) + # FIXME actually use the argument return charm_url, resolved_origin @@ -2045,7 +2064,7 @@ async def _resolve_architecture(self, url=None): if "arch" in constraints: return constraints["arch"] - return DEFAULT_ARCHITECTURE + return version.DEFAULT_ARCHITECTURE async def _add_charmhub_resources( self, application, entity_url, origin, overrides=None diff --git a/juju/utils.py b/juju/utils.py index 710fcc56..515c4b49 100644 --- a/juju/utils.py +++ b/juju/utils.py @@ -516,7 +516,11 @@ def user_requested(series_arg, supported_series, force): def series_selector( - series_arg="", charm_url=None, model_config=None, supported_series=[], force=False + series_arg: str | None = "", + charm_url=None, + model_config=None, + supported_series=[], + force=False, ): """Select series to deploy on. diff --git a/juju/version.py b/juju/version.py index a0022b09..484f75cc 100644 --- a/juju/version.py +++ b/juju/version.py @@ -2,7 +2,22 @@ # Licensed under the Apache V2, see LICENCE file for details. """Client version definitions.""" -LTS_RELEASES = ["jammy", "focal", "bionic", "xenial", "trusty", "precise"] +LTS_RELEASES = ["noble", "jammy", "focal", "bionic", "xenial", "trusty", "precise"] + +SERIES_TO_BASE = { + "noble": "ubuntu@24.04", + "jammy": "ubuntu@22.04", + "focal": "ubuntu@20.04", + "bionic": "ubuntu@18.04", + "xenial": "ubuntu@16.04", + "trusty": "ubuntu@14.04", + "precise": "ubuntu@12.04", + "groovy": "ubuntu@20.10", + "disco": "ubuntu@19.04", + "cosmic": "ubuntu@18.10", + "artful": "ubuntu@17.10", + "vivid": "ubuntu@15.04", +} DEFAULT_ARCHITECTURE = "amd64"