From ba33f2aca9d5a0db464a6772d300363600ac2b8b Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Tue, 16 Jan 2024 11:31:35 -0700 Subject: [PATCH 1/7] Read client version from VERSION file --- juju/version.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/juju/version.py b/juju/version.py index 3035fce9..36b3157a 100644 --- a/juju/version.py +++ b/juju/version.py @@ -1,11 +1,15 @@ # Copyright 2023 Canonical Ltd. # Licensed under the Apache V2, see LICENCE file for details. +import re LTS_RELEASES = ["jammy", "focal", "bionic", "xenial", "trusty", "precise"] DEFAULT_ARCHITECTURE = 'amd64' +VERSION_FILE_PATH = '../VERSION' +CLIENT_VERSION = re.search(r'\d+\.\d+\.\d+', open(VERSION_FILE_PATH).read().strip()).group() + # Juju server version we target. Depending on this value, the Juju server # may stop the connecting considering us not compatible. TARGET_JUJU_VERSION = '3.2.0' From 8501b2363386fab434b94105831bf4e177ad0bab Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Tue, 16 Jan 2024 11:32:02 -0700 Subject: [PATCH 2/7] Use the client version in the connection rather then setting it to the TARGET_JUJU_VERSION --- juju/client/connection.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/juju/client/connection.py b/juju/client/connection.py index 3d680175..ff6d8ee6 100644 --- a/juju/client/connection.py +++ b/juju/client/connection.py @@ -17,7 +17,7 @@ from juju import errors, tag, utils, jasyncio from juju.client import client from juju.utils import IdQueue -from juju.version import TARGET_JUJU_VERSION +from juju.version import CLIENT_VERSION log = logging.getLogger('juju.client.connection') @@ -963,7 +963,7 @@ def _build_facades(self, facades_from_connection): async def login(self): params = {} # Set the client version - params['client-version'] = TARGET_JUJU_VERSION + params['client-version'] = CLIENT_VERSION params['auth-tag'] = self.usertag if self.password: params['credentials'] = self.password From 0fe52cc48444e11aca0a1abeb5e714532a5c3c15 Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Tue, 16 Jan 2024 12:30:25 -0700 Subject: [PATCH 3/7] Use only client version to check the server connection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we spam debug output for any use of pylibjuju where the "target" != juju controller version. It should be turned into a ceiling, as it’s pretty normal to use the latest pylibjuju (currently 3.3.0.0) against any 3.x controller (e.g. 3.1). The only time we emit a warning should be when we think the client should be upgraded to a more recent version (e.g. running pylibjuju 3.3.0.0 against juju controller 3.6). --- juju/client/connector.py | 23 ++++++++++++----------- juju/version.py | 18 ++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/juju/client/connector.py b/juju/client/connector.py index 69840620..c57ca9ed 100644 --- a/juju/client/connector.py +++ b/juju/client/connector.py @@ -4,6 +4,8 @@ import copy import logging +from packaging import version + import macaroonbakery.httpbakery as httpbakery from juju.client import client @@ -12,7 +14,7 @@ from juju.client.jujudata import API_ENDPOINTS_KEY, FileJujuData from juju.client.proxy.factory import proxy_from_config from juju.errors import JujuConnectionError, JujuError -from juju.version import SUPPORTED_MAJOR_VERSION, TARGET_JUJU_VERSION +from juju.version import CLIENT_VERSION log = logging.getLogger("connector") @@ -86,20 +88,19 @@ async def connect(self, **kwargs): self._connection = await Connection.connect(**kwargs) # Check if we support the target controller - juju_server_version = self._connection.info["server-version"] - if not juju_server_version.startswith(TARGET_JUJU_VERSION): - log.debug( - "This version was tested using {} juju version {} may have compatibility issues".format( - TARGET_JUJU_VERSION, juju_server_version - ) - ) - if not self._connection.info["server-version"].startswith( - SUPPORTED_MAJOR_VERSION - ): + juju_server_version = version.parse(self._connection.info["server-version"]) + client_version = version.parse(CLIENT_VERSION) + + if juju_server_version.major != client_version.major: raise JujuConnectionError( "juju server-version %s not supported" % juju_server_version ) + if juju_server_version > client_version: + log.warning( + f"This client is tested up to the version {client_version} Juju controller. Detected a Juju controller version {juju_server_version} that's higher than the {client_version}. Some functionalities that the Juju {juju_server_version} offers may not be available. Please consider upgrading to pylibjuju {juju_server_version}." + ) + async def disconnect(self, entity): """Shut down the watcher task and close websockets.""" if self._connection: diff --git a/juju/version.py b/juju/version.py index 36b3157a..6e007a4c 100644 --- a/juju/version.py +++ b/juju/version.py @@ -7,14 +7,12 @@ DEFAULT_ARCHITECTURE = 'amd64' -VERSION_FILE_PATH = '../VERSION' +# CLIENT_VERSION (that's read from the VERSION file) is the highest Juju server +# version that this client supports. +# Note that this is a ceiling. CLIENT_VERSION <= juju-controller-version works. +# For CLIENT_VERSION < juju-controller-version (strictly smaller), we emit a warning +# to update the client to the latest. +# However, for any CLIENT_VERSION > juju-controller-version, a "client incompatible +# with server" will be returned by the juju controller. +VERSION_FILE_PATH = './VERSION' CLIENT_VERSION = re.search(r'\d+\.\d+\.\d+', open(VERSION_FILE_PATH).read().strip()).group() - -# Juju server version we target. Depending on this value, the Juju server -# may stop the connecting considering us not compatible. -TARGET_JUJU_VERSION = '3.2.0' - -# Used by connector to determine if we are compatible with the juju server -SUPPORTED_MAJOR_VERSION = '3' - -SUPPORTED_MAJOR_MINOR_VERSION = '3.2' From ba64ece670859ae5495f8cedc4f1a3b00c40f6aa Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Tue, 16 Jan 2024 14:16:37 -0700 Subject: [PATCH 4/7] Locate VERSION file relative to the version.py module In case this is run from somewhere else than the libjuju top directory (e.g. sphinx). --- juju/version.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/juju/version.py b/juju/version.py index 6e007a4c..e4289b46 100644 --- a/juju/version.py +++ b/juju/version.py @@ -1,6 +1,7 @@ # Copyright 2023 Canonical Ltd. # Licensed under the Apache V2, see LICENCE file for details. +import pathlib import re LTS_RELEASES = ["jammy", "focal", "bionic", "xenial", "trusty", "precise"] @@ -14,5 +15,5 @@ # to update the client to the latest. # However, for any CLIENT_VERSION > juju-controller-version, a "client incompatible # with server" will be returned by the juju controller. -VERSION_FILE_PATH = './VERSION' +VERSION_FILE_PATH = pathlib.Path(__file__).parent.parent / 'VERSION' CLIENT_VERSION = re.search(r'\d+\.\d+\.\d+', open(VERSION_FILE_PATH).read().strip()).group() From 2d725fae049688e438f32d4386b4ec56b26d63c6 Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Wed, 17 Jan 2024 12:45:43 -0700 Subject: [PATCH 5/7] Remove sniffing for minor version --- juju/client/connector.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/juju/client/connector.py b/juju/client/connector.py index c57ca9ed..a4cd9dad 100644 --- a/juju/client/connector.py +++ b/juju/client/connector.py @@ -96,11 +96,6 @@ async def connect(self, **kwargs): "juju server-version %s not supported" % juju_server_version ) - if juju_server_version > client_version: - log.warning( - f"This client is tested up to the version {client_version} Juju controller. Detected a Juju controller version {juju_server_version} that's higher than the {client_version}. Some functionalities that the Juju {juju_server_version} offers may not be available. Please consider upgrading to pylibjuju {juju_server_version}." - ) - async def disconnect(self, entity): """Shut down the watcher task and close websockets.""" if self._connection: From b9f60891cb25d6850c6c8b1cce09b42464d9a736 Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Tue, 23 Jan 2024 13:53:55 -0700 Subject: [PATCH 6/7] Handle non-standard version strings coming from api --- juju/client/connector.py | 16 +++++++++++++--- juju/errors.py | 4 ++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/juju/client/connector.py b/juju/client/connector.py index a4cd9dad..1654a834 100644 --- a/juju/client/connector.py +++ b/juju/client/connector.py @@ -4,7 +4,7 @@ import copy import logging -from packaging import version +from packaging import version, InvalidVersion import macaroonbakery.httpbakery as httpbakery @@ -13,7 +13,7 @@ from juju.client.gocookies import GoCookieJar, go_to_py_cookie from juju.client.jujudata import API_ENDPOINTS_KEY, FileJujuData from juju.client.proxy.factory import proxy_from_config -from juju.errors import JujuConnectionError, JujuError +from juju.errors import JujuConnectionError, JujuError, JujuUnknownVersion from juju.version import CLIENT_VERSION log = logging.getLogger("connector") @@ -88,7 +88,17 @@ async def connect(self, **kwargs): self._connection = await Connection.connect(**kwargs) # Check if we support the target controller - juju_server_version = version.parse(self._connection.info["server-version"]) + server_version = self._connection.info["server-version"] + try: + juju_server_version = version.parse(server_version) + except InvalidVersion as err: + # We're only interested in the major version, so + # we attempt to clean up versions such as 3.4-rc1.2 as just 3.4 + if '-' not in server_version: + raise JujuUnknownVersion(err) + juju_server_version = version.parse(server_version.split('-')[0]) + + # CLIENT_VERSION statically comes from the VERSION file in the repo client_version = version.parse(CLIENT_VERSION) if juju_server_version.major != client_version.major: diff --git a/juju/errors.py b/juju/errors.py index b996ea7f..d0284158 100644 --- a/juju/errors.py +++ b/juju/errors.py @@ -137,3 +137,7 @@ class AbstractMethodError(Exception): class PylibjujuError(JujuError): pass + + +class JujuUnknownVersion(PylibjujuError): + pass From 838be65b70d06aa929ce50660e5710617126d4aa Mon Sep 17 00:00:00 2001 From: Caner Derici Date: Tue, 23 Jan 2024 14:04:02 -0700 Subject: [PATCH 7/7] Fix free import --- juju/client/connector.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/juju/client/connector.py b/juju/client/connector.py index 1654a834..f1fe54a5 100644 --- a/juju/client/connector.py +++ b/juju/client/connector.py @@ -4,7 +4,7 @@ import copy import logging -from packaging import version, InvalidVersion +from packaging import version import macaroonbakery.httpbakery as httpbakery @@ -91,7 +91,7 @@ async def connect(self, **kwargs): server_version = self._connection.info["server-version"] try: juju_server_version = version.parse(server_version) - except InvalidVersion as err: + except version.InvalidVersion as err: # We're only interested in the major version, so # we attempt to clean up versions such as 3.4-rc1.2 as just 3.4 if '-' not in server_version: