diff --git a/lib/charms/postgresql_k8s/v0/postgresql.py b/lib/charms/postgresql_k8s/v0/postgresql.py index 3149b4b998..7ac8504675 100644 --- a/lib/charms/postgresql_k8s/v0/postgresql.py +++ b/lib/charms/postgresql_k8s/v0/postgresql.py @@ -35,7 +35,7 @@ # Increment this PATCH version before using `charmcraft publish-lib` or reset # to 0 if you are raising the major API version -LIBPATCH = 55 +LIBPATCH = 52 # Groups to distinguish HBA access ACCESS_GROUP_IDENTITY = "identity_access" @@ -751,7 +751,7 @@ def list_users_from_relation(self, current_host=False) -> Set[str]: "SELECT usename " "FROM pg_catalog.pg_user " "WHERE usename LIKE 'relation_id_%' OR usename LIKE 'relation-%' " - "OR usename LIKE 'pgbouncer_auth_relation_id_%' OR usename LIKE '%_user_%_%';" + "OR usename LIKE 'pgbouncer_auth_relation_%' OR usename LIKE '%_user_%_%';" ) usernames = cursor.fetchall() return {username[0] for username in usernames} @@ -831,7 +831,7 @@ def set_up_database(self, temp_location: Optional[str] = None) -> None: END IF; END LOOP; -- Remove users that don't exist anymore from the pg_hba file. - FOR rec IN SELECT h.lines FROM pg_hba AS h LEFT JOIN relation_users AS r ON SPLIT_PART(h.lines, ' ', 3) = r.user WHERE r.user IS NULL AND (SPLIT_PART(h.lines, ' ', 3) LIKE 'relation_id_%' OR SPLIT_PART(h.lines, ' ', 3) LIKE 'pgbouncer_auth_relation_id_%' OR SPLIT_PART(h.lines, ' ', 3) LIKE '%_user_%_%') + FOR rec IN SELECT h.lines FROM pg_hba AS h LEFT JOIN relation_users AS r ON SPLIT_PART(h.lines, ' ', 3) = r.user WHERE r.user IS NULL AND (SPLIT_PART(h.lines, ' ', 3) LIKE 'relation_id_%' OR SPLIT_PART(h.lines, ' ', 3) LIKE 'pgbouncer_auth_relation_%' OR SPLIT_PART(h.lines, ' ', 3) LIKE '%_user_%_%') LOOP DELETE FROM pg_hba WHERE lines = rec.lines; changes := changes + 1; @@ -1081,3 +1081,21 @@ def validate_group_map(self, group_map: Optional[str]) -> bool: return False return True + + def is_user_in_hba(self, username: str) -> bool: + """Check if user was added in pg_hba.""" + connection = None + try: + with self._connect_to_database() as connection, connection.cursor() as cursor: + cursor.execute( + SQL( + "SELECT COUNT(*) FROM pg_hba_file_rules WHERE {} = ANY(user_name);" + ).format(Literal(username)) + ) + return cursor.fetchone()[0] > 0 + except psycopg2.Error as e: + logger.debug(f"Failed to check pg_hba: {e}") + return False + finally: + if connection: + connection.close() diff --git a/poetry.lock b/poetry.lock index b23a6f9cff..7ab3392ad5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -61,7 +61,6 @@ files = [ ] [package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} @@ -176,19 +175,6 @@ files = [ {file = "backports_datetime_fromisoformat-2.0.3.tar.gz", hash = "sha256:b58edc8f517b66b397abc250ecc737969486703a66eb97e01e6d51291b1a139d"}, ] -[[package]] -name = "backports-strenum" -version = "1.3.1" -description = "Base class for creating enumerated constants that are also subclasses of str" -optional = false -python-versions = ">=3.8.6,<3.11" -groups = ["integration"] -markers = "python_version == \"3.10\"" -files = [ - {file = "backports_strenum-1.3.1-py3-none-any.whl", hash = "sha256:cdcfe36dc897e2615dc793b7d3097f54d359918fc448754a517e6f23044ccf83"}, - {file = "backports_strenum-1.3.1.tar.gz", hash = "sha256:77c52407342898497714f0596e86188bb7084f89063226f4ba66863482f42414"}, -] - [[package]] name = "bcrypt" version = "4.3.0" @@ -628,9 +614,6 @@ files = [ {file = "coverage-7.8.2.tar.gz", hash = "sha256:a886d531373a1f6ff9fad2a2ba4a045b68467b779ae729ee0b3b10ac20033b27"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - [package.extras] toml = ["tomli ; python_full_version <= \"3.11.0a6\""] @@ -724,25 +707,6 @@ wrapt = ">=1.10,<2" [package.extras] dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "setuptools ; python_version >= \"3.12\"", "tox"] -[[package]] -name = "exceptiongroup" -version = "1.3.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -groups = ["main", "integration", "unit"] -markers = "python_version == \"3.10\"" -files = [ - {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, - {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "executing" version = "2.2.0" @@ -940,9 +904,8 @@ files = [ ] [package.dependencies] -decorator = {version = "*", markers = "python_version > \"3.6\""} -ipython = {version = ">=7.31.1", markers = "python_version > \"3.6\""} -tomli = {version = "*", markers = "python_version > \"3.6\" and python_version < \"3.11\""} +decorator = {version = "*", markers = "python_version >= \"3.11\""} +ipython = {version = ">=7.31.1", markers = "python_version >= \"3.11\""} [[package]] name = "ipython" @@ -959,7 +922,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} @@ -967,7 +929,6 @@ prompt_toolkit = ">=3.0.41,<3.1.0" pygments = ">=2.4.0" stack_data = "*" traitlets = ">=5.13.0" -typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""} [package.extras] all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] @@ -1083,7 +1044,6 @@ files = [ [package.dependencies] backports-datetime-fromisoformat = ">=2.0.2" -"backports.strenum" = {version = ">=1.3.1", markers = "python_version < \"3.11\""} hvac = "*" kubernetes = ">=12.0.1,<31.0.0" macaroonbakery = ">=1.1,<2.0" @@ -1880,11 +1840,9 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -2324,49 +2282,6 @@ files = [ doc = ["reno", "sphinx"] test = ["pytest", "tornado (>=4.5)", "typeguard"] -[[package]] -name = "tomli" -version = "2.2.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.8" -groups = ["integration", "unit"] -files = [ - {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, - {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, - {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, - {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, - {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, - {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, - {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, - {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, - {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, - {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, - {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, - {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, - {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, - {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, - {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, - {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, - {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, - {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, -] -markers = {integration = "python_version == \"3.10\"", unit = "python_full_version <= \"3.11.0a6\""} - [[package]] name = "toposort" version = "1.10" @@ -2401,12 +2316,11 @@ version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" -groups = ["main", "charm-libs", "integration", "unit"] +groups = ["main", "charm-libs", "integration"] files = [ {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] -markers = {unit = "python_version == \"3.10\""} [[package]] name = "typing-inspect" @@ -2661,5 +2575,5 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" -python-versions = "^3.10" -content-hash = "cfcd8daa109980760d41cde38a2108dc44eca62c272b1fae5eddc1ae25103067" +python-versions = "^3.12" +content-hash = "353c0f4e60d72b50776fa87af08336d7e0e0c6b34ae9d7b7ce21bd5111a038d4" diff --git a/pyproject.toml b/pyproject.toml index 9450f840df..77c3ba6da7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ package-mode = false requires-poetry = ">=2.0.0" [tool.poetry.dependencies] -python = "^3.10" +python = "^3.12" ops = "^2.22.0" boto3 = "^1.38.27" pgconnstr = "^1.0.1" @@ -94,7 +94,7 @@ markers = ["juju3", "juju_secrets"] [tool.ruff] # preview and explicit preview are enabled for CPY001 preview = true -target-version = "py310" +target-version = "py312" src = ["src", "."] line-length = 99 diff --git a/src/backups.py b/src/backups.py index 9c8a68e0af..12a04f98ce 100644 --- a/src/backups.py +++ b/src/backups.py @@ -9,7 +9,7 @@ import re import tempfile import time -from datetime import datetime, timezone +from datetime import UTC, datetime from io import BytesIO import boto3 @@ -371,9 +371,7 @@ def _generate_backup_list_output(self) -> str: backup_reference, _ = self._parse_backup_id(backup["reference"][-1]) lsn_start_stop = f"{backup['lsn']['start']} / {backup['lsn']['stop']}" time_start, time_stop = ( - datetime.strftime( - datetime.fromtimestamp(stamp, timezone.utc), "%Y-%m-%dT%H:%M:%SZ" - ) + datetime.strftime(datetime.fromtimestamp(stamp, UTC), "%Y-%m-%dT%H:%M:%SZ") for stamp in backup["timestamp"].values() ) backup_timeline = ( @@ -465,7 +463,7 @@ def _list_timelines(self) -> dict[str, tuple[str, str]]: return dict[str, tuple[str, str]]({ datetime.strftime( - datetime.fromtimestamp(timeline_object["time"], timezone.utc), + datetime.fromtimestamp(timeline_object["time"], UTC), "%Y-%m-%dT%H:%M:%SZ", ): ( timeline.split("/")[1], @@ -513,8 +511,8 @@ def _parse_psql_timestamp(self, timestamp: str) -> datetime: t = re.sub(r"\.(\d+)", lambda x: f".{x[1]:06}", t) dt = datetime.fromisoformat(t) # Convert to the timezone-naive - if dt.tzinfo is not None and dt.tzinfo is not timezone.utc: - dt = dt.astimezone(tz=timezone.utc) + if dt.tzinfo is not None and dt.tzinfo is not UTC: + dt = dt.astimezone(tz=UTC) return dt.replace(tzinfo=None) def _parse_backup_id(self, label) -> tuple[str, str]: diff --git a/src/constants.py b/src/constants.py index b67ff74cfa..030699032c 100644 --- a/src/constants.py +++ b/src/constants.py @@ -54,8 +54,4 @@ DATABASE = "database" -ENDPOINT_SIMULTANEOUSLY_BLOCKING_MESSAGE = ( - "Please choose one endpoint to use. No need to relate all of them simultaneously!" -) - PGBACKREST_LOGROTATE_FILE = "/etc/logrotate.d/pgbackrest.logrotate" diff --git a/src/relations/postgresql_provider.py b/src/relations/postgresql_provider.py index 25d4e32ce8..68577c5607 100644 --- a/src/relations/postgresql_provider.py +++ b/src/relations/postgresql_provider.py @@ -4,6 +4,7 @@ """Postgres client relation hooks & helpers.""" import logging +from datetime import datetime from charms.data_platform_libs.v0.data_interfaces import ( DatabaseProvides, @@ -18,14 +19,12 @@ PostgreSQLDeleteUserError, PostgreSQLGetPostgreSQLVersionError, ) -from ops.charm import CharmBase, RelationBrokenEvent, RelationChangedEvent, RelationDepartedEvent +from ops.charm import CharmBase, RelationBrokenEvent, RelationDepartedEvent from ops.framework import Object from ops.model import ActiveStatus, BlockedStatus, Relation +from tenacity import RetryError, Retrying, stop_after_attempt, wait_fixed -from constants import ( - DATABASE_PORT, - ENDPOINT_SIMULTANEOUSLY_BLOCKING_MESSAGE, -) +from constants import DATABASE_PORT from utils import new_password logger = logging.getLogger(__name__) @@ -55,10 +54,6 @@ def __init__(self, charm: CharmBase, relation_name: str = "database") -> None: self.framework.observe( charm.on[self.relation_name].relation_broken, self._on_relation_broken ) - self.framework.observe( - charm.on[self.relation_name].relation_changed, - self._on_relation_changed_event, - ) self.charm = charm # Charm events defined in the database provides charm library. @@ -66,9 +61,6 @@ def __init__(self, charm: CharmBase, relation_name: str = "database") -> None: self.framework.observe( self.database_provides.on.database_requested, self._on_database_requested ) - self.framework.observe( - charm.on[self.relation_name].relation_changed, self._on_relation_changed - ) @staticmethod def _sanitize_extra_roles(extra_roles: str | None) -> list[str]: @@ -163,26 +155,19 @@ def _on_database_requested(self, event: DatabaseRequestedEvent) -> None: if issubclass(type(e), PostgreSQLCreateUserError) and e.message is not None else f"Failed to initialize {self.relation_name} relation" ) - - def _on_relation_changed(self, event: RelationChangedEvent) -> None: - # Check for some conditions before trying to access the PostgreSQL instance. - if not self.charm.is_cluster_initialised: - logger.debug( - "Deferring on_relation_changed: Cluster must be initialized before configuration can be updated with relation users" - ) - event.defer() - return - - if ( - not self.charm._patroni.member_started - or f"relation_id_{event.relation.id}" - not in self.charm.postgresql.list_users(current_host=True) - ): - logger.debug("Deferring on_relation_changed: user was not created yet") - event.defer() return - self.charm.update_config() + # Try to wait for pg_hba trigger + try: + for attempt in Retrying(stop=stop_after_attempt(3), wait=wait_fixed(1)): + with attempt: + if not self.charm.postgresql.is_user_in_hba(user): + raise Exception("pg_hba not ready") + self.charm.unit_peer_data.update({ + "pg_hba_needs_update_timestamp": str(datetime.now()) + }) + except RetryError: + logger.warning("database requested: Unable to check pg_hba rule update") def _on_relation_departed(self, event: RelationDepartedEvent) -> None: """Set a flag to avoid deleting database users when not wanted.""" @@ -301,20 +286,6 @@ def update_tls_flag(self, tls: str) -> None: self.database_provides.set_tls(relation.id, tls) self.database_provides.set_tls_ca(relation.id, ca) - def _check_multiple_endpoints(self) -> bool: - """Checks if there are relations with other endpoints.""" - relation_names = {relation.name for relation in self.charm.client_relations} - return "database" in relation_names and len(relation_names) > 1 - - def _update_unit_status_on_blocking_endpoint_simultaneously(self): - """Clean up Blocked status if this is due related of multiple endpoints.""" - if ( - self.charm._has_blocked_status - and self.charm.unit.status.message == ENDPOINT_SIMULTANEOUSLY_BLOCKING_MESSAGE - and not self._check_multiple_endpoints() - ): - self.charm.unit.status = ActiveStatus() - def _update_unit_status(self, relation: Relation) -> None: """# Clean up Blocked status if it's due to extensions request.""" if ( @@ -324,18 +295,6 @@ def _update_unit_status(self, relation: Relation) -> None: ): self.charm.unit.status = ActiveStatus() - self._update_unit_status_on_blocking_endpoint_simultaneously() - - def _on_relation_changed_event(self, event: RelationChangedEvent) -> None: - """Event emitted when the relation has changed.""" - # Leader only - if not self.charm.unit.is_leader(): - return - - if self._check_multiple_endpoints(): - self.charm.unit.status = BlockedStatus(ENDPOINT_SIMULTANEOUSLY_BLOCKING_MESSAGE) - return - def check_for_invalid_extra_user_roles(self, relation_id: int) -> bool: """Checks if there are relations with invalid extra user roles. diff --git a/src/upgrade.py b/src/upgrade.py index 07526711ba..66dd6f0ef4 100644 --- a/src/upgrade.py +++ b/src/upgrade.py @@ -5,6 +5,7 @@ import json import logging +from typing import override from charms.data_platform_libs.v0.upgrade import ( ClusterNotReadyError, @@ -20,7 +21,6 @@ from ops.model import BlockedStatus, MaintenanceStatus, RelationDataContent from pydantic import BaseModel from tenacity import RetryError, Retrying, stop_after_attempt, wait_fixed -from typing_extensions import override from constants import APP_SCOPE, MONITORING_PASSWORD_KEY, MONITORING_USER, PATRONI_PASSWORD_KEY from patroni import SwitchoverFailedError diff --git a/tests/integration/ha_tests/test_smoke.py b/tests/integration/ha_tests/test_smoke.py index 59e6aa2282..779bfde790 100644 --- a/tests/integration/ha_tests/test_smoke.py +++ b/tests/integration/ha_tests/test_smoke.py @@ -2,7 +2,6 @@ # Copyright 2021 Canonical Ltd. # See LICENSE file for licensing details. -import asyncio import logging import os @@ -213,7 +212,7 @@ async def test_app_resources_conflicts(ops_test: OpsTest, charm): await ops_test.model.wait_for_idle( apps=[DUP_DATABASE_APP_NAME], timeout=500, status="blocked" ) - except asyncio.TimeoutError: + except TimeoutError: logger.info("Application is not in blocked state. Checking logs...") # Since application have postgresql db in storage from external application it should not be able to connect due to new password diff --git a/tests/unit/test_postgresql.py b/tests/unit/test_postgresql.py index 7df5a02a51..87ff499a1a 100644 --- a/tests/unit/test_postgresql.py +++ b/tests/unit/test_postgresql.py @@ -227,7 +227,7 @@ def test_grant_relation_access_group_memberships(harness): "SELECT usename " "FROM pg_catalog.pg_user " "WHERE usename LIKE 'relation_id_%' OR usename LIKE 'relation-%' " - "OR usename LIKE 'pgbouncer_auth_relation_id_%' OR usename LIKE '%_user_%_%';" + "OR usename LIKE 'pgbouncer_auth_relation_%' OR usename LIKE '%_user_%_%';" ), ]) diff --git a/tests/unit/test_postgresql_provider.py b/tests/unit/test_postgresql_provider.py index dc5db5cfde..4c86d29622 100644 --- a/tests/unit/test_postgresql_provider.py +++ b/tests/unit/test_postgresql_provider.py @@ -76,9 +76,6 @@ def request_database(_harness): def test_on_database_requested(harness): with ( patch("charm.PostgresqlOperatorCharm.update_config"), - patch( - "relations.postgresql_provider.PostgreSQLProvider._on_relation_changed" - ) as _on_relation_changed, patch.object(PostgresqlOperatorCharm, "postgresql", Mock()) as postgresql_mock, patch.object(EventBase, "defer") as _defer, patch("charm.Patroni.member_started", new_callable=PropertyMock) as _member_started,