diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index b11e60e9..ff4fee5d 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -19,6 +19,7 @@ jobs: python-version: [ "3.10", "3.11", + "3.12", ] steps: - name: Checkout @@ -54,6 +55,7 @@ jobs: python-version: [ "3.10", "3.11", + "3.12", ] steps: - name: Checkout @@ -79,6 +81,7 @@ jobs: python-version: [ "3.10", "3.11", + "3.12", ] steps: - name: Checkout diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ff6274f..0657e603 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: hooks: - id: black - repo: https://github.com/PyCQA/flake8 - rev: "3.8.4" + rev: "7.1.1" hooks: - id: flake8 - repo: https://github.com/timothycrosley/isort diff --git a/crate/operator/__init__.py b/crate/operator/__init__.py index 47fded89..cbe173ce 100644 --- a/crate/operator/__init__.py +++ b/crate/operator/__init__.py @@ -18,10 +18,18 @@ # However, if you have executed another commercial license agreement # with Crate these terms will supersede the license and you may use the # software solely pursuant to the terms of the relevant commercial agreement. -from pkg_resources import DistributionNotFound, get_distribution try: - __version__ = get_distribution("crate-operator").version -except DistributionNotFound: - # package is not installed - pass + from importlib.metadata import PackageNotFoundError, version +except (ImportError, ModuleNotFoundError): # pragma:nocover + from importlib_metadata import ( # type: ignore[assignment,no-redef,unused-ignore] + PackageNotFoundError, + version, + ) + +__appname__ = "crate-operator" + +try: + __version__ = version(__appname__) +except PackageNotFoundError: # pragma: no cover + __version__ = "unknown" diff --git a/crate/operator/operations.py b/crate/operator/operations.py index 14f34fff..27f611c5 100644 --- a/crate/operator/operations.py +++ b/crate/operator/operations.py @@ -505,12 +505,12 @@ async def restart_cluster( if next_pod_uid in all_pod_uids: # The next to-be-terminated pod still appears to be running. logger.info("Terminating pod '%s'", next_pod_name) + node_index = int(next_pod_name[next_pod_name.rindex("-") + 1 :]) + node_progress = f"{node_index + 1}/{len(all_pod_uids)}" await send_operation_progress_notification( namespace=namespace, name=name, - message="Waiting for node " - f"{int(next_pod_name[next_pod_name.rindex('-')+1:])+1}/{len(all_pod_uids)}" - " to be terminated...", + message=f"Waiting for node {node_progress} to be terminated.", logger=logger, status=WebhookStatus.IN_PROGRESS, operation=WebhookOperation.UPDATE, @@ -527,12 +527,12 @@ async def restart_cluster( elif next_pod_name in all_pod_names: total_nodes = get_total_nodes_count(old["spec"]["nodes"], "all") # The new pod has been spawned. Only a matter of time until it's ready. + node_index = int(next_pod_name[next_pod_name.rindex("-") + 1 :]) + node_progress = f"{node_index + 1}/{len(all_pod_uids)}" await send_operation_progress_notification( namespace=namespace, name=name, - message="Waiting for node " - f"{int(next_pod_name[next_pod_name.rindex('-')+1:])+1}/{len(all_pod_uids)}" - " to be restarted...", + message=f"Waiting for node {node_progress} to be restarted.", logger=logger, status=WebhookStatus.IN_PROGRESS, operation=WebhookOperation.UPDATE, diff --git a/crate/operator/utils/version.py b/crate/operator/utils/version.py index 7f638729..061fe017 100644 --- a/crate/operator/utils/version.py +++ b/crate/operator/utils/version.py @@ -20,9 +20,10 @@ # software solely pursuant to the terms of the relevant commercial agreement. import re -from distutils.version import Version from typing import Optional, Tuple, Union, cast +from verlib2.distutils.version import Version + class CrateVersion(Version): """Version numbering for CrateDB releases. diff --git a/crate/operator/webhooks.py b/crate/operator/webhooks.py index b4d51b76..041aab03 100644 --- a/crate/operator/webhooks.py +++ b/crate/operator/webhooks.py @@ -26,8 +26,8 @@ import aiohttp import kopf from aiohttp import TCPConnector -from pkg_resources import get_distribution +from crate.operator import __version__ from crate.operator.utils.crd import has_compute_changed @@ -225,11 +225,10 @@ def configure(self, url: str, username: str, password: str) -> None: :param password: All requests will include HTTP Basic Auth credentials. This is the password for that. """ - version = get_distribution("crate-operator").version self._url = url self._session = aiohttp.ClientSession( headers={ - "User-Agent": f"cratedb-operator/{version}", + "User-Agent": f"cratedb-operator/{__version__}", "Content-Type": "application/json", }, auth=aiohttp.BasicAuth(username, password), diff --git a/docs/source/conf.py b/docs/source/conf.py index aa123b42..401977bc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,7 +6,7 @@ import sys from typing import List -from pkg_resources import get_distribution +from crate.operator import __version__ # Configuration file for the Sphinx documentation builder. # @@ -26,7 +26,7 @@ copyright = "2020, Crate.io" author = "Crate.io" -version = release = get_distribution("crate-operator").version +version = release = __version__ # -- General configuration --------------------------------------------------- @@ -55,6 +55,7 @@ # undoc'd; https://docs.python.org/3/distutils/apiref.html#module-distutils.version ("py:class", "distutils.version.Version"), ("py:class", "prometheus_client.registry.Collector"), + ("py:class", "verlib2.distutils.version.Version"), ] diff --git a/setup.py b/setup.py index 2855a5d0..21666d20 100644 --- a/setup.py +++ b/setup.py @@ -51,11 +51,13 @@ def read(path: str) -> str: install_requires=[ "aiopg==1.4.0", "bitmath==1.3.3.1", + "importlib-metadata; python_version<'3.8'", "kopf==1.36.2", "kubernetes-asyncio==31.1.0", "PyYAML<7.0", "prometheus_client==0.21.1", "aiohttp==3.11.16", + "verlib2==0.3.1", "wrapt==1.17.2", "python-json-logger==3.3.0", ], @@ -80,13 +82,14 @@ def read(path: str) -> str: "mypy==1.13.0", ], }, - python_requires=">=3.10,<3.12", + python_requires=">=3.10,<3.13", classifiers=[ "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ], use_scm_version=True, ) diff --git a/tests/test_change_compute.py b/tests/test_change_compute.py index dfc6dc1a..6fd03d03 100644 --- a/tests/test_change_compute.py +++ b/tests/test_change_compute.py @@ -362,7 +362,7 @@ async def test_generate_body_patch( affinity = body["spec"]["template"]["spec"]["affinity"] tolerations = body["spec"]["template"]["spec"]["tolerations"] if new_cpu_request or new_memory_request: - assert type(affinity.node_affinity) == V1NodeAffinity + assert type(affinity.node_affinity) is V1NodeAffinity assert affinity.pod_anti_affinity == {"$patch": "delete"} assert len(tolerations) == 1 assert tolerations[0].to_dict() == { @@ -373,7 +373,7 @@ async def test_generate_body_patch( "value": "shared", } else: - assert type(affinity.pod_anti_affinity) == V1PodAntiAffinity + assert type(affinity.pod_anti_affinity) is V1PodAntiAffinity assert affinity.node_affinity == {"$patch": "delete"} assert len(tolerations) == 1 assert tolerations[0].to_dict() == {