postgresql-charms-single-kernel 16.1.6__tar.gz → 16.1.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/PKG-INFO +4 -1
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/pyproject.toml +16 -19
- postgresql_charms_single_kernel-16.1.7/single_kernel_postgresql/events/tls_transfer.py +89 -0
- postgresql_charms_single_kernel-16.1.7/single_kernel_postgresql/utils/__init__.py +3 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/utils/postgresql.py +15 -13
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/LICENSE +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/README.md +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/abstract_charm.py +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/charmcraft.yaml +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/config/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/config/literals.py +0 -0
- {postgresql_charms_single_kernel-16.1.6/single_kernel_postgresql/utils → postgresql_charms_single_kernel-16.1.7/single_kernel_postgresql/events}/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/single_kernel_postgresql/utils/filesystem.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: postgresql-charms-single-kernel
|
|
3
|
-
Version: 16.1.
|
|
3
|
+
Version: 16.1.7
|
|
4
4
|
Summary: Shared and reusable code for PostgreSQL-related charms
|
|
5
5
|
Author: Canonical Data Platform
|
|
6
6
|
Author-email: Canonical Data Platform <data-platform@lists.launchpad.net>
|
|
@@ -209,6 +209,9 @@ License:
|
|
|
209
209
|
Classifier: Development Status :: 3 - Alpha
|
|
210
210
|
Classifier: Intended Audience :: Developers
|
|
211
211
|
Classifier: Operating System :: POSIX :: Linux
|
|
212
|
+
Requires-Dist: ops>=2.0.0
|
|
213
|
+
Requires-Dist: psycopg2>=2.9.10
|
|
214
|
+
Requires-Dist: tenacity>=9.0.0
|
|
212
215
|
Requires-Python: >=3.8, <4.0
|
|
213
216
|
Description-Content-Type: text/markdown
|
|
214
217
|
|
{postgresql_charms_single_kernel-16.1.6 → postgresql_charms_single_kernel-16.1.7}/pyproject.toml
RENAMED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[project]
|
|
5
5
|
name = "postgresql-charms-single-kernel"
|
|
6
6
|
description = "Shared and reusable code for PostgreSQL-related charms"
|
|
7
|
-
version = "16.1.
|
|
7
|
+
version = "16.1.7"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
license = {file = "LICENSE"}
|
|
10
10
|
authors = [
|
|
@@ -16,6 +16,11 @@ classifiers = [
|
|
|
16
16
|
"Operating System :: POSIX :: Linux",
|
|
17
17
|
]
|
|
18
18
|
requires-python = ">=3.8,<4.0"
|
|
19
|
+
dependencies = [
|
|
20
|
+
"ops>=2.0.0",
|
|
21
|
+
"psycopg2>=2.9.10",
|
|
22
|
+
"tenacity>=9.0.0",
|
|
23
|
+
]
|
|
19
24
|
|
|
20
25
|
[build-system]
|
|
21
26
|
requires = ["uv_build>=0.9.9,<0.10.0"]
|
|
@@ -26,20 +31,16 @@ module-name = "single_kernel_postgresql"
|
|
|
26
31
|
module-root = ""
|
|
27
32
|
|
|
28
33
|
[dependency-groups]
|
|
29
|
-
lib = [
|
|
30
|
-
"ops>=2.0.0",
|
|
31
|
-
"psycopg2>=2.9.10",
|
|
32
|
-
]
|
|
33
34
|
format = [
|
|
34
|
-
"ruff==0.14.
|
|
35
|
+
"ruff==0.14.14; python_version >= '3.12'"
|
|
35
36
|
]
|
|
36
37
|
lint = [
|
|
37
|
-
"codespell==2.4.1",
|
|
38
|
-
"
|
|
38
|
+
"codespell==2.4.1; python_version >= '3.12'",
|
|
39
|
+
"ty==0.0.14; python_version >= '3.12'"
|
|
39
40
|
]
|
|
40
41
|
unit = [
|
|
41
|
-
"coverage[toml]==7.13.
|
|
42
|
-
"pytest==9.0.2; python_version >= '3.
|
|
42
|
+
"coverage[toml]==7.13.2; python_version >= '3.12'",
|
|
43
|
+
"pytest==9.0.2; python_version >= '3.12'"
|
|
43
44
|
]
|
|
44
45
|
|
|
45
46
|
# Testing tools configuration
|
|
@@ -102,12 +103,8 @@ max-complexity = 10
|
|
|
102
103
|
[tool.ruff.lint.pydocstyle]
|
|
103
104
|
convention = "google"
|
|
104
105
|
|
|
105
|
-
[tool.
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
reportIncompatibleMethodOverride = false
|
|
111
|
-
reportImportCycles = false
|
|
112
|
-
reportMissingModuleSource = true
|
|
113
|
-
stubPath = ""
|
|
106
|
+
[tool.ty.environment]
|
|
107
|
+
python = ".tox/lint/"
|
|
108
|
+
|
|
109
|
+
[tool.ty.src]
|
|
110
|
+
exclude = ["tests"]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Copyright 2022 Canonical Ltd.
|
|
2
|
+
# See LICENSE file for licensing details.
|
|
3
|
+
|
|
4
|
+
"""TLS Transfer Handler."""
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
from collections.abc import Iterator
|
|
8
|
+
|
|
9
|
+
# Charmlib import. Should be made available in the charm itself
|
|
10
|
+
from charms.certificate_transfer_interface.v0.certificate_transfer import ( # type: ignore
|
|
11
|
+
CertificateAvailableEvent,
|
|
12
|
+
CertificateRemovedEvent,
|
|
13
|
+
CertificateTransferRequires,
|
|
14
|
+
)
|
|
15
|
+
from ops.framework import Object
|
|
16
|
+
from ops.pebble import ConnectionError as PebbleConnectionError
|
|
17
|
+
from ops.pebble import PathError, ProtocolError
|
|
18
|
+
from tenacity import RetryError
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
SCOPE = "unit"
|
|
22
|
+
TLS_TRANSFER_RELATION = "receive-ca-cert"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class TLSTransfer(Object):
|
|
26
|
+
"""In this class we manage certificate transfer relation."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, charm, peer_relation: str):
|
|
29
|
+
super().__init__(charm, "client-relations")
|
|
30
|
+
self.charm = charm
|
|
31
|
+
self.peer_relation = peer_relation
|
|
32
|
+
self.certs_transfer = CertificateTransferRequires(self.charm, TLS_TRANSFER_RELATION)
|
|
33
|
+
self.framework.observe(
|
|
34
|
+
self.certs_transfer.on.certificate_available, self._on_certificate_available
|
|
35
|
+
)
|
|
36
|
+
self.framework.observe(
|
|
37
|
+
self.certs_transfer.on.certificate_removed, self._on_certificate_removed
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def _on_certificate_available(self, event: CertificateAvailableEvent) -> None:
|
|
41
|
+
"""Enable TLS when TLS certificate is added."""
|
|
42
|
+
relation = self.charm.model.get_relation(TLS_TRANSFER_RELATION, event.relation_id)
|
|
43
|
+
if relation is None:
|
|
44
|
+
logger.error("Relationship not established anymore.")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
secret_name = f"ca-{relation.app.name}"
|
|
48
|
+
self.charm.set_secret(SCOPE, secret_name, event.ca)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
if not self.charm.push_ca_file_into_workload(secret_name):
|
|
52
|
+
logger.debug("Cannot push TLS certificates at this moment")
|
|
53
|
+
event.defer()
|
|
54
|
+
return
|
|
55
|
+
except (PebbleConnectionError, PathError, ProtocolError, RetryError) as e:
|
|
56
|
+
logger.error("Cannot push TLS certificates: %r", e)
|
|
57
|
+
event.defer()
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
def _on_certificate_removed(self, event: CertificateRemovedEvent) -> None:
|
|
61
|
+
"""Disable TLS when TLS certificate is removed."""
|
|
62
|
+
relation = self.charm.model.get_relation(TLS_TRANSFER_RELATION, event.relation_id)
|
|
63
|
+
if relation is None:
|
|
64
|
+
logger.error("Relationship not established anymore.")
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
secret_name = f"ca-{relation.app.name}"
|
|
68
|
+
self.charm.set_secret(SCOPE, secret_name, None)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
if not self.charm.clean_ca_file_from_workload(secret_name):
|
|
72
|
+
logger.debug("Cannot clean CA certificates at this moment")
|
|
73
|
+
event.defer()
|
|
74
|
+
return
|
|
75
|
+
except (PebbleConnectionError, PathError, ProtocolError, RetryError) as e:
|
|
76
|
+
logger.error("Cannot clean CA certificates: %r", e)
|
|
77
|
+
event.defer()
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
def get_ca_secret_names(self) -> Iterator[str]:
|
|
81
|
+
"""Get a secret-name for each relation fulfilling the CA transfer interface.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Secret name for a CA transfer fulfilled interface.
|
|
85
|
+
"""
|
|
86
|
+
relations = self.charm.model.relations.get(TLS_TRANSFER_RELATION, [])
|
|
87
|
+
|
|
88
|
+
for relation in relations:
|
|
89
|
+
yield f"ca-{relation.app.name}"
|
|
@@ -27,6 +27,8 @@ from datetime import datetime, timezone
|
|
|
27
27
|
from typing import Dict, List, Optional, Set, Tuple
|
|
28
28
|
|
|
29
29
|
import psycopg2
|
|
30
|
+
import psycopg2.errors
|
|
31
|
+
import psycopg2.extensions
|
|
30
32
|
from ops import ConfigData
|
|
31
33
|
from psycopg2.sql import SQL, Identifier, Literal
|
|
32
34
|
|
|
@@ -430,11 +432,10 @@ class PostgreSQL:
|
|
|
430
432
|
cursor.execute(connect_statement)
|
|
431
433
|
|
|
432
434
|
# Add extra user roles to the new user.
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
)
|
|
435
|
+
for role in roles:
|
|
436
|
+
cursor.execute(
|
|
437
|
+
SQL("GRANT {} TO {};").format(Identifier(role), Identifier(user))
|
|
438
|
+
)
|
|
438
439
|
except psycopg2.Error as e:
|
|
439
440
|
logger.error(f"Failed to create user: {e}")
|
|
440
441
|
raise PostgreSQLCreateUserError() from e
|
|
@@ -498,9 +499,10 @@ class PostgreSQL:
|
|
|
498
499
|
|
|
499
500
|
def _process_extra_user_roles(
|
|
500
501
|
self, user: str, extra_user_roles: Optional[List[str]] = None
|
|
501
|
-
) -> Tuple[
|
|
502
|
+
) -> Tuple[List[str], Set[str]]:
|
|
502
503
|
# Separate roles and privileges from the provided extra user roles.
|
|
503
|
-
roles =
|
|
504
|
+
roles = []
|
|
505
|
+
privileges = set()
|
|
504
506
|
if extra_user_roles:
|
|
505
507
|
if len(extra_user_roles) > 2 and sorted(extra_user_roles) != [
|
|
506
508
|
ROLE_ADMIN,
|
|
@@ -825,9 +827,9 @@ class PostgreSQL:
|
|
|
825
827
|
else f"DROP EXTENSION IF EXISTS {extension};"
|
|
826
828
|
)
|
|
827
829
|
self._configure_pgaudit(ordered_extensions.get("pgaudit", False))
|
|
828
|
-
except psycopg2.errors.UniqueViolation:
|
|
830
|
+
except psycopg2.errors.UniqueViolation: # type: ignore
|
|
829
831
|
pass
|
|
830
|
-
except psycopg2.errors.DependentObjectsStillExist:
|
|
832
|
+
except psycopg2.errors.DependentObjectsStillExist: # type: ignore
|
|
831
833
|
raise
|
|
832
834
|
except psycopg2.Error as e:
|
|
833
835
|
raise PostgreSQLEnableDisableExtensionError() from e
|
|
@@ -841,7 +843,7 @@ class PostgreSQL:
|
|
|
841
843
|
with self._connect_to_database() as connection, connection.cursor() as cursor:
|
|
842
844
|
# Should always be present
|
|
843
845
|
cursor.execute("SELECT last_archived_wal FROM pg_stat_archiver;")
|
|
844
|
-
return cursor.fetchone()[0]
|
|
846
|
+
return cursor.fetchone()[0]
|
|
845
847
|
except psycopg2.Error as e:
|
|
846
848
|
logger.error(f"Failed to get PostgreSQL last archived WAL: {e}")
|
|
847
849
|
raise PostgreSQLGetLastArchivedWALError() from e
|
|
@@ -852,7 +854,7 @@ class PostgreSQL:
|
|
|
852
854
|
with self._connect_to_database() as connection, connection.cursor() as cursor:
|
|
853
855
|
cursor.execute("SELECT timeline_id FROM pg_control_checkpoint();")
|
|
854
856
|
# There should always be a timeline
|
|
855
|
-
return cursor.fetchone()[0]
|
|
857
|
+
return cursor.fetchone()[0]
|
|
856
858
|
except psycopg2.Error as e:
|
|
857
859
|
logger.error(f"Failed to get PostgreSQL current timeline id: {e}")
|
|
858
860
|
raise PostgreSQLGetCurrentTimelineError() from e
|
|
@@ -909,7 +911,7 @@ class PostgreSQL:
|
|
|
909
911
|
) as connection, connection.cursor() as cursor:
|
|
910
912
|
cursor.execute("SELECT version();")
|
|
911
913
|
# Split to get only the version number. There should always be a version.
|
|
912
|
-
return cursor.fetchone()[0].split(" ")[1]
|
|
914
|
+
return cursor.fetchone()[0].split(" ")[1]
|
|
913
915
|
except psycopg2.Error as e:
|
|
914
916
|
logger.error(f"Failed to get PostgreSQL version: {e}")
|
|
915
917
|
raise PostgreSQLGetPostgreSQLVersionError() from e
|
|
@@ -930,7 +932,7 @@ class PostgreSQL:
|
|
|
930
932
|
) as connection, connection.cursor() as cursor:
|
|
931
933
|
cursor.execute("SHOW ssl;")
|
|
932
934
|
# SSL state should always be set
|
|
933
|
-
return "on" in cursor.fetchone()[0]
|
|
935
|
+
return "on" in cursor.fetchone()[0]
|
|
934
936
|
except psycopg2.Error:
|
|
935
937
|
# Connection errors happen when PostgreSQL has not started yet.
|
|
936
938
|
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|