postgresql-charms-single-kernel 16.3.4__tar.gz → 16.3.5__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.3.4 → postgresql_charms_single_kernel-16.3.5}/PKG-INFO +4 -1
- postgresql_charms_single_kernel-16.3.5/pyproject.toml +166 -0
- postgresql_charms_single_kernel-16.3.4/pyproject.toml → postgresql_charms_single_kernel-16.3.5/pyproject.toml.orig +10 -6
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/charms/abstract_charm.py +27 -1
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/charms/k8s_charm.py +15 -1
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/charms/vm_charm.py +15 -3
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/exceptions.py +4 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/core/peer_relation.py +56 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/core/state.py +14 -1
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/events/tls.py +19 -1
- postgresql_charms_single_kernel-16.3.5/single_kernel_postgresql/managers/config.py +728 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/managers/k8s.py +85 -2
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/managers/patroni.py +1 -1
- postgresql_charms_single_kernel-16.3.5/single_kernel_postgresql/templates/__init__.py +4 -0
- postgresql_charms_single_kernel-16.3.5/single_kernel_postgresql/templates/patroni.yml.j2 +296 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/base.py +9 -5
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/k8s.py +0 -4
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/paths/base.py +5 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/paths/k8s.py +2 -2
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/paths/vm.py +1 -1
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/vm.py +10 -0
- postgresql_charms_single_kernel-16.3.4/single_kernel_postgresql/managers/config.py +0 -295
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/LICENSE +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/README.md +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/charmcraft.yaml +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/charms/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/compat/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/compat/postgresql.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/compat/pyproject.toml +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/enums.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/literals.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/locales.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/pyproject.toml +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/config/statuses.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/core/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/core/config.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/core/relation_state.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/events/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/events/postgresql.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/events/tls_transfer.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/lib/charms/data_platform_libs/v0/data_interfaces.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/lib/charms/data_platform_libs/v1/data_models.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/managers/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/managers/base.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/managers/cluster.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/managers/tls.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/utils/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/utils/arch.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/utils/filesystem.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/utils/postgresql.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/utils/secret.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/utils/status.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.5}/single_kernel_postgresql/workload/paths/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: postgresql-charms-single-kernel
|
|
3
|
-
Version: 16.3.
|
|
3
|
+
Version: 16.3.5
|
|
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>
|
|
@@ -212,6 +212,8 @@ Classifier: Operating System :: POSIX :: Linux
|
|
|
212
212
|
Requires-Dist: ops>=2.0.0
|
|
213
213
|
Requires-Dist: tenacity>=9.0.0
|
|
214
214
|
Requires-Dist: psycopg2>=2.9.10 ; extra == 'db-driver'
|
|
215
|
+
Requires-Dist: lightkube>=0.21.0 ; python_full_version >= '3.12' and extra == 'k8s'
|
|
216
|
+
Requires-Dist: lightkube-models>=1.28.1.4 ; python_full_version >= '3.12' and extra == 'k8s'
|
|
215
217
|
Requires-Dist: httpx ; python_full_version >= '3.12' and extra == 'postgresql'
|
|
216
218
|
Requires-Dist: data-platform-helpers>=0.1.7 ; python_full_version >= '3.12' and extra == 'postgresql'
|
|
217
219
|
Requires-Dist: charmlibs-pathops>=1.0.1 ; python_full_version >= '3.12' and extra == 'postgresql'
|
|
@@ -228,6 +230,7 @@ Requires-Dist: charmlibs-snap>=1.0.1 ; python_full_version >= '3.12' and extra =
|
|
|
228
230
|
Requires-Dist: charmlibs-systemd>=1.0.0.post0 ; python_full_version >= '3.12' and extra == 'vm'
|
|
229
231
|
Requires-Python: >=3.8, <4.0
|
|
230
232
|
Provides-Extra: db-driver
|
|
233
|
+
Provides-Extra: k8s
|
|
231
234
|
Provides-Extra: postgresql
|
|
232
235
|
Provides-Extra: vm
|
|
233
236
|
Description-Content-Type: text/markdown
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "postgresql-charms-single-kernel"
|
|
3
|
+
description = "Shared and reusable code for PostgreSQL-related charms"
|
|
4
|
+
version = "16.3.5"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
classifiers = [
|
|
7
|
+
"Development Status :: 3 - Alpha",
|
|
8
|
+
"Intended Audience :: Developers",
|
|
9
|
+
"Operating System :: POSIX :: Linux",
|
|
10
|
+
]
|
|
11
|
+
requires-python = ">=3.8,<4.0"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"ops>=2.0.0",
|
|
14
|
+
"tenacity>=9.0.0",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.license]
|
|
18
|
+
file = "LICENSE"
|
|
19
|
+
|
|
20
|
+
[[project.authors]]
|
|
21
|
+
name = "Canonical Data Platform"
|
|
22
|
+
email = "data-platform@lists.launchpad.net"
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
postgresql = [
|
|
26
|
+
"httpx; python_version >= '3.12'",
|
|
27
|
+
"data-platform-helpers>=0.1.7; python_version >= '3.12'",
|
|
28
|
+
"charmlibs-pathops>=1.0.1; python_version >= '3.12'",
|
|
29
|
+
"charmlibs-interfaces-tls-certificates>=1.8.3; python_version >= '3.12'",
|
|
30
|
+
"charm-refresh; python_version >= '3.12'",
|
|
31
|
+
"requests; python_version >= '3.12'",
|
|
32
|
+
"tomli; python_version >= '3.12'",
|
|
33
|
+
"pydantic>=2.0; python_version >= '3.12'",
|
|
34
|
+
"jinja2 >=3.1.6; python_version >= '3.12'",
|
|
35
|
+
"charmlibs-rollingops>=1.1.1; python_version >= '3.12'",
|
|
36
|
+
]
|
|
37
|
+
vm = [
|
|
38
|
+
"pysyncobj>=0.3.15; python_version >= '3.12'",
|
|
39
|
+
"psutil>=7.2.2; python_version >= '3.12'",
|
|
40
|
+
"charmlibs-snap>=1.0.1; python_version >= '3.12'",
|
|
41
|
+
"charmlibs-systemd>=1.0.0.post0; python_version >= '3.12'",
|
|
42
|
+
]
|
|
43
|
+
k8s = [
|
|
44
|
+
"lightkube>=0.21.0; python_version >= '3.12'",
|
|
45
|
+
"lightkube-models>=1.28.1.4; python_version >= '3.12'",
|
|
46
|
+
]
|
|
47
|
+
db-driver = ["psycopg2>=2.9.10"]
|
|
48
|
+
|
|
49
|
+
[build-system]
|
|
50
|
+
requires = ["uv_build>=0.12.1,<0.13.0"]
|
|
51
|
+
build-backend = "uv_build"
|
|
52
|
+
|
|
53
|
+
[tool.uv]
|
|
54
|
+
exclude-newer = "1 day"
|
|
55
|
+
|
|
56
|
+
[tool.uv.build-backend]
|
|
57
|
+
module-name = "single_kernel_postgresql"
|
|
58
|
+
module-root = ""
|
|
59
|
+
|
|
60
|
+
[tool.uv.dependency-groups.unit]
|
|
61
|
+
requires-python = ">=3.12"
|
|
62
|
+
|
|
63
|
+
[tool.uv.dependency-groups.lint]
|
|
64
|
+
requires-python = ">=3.12"
|
|
65
|
+
|
|
66
|
+
[tool.uv.dependency-groups.format]
|
|
67
|
+
requires-python = ">=3.12"
|
|
68
|
+
|
|
69
|
+
[tool.coverage.run]
|
|
70
|
+
branch = true
|
|
71
|
+
|
|
72
|
+
[tool.coverage.report]
|
|
73
|
+
show_missing = true
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
minversion = "6.0"
|
|
77
|
+
log_cli_level = "INFO"
|
|
78
|
+
|
|
79
|
+
[tool.ruff]
|
|
80
|
+
preview = true
|
|
81
|
+
target-version = "py312"
|
|
82
|
+
src = [
|
|
83
|
+
"src",
|
|
84
|
+
".",
|
|
85
|
+
]
|
|
86
|
+
line-length = 99
|
|
87
|
+
|
|
88
|
+
[tool.ruff.lint]
|
|
89
|
+
explicit-preview-rules = true
|
|
90
|
+
select = [
|
|
91
|
+
"A",
|
|
92
|
+
"E",
|
|
93
|
+
"W",
|
|
94
|
+
"F",
|
|
95
|
+
"C",
|
|
96
|
+
"N",
|
|
97
|
+
"D",
|
|
98
|
+
"I001",
|
|
99
|
+
"B",
|
|
100
|
+
"CPY001",
|
|
101
|
+
"RUF",
|
|
102
|
+
"S",
|
|
103
|
+
"SIM",
|
|
104
|
+
"UP",
|
|
105
|
+
"TCH",
|
|
106
|
+
]
|
|
107
|
+
extend-ignore = [
|
|
108
|
+
"D203",
|
|
109
|
+
"D204",
|
|
110
|
+
"D213",
|
|
111
|
+
"D215",
|
|
112
|
+
"D400",
|
|
113
|
+
"D404",
|
|
114
|
+
"D406",
|
|
115
|
+
"D407",
|
|
116
|
+
"D408",
|
|
117
|
+
"D409",
|
|
118
|
+
"D413",
|
|
119
|
+
]
|
|
120
|
+
ignore = [
|
|
121
|
+
"E501",
|
|
122
|
+
"D107",
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
[tool.ruff.lint.per-file-ignores]
|
|
126
|
+
"tests/*" = [
|
|
127
|
+
"D100",
|
|
128
|
+
"D101",
|
|
129
|
+
"D102",
|
|
130
|
+
"D103",
|
|
131
|
+
"D104",
|
|
132
|
+
"B011",
|
|
133
|
+
"S",
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
[tool.ruff.lint.flake8-copyright]
|
|
137
|
+
author = "Canonical Ltd."
|
|
138
|
+
notice-rgx = 'Copyright\s\d{4}([-,]\d{4})*\s+'
|
|
139
|
+
min-file-size = 1
|
|
140
|
+
|
|
141
|
+
[tool.ruff.lint.mccabe]
|
|
142
|
+
max-complexity = 10
|
|
143
|
+
|
|
144
|
+
[tool.ruff.lint.pydocstyle]
|
|
145
|
+
convention = "google"
|
|
146
|
+
|
|
147
|
+
[tool.ty.environment]
|
|
148
|
+
python = ".tox/lint/"
|
|
149
|
+
python-version = "3.12"
|
|
150
|
+
|
|
151
|
+
[tool.ty.src]
|
|
152
|
+
exclude = [
|
|
153
|
+
"tests",
|
|
154
|
+
"single_kernel_postgresql/lib",
|
|
155
|
+
]
|
|
156
|
+
|
|
157
|
+
[dependency-groups]
|
|
158
|
+
format = ["ruff==0.16.3"]
|
|
159
|
+
lint = [
|
|
160
|
+
"codespell==2.4.3",
|
|
161
|
+
"ty==0.0.72",
|
|
162
|
+
]
|
|
163
|
+
unit = [
|
|
164
|
+
"coverage[toml]==7.15.4",
|
|
165
|
+
"pytest==9.1.1",
|
|
166
|
+
]
|
|
@@ -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.3.
|
|
7
|
+
version = "16.3.5"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
license = {file = "LICENSE"}
|
|
10
10
|
authors = [
|
|
@@ -40,12 +40,16 @@ vm = [
|
|
|
40
40
|
"charmlibs-snap>=1.0.1; python_version >= '3.12'",
|
|
41
41
|
"charmlibs-systemd>=1.0.0.post0; python_version >= '3.12'"
|
|
42
42
|
]
|
|
43
|
+
k8s = [
|
|
44
|
+
"lightkube>=0.21.0; python_version >= '3.12'",
|
|
45
|
+
"lightkube-models>=1.28.1.4; python_version >= '3.12'",
|
|
46
|
+
]
|
|
43
47
|
db-driver = [
|
|
44
48
|
"psycopg2>=2.9.10",
|
|
45
49
|
]
|
|
46
50
|
|
|
47
51
|
[build-system]
|
|
48
|
-
requires = ["uv_build>=0.
|
|
52
|
+
requires = ["uv_build>=0.12.1,<0.13.0"]
|
|
49
53
|
build-backend = "uv_build"
|
|
50
54
|
|
|
51
55
|
[tool.uv]
|
|
@@ -57,14 +61,14 @@ module-root = ""
|
|
|
57
61
|
|
|
58
62
|
[dependency-groups]
|
|
59
63
|
format = [
|
|
60
|
-
"ruff==0.
|
|
64
|
+
"ruff==0.16.3"
|
|
61
65
|
]
|
|
62
66
|
lint = [
|
|
63
|
-
"codespell==2.4.
|
|
64
|
-
"ty==0.0.
|
|
67
|
+
"codespell==2.4.3",
|
|
68
|
+
"ty==0.0.72"
|
|
65
69
|
]
|
|
66
70
|
unit = [
|
|
67
|
-
"coverage[toml]==7.15.
|
|
71
|
+
"coverage[toml]==7.15.4",
|
|
68
72
|
"pytest==9.1.1"
|
|
69
73
|
]
|
|
70
74
|
|
|
@@ -42,7 +42,15 @@ class AbstractPostgreSQLCharm(CharmBase, ABC):
|
|
|
42
42
|
)
|
|
43
43
|
self.patroni_manager = PatroniManager(state=self.state, workload=self.workload)
|
|
44
44
|
self.cluster_manager = ClusterManager(state=self.state, workload=self.workload)
|
|
45
|
-
self.config_manager = ConfigManager(
|
|
45
|
+
self.config_manager = ConfigManager(
|
|
46
|
+
state=self.state,
|
|
47
|
+
workload=self.workload,
|
|
48
|
+
tls_manager=self.tls_manager,
|
|
49
|
+
patroni_manager=self.patroni_manager,
|
|
50
|
+
request_restart=self.request_restart,
|
|
51
|
+
refresh_endpoints=self.refresh_endpoints,
|
|
52
|
+
restart_services=self.restart_services,
|
|
53
|
+
)
|
|
46
54
|
|
|
47
55
|
# Events Handler
|
|
48
56
|
self.postgresql_events_handler = PostgreSQLEventsHandler(
|
|
@@ -84,3 +92,21 @@ class AbstractPostgreSQLCharm(CharmBase, ABC):
|
|
|
84
92
|
def substrate(self) -> Substrates:
|
|
85
93
|
"""Access current substrate."""
|
|
86
94
|
pass
|
|
95
|
+
|
|
96
|
+
# Config-update bridges: charm-side callables the ConfigManager invokes for the
|
|
97
|
+
# substrate-tangled restart trigger, endpoint refresh and monitoring/ldap service
|
|
98
|
+
# restarts. They stay in the charm until their own migration phases.
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def request_restart(self) -> None:
|
|
101
|
+
"""Run the substrate pre-restart side effect and acquire the restart lock."""
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
@abstractmethod
|
|
105
|
+
def refresh_endpoints(self) -> None:
|
|
106
|
+
"""Refresh the client relation endpoints."""
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@abstractmethod
|
|
110
|
+
def restart_services(self) -> None:
|
|
111
|
+
"""Restart the monitoring and LDAP-sync sidecar services."""
|
|
112
|
+
pass
|
|
@@ -26,6 +26,7 @@ class PostgreSQLK8sCharm(AbstractPostgreSQLCharm):
|
|
|
26
26
|
"Workload must be an instance of K8sWorkload"
|
|
27
27
|
)
|
|
28
28
|
self.k8s_manager = K8sManager(self.state, self.workload)
|
|
29
|
+
self.state.resource_provider = self.k8s_manager
|
|
29
30
|
|
|
30
31
|
@property
|
|
31
32
|
def postgresql(self) -> PostgreSQL:
|
|
@@ -52,7 +53,8 @@ class PostgreSQLK8sCharm(AbstractPostgreSQLCharm):
|
|
|
52
53
|
BaseWorkload: The K8sWorkload instance for this charm
|
|
53
54
|
"""
|
|
54
55
|
return K8sWorkload(
|
|
55
|
-
charm_dir=self.charm_dir,
|
|
56
|
+
charm_dir=self.charm_dir,
|
|
57
|
+
container=self.unit.get_container(CONTAINER_NAME),
|
|
56
58
|
)
|
|
57
59
|
|
|
58
60
|
@property
|
|
@@ -63,3 +65,15 @@ class PostgreSQLK8sCharm(AbstractPostgreSQLCharm):
|
|
|
63
65
|
Substrates: always Substrates.K8S for this charm
|
|
64
66
|
"""
|
|
65
67
|
return Substrates.K8S
|
|
68
|
+
|
|
69
|
+
# The concrete production charm owns these bridges (update_scrape_job_spec +
|
|
70
|
+
# acquire_lock, update_endpoints, pebble metrics/ldap restarts), so they default to
|
|
71
|
+
# no-ops here.
|
|
72
|
+
def request_restart(self) -> None:
|
|
73
|
+
"""Run the substrate pre-restart side effect and acquire the restart lock."""
|
|
74
|
+
|
|
75
|
+
def refresh_endpoints(self) -> None:
|
|
76
|
+
"""Refresh the client relation endpoints."""
|
|
77
|
+
|
|
78
|
+
def restart_services(self) -> None:
|
|
79
|
+
"""Restart the monitoring and LDAP-sync sidecar services."""
|
|
@@ -9,7 +9,6 @@ import logging
|
|
|
9
9
|
from single_kernel_postgresql.charms.abstract_charm import AbstractPostgreSQLCharm, PostgreSQL
|
|
10
10
|
from single_kernel_postgresql.config.enums import Substrates
|
|
11
11
|
from single_kernel_postgresql.config.literals import SYSTEM_USERS, USER
|
|
12
|
-
from single_kernel_postgresql.workload.base import BaseWorkload
|
|
13
12
|
from single_kernel_postgresql.workload.vm import VMWorkload
|
|
14
13
|
|
|
15
14
|
logger = logging.getLogger(__name__)
|
|
@@ -21,6 +20,7 @@ class PostgreSQLVMCharm(AbstractPostgreSQLCharm):
|
|
|
21
20
|
def __init__(self, *args):
|
|
22
21
|
"""Initialize the PostgreSQL VM Charm."""
|
|
23
22
|
super().__init__(*args)
|
|
23
|
+
self.state.resource_provider = self.workload
|
|
24
24
|
|
|
25
25
|
@property
|
|
26
26
|
def postgresql(self) -> PostgreSQL:
|
|
@@ -38,13 +38,13 @@ class PostgreSQLVMCharm(AbstractPostgreSQLCharm):
|
|
|
38
38
|
)
|
|
39
39
|
|
|
40
40
|
@property
|
|
41
|
-
def workload(self) ->
|
|
41
|
+
def workload(self) -> VMWorkload:
|
|
42
42
|
"""Access current workload instance.
|
|
43
43
|
|
|
44
44
|
Returns the workload object.
|
|
45
45
|
|
|
46
46
|
Returns:
|
|
47
|
-
|
|
47
|
+
VMWorkload: The VMWorkload instance for this charm
|
|
48
48
|
"""
|
|
49
49
|
return VMWorkload(charm_dir=self.charm_dir)
|
|
50
50
|
|
|
@@ -56,3 +56,15 @@ class PostgreSQLVMCharm(AbstractPostgreSQLCharm):
|
|
|
56
56
|
Substrates: always Substrates.VM for this charm
|
|
57
57
|
"""
|
|
58
58
|
return Substrates.VM
|
|
59
|
+
|
|
60
|
+
# The concrete production charm owns these bridges (pops postgresql_restarted +
|
|
61
|
+
# acquire_lock, update_endpoints, snap metrics/ldap restarts), so they default to
|
|
62
|
+
# no-ops here.
|
|
63
|
+
def request_restart(self) -> None:
|
|
64
|
+
"""Run the substrate pre-restart side effect and acquire the restart lock."""
|
|
65
|
+
|
|
66
|
+
def refresh_endpoints(self) -> None:
|
|
67
|
+
"""Refresh the client relation endpoints."""
|
|
68
|
+
|
|
69
|
+
def restart_services(self) -> None:
|
|
70
|
+
"""Restart the monitoring and LDAP-sync sidecar services."""
|
|
@@ -73,3 +73,7 @@ class SwitchoverNotSyncError(SwitchoverFailedError):
|
|
|
73
73
|
|
|
74
74
|
class UpdateSyncNodeCountError(Exception):
|
|
75
75
|
"""Raised when updating synchronous_node_count failed for some reason."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class DeployedWithoutTrustError(Exception):
|
|
79
|
+
"""Raised when the K8s API denies access because the app wasn't deployed with --trust."""
|
|
@@ -225,6 +225,48 @@ class PostgreSQLPeer(RelationState):
|
|
|
225
225
|
return True
|
|
226
226
|
return self.relation.data[self.unit].get("connectivity", "on") == "on"
|
|
227
227
|
|
|
228
|
+
@property
|
|
229
|
+
def config_hash(self) -> str | None:
|
|
230
|
+
"""Get the last-applied PostgreSQL config hash from the peer relation data."""
|
|
231
|
+
if not self.relation:
|
|
232
|
+
return None
|
|
233
|
+
return self.relation.data[self.unit].get("config_hash")
|
|
234
|
+
|
|
235
|
+
@config_hash.setter
|
|
236
|
+
def config_hash(self, value: str) -> None:
|
|
237
|
+
"""Set the last-applied PostgreSQL config hash in the peer relation data."""
|
|
238
|
+
if not self.relation:
|
|
239
|
+
return
|
|
240
|
+
self.relation.data[self.unit]["config_hash"] = value
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def user_hash(self) -> str | None:
|
|
244
|
+
"""Get the last-applied users hash from the peer relation data."""
|
|
245
|
+
if not self.relation:
|
|
246
|
+
return None
|
|
247
|
+
return self.relation.data[self.unit].get("user_hash")
|
|
248
|
+
|
|
249
|
+
@user_hash.setter
|
|
250
|
+
def user_hash(self, value: str) -> None:
|
|
251
|
+
"""Set the last-applied users hash in the peer relation data."""
|
|
252
|
+
if not self.relation:
|
|
253
|
+
return
|
|
254
|
+
self.relation.data[self.unit]["user_hash"] = value
|
|
255
|
+
|
|
256
|
+
@property
|
|
257
|
+
def tls(self) -> bool:
|
|
258
|
+
"""Get the last-rendered TLS flag from the peer relation data."""
|
|
259
|
+
if not self.relation:
|
|
260
|
+
return False
|
|
261
|
+
return self.relation.data[self.unit].get("tls") == "enabled"
|
|
262
|
+
|
|
263
|
+
@tls.setter
|
|
264
|
+
def tls(self, value: bool) -> None:
|
|
265
|
+
"""Set the last-rendered TLS flag in the peer relation data."""
|
|
266
|
+
if not self.relation:
|
|
267
|
+
return
|
|
268
|
+
self.relation.data[self.unit]["tls"] = "enabled" if value else ""
|
|
269
|
+
|
|
228
270
|
@cached_property
|
|
229
271
|
def data(self) -> MutableMapping[str, str]:
|
|
230
272
|
"""Escape hatch method to access the peer data directly."""
|
|
@@ -418,6 +460,20 @@ class PostgreSQLApplication(RelationState):
|
|
|
418
460
|
"""Return whether this unit has LDAP enabled."""
|
|
419
461
|
return self.is_ldap_charm_related and self.is_cluster_initialised
|
|
420
462
|
|
|
463
|
+
@property
|
|
464
|
+
def user_hash(self) -> str | None:
|
|
465
|
+
"""Get the last-applied users hash from the peer relation data."""
|
|
466
|
+
if not self.relation:
|
|
467
|
+
return None
|
|
468
|
+
return self.relation.data[self.app].get("user_hash")
|
|
469
|
+
|
|
470
|
+
@user_hash.setter
|
|
471
|
+
def user_hash(self, value: str) -> None:
|
|
472
|
+
"""Set the last-applied users hash in the peer relation data."""
|
|
473
|
+
if not self.relation:
|
|
474
|
+
return
|
|
475
|
+
self.relation.data[self.app]["user_hash"] = value
|
|
476
|
+
|
|
421
477
|
def get_secret(self, key: str) -> str | None:
|
|
422
478
|
"""Get the secret value for 'key' from the peer relation data."""
|
|
423
479
|
if not self.relation:
|
|
@@ -8,7 +8,7 @@ import re
|
|
|
8
8
|
import socket
|
|
9
9
|
from contextlib import suppress
|
|
10
10
|
from functools import cached_property
|
|
11
|
-
from typing import Any, get_args
|
|
11
|
+
from typing import TYPE_CHECKING, Any, get_args
|
|
12
12
|
|
|
13
13
|
from data_platform_helpers.advanced_statuses import StatusesState, StatusObject
|
|
14
14
|
from data_platform_helpers.advanced_statuses.types import Scope as AdvancedStatusesScope
|
|
@@ -44,6 +44,9 @@ from single_kernel_postgresql.utils import unit_name_to_pod_name
|
|
|
44
44
|
from single_kernel_postgresql.utils.secret import translate_field_to_secret_key
|
|
45
45
|
from single_kernel_postgresql.utils.status import format_status
|
|
46
46
|
|
|
47
|
+
if TYPE_CHECKING:
|
|
48
|
+
from single_kernel_postgresql.workload.base import ResourceProvider
|
|
49
|
+
|
|
47
50
|
|
|
48
51
|
class CharmState(Object):
|
|
49
52
|
"""The global PostgreSQL Charm State."""
|
|
@@ -61,6 +64,16 @@ class CharmState(Object):
|
|
|
61
64
|
|
|
62
65
|
self.statuses = StatusesState(self, STATUS_PEERS_RELATION)
|
|
63
66
|
|
|
67
|
+
# Set by the charm to the substrate's resource introspector (VM workload / K8s manager).
|
|
68
|
+
self.resource_provider: ResourceProvider | None = None
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def available_resources(self) -> tuple[int, int]:
|
|
72
|
+
"""(cpu_cores, memory_bytes) for this unit, via the substrate's provider."""
|
|
73
|
+
if self.resource_provider is None:
|
|
74
|
+
raise RuntimeError("resource_provider was not set by the charm")
|
|
75
|
+
return self.resource_provider.get_available_resources()
|
|
76
|
+
|
|
64
77
|
# -- Charm Config
|
|
65
78
|
@cached_property
|
|
66
79
|
def config(self) -> CharmConfig:
|
|
@@ -25,6 +25,10 @@ class RefreshTLSCertificatesEvent(EventBase):
|
|
|
25
25
|
"""Event emitted to trigger a re-request of TLS certificates with updated SANs."""
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class TLSFilesPushedEvent(EventBase):
|
|
29
|
+
"""Event emitted after the handler successfully pushes cert files to the workload."""
|
|
30
|
+
|
|
31
|
+
|
|
28
32
|
class TLS(Object):
|
|
29
33
|
"""Owns the client/peer certificate requirers and pushes assigned certs to the workload.
|
|
30
34
|
|
|
@@ -50,9 +54,18 @@ class TLS(Object):
|
|
|
50
54
|
trigger. The client/peer SANs in the certificate requests remain inert until
|
|
51
55
|
the cluster (address-writer) code migrates and starts writing
|
|
52
56
|
``database-address`` / ``database-peers-address`` keys into the peer databag.
|
|
57
|
+
|
|
58
|
+
4. **Push-completion event.** ``tls_files_pushed`` fires only after a successful
|
|
59
|
+
``push_tls_files()``. The charm's reload bridge observes it rather than
|
|
60
|
+
``certificate_available`` directly, so a deferred push (Pebble not ready, or the
|
|
61
|
+
internal CA not yet present) does not trigger a reload against files that were
|
|
62
|
+
never written -- the retried push is what emits and reloads. Defer is per-observer,
|
|
63
|
+
so coupling the reload to this event is the only way a deferred push and its reload
|
|
64
|
+
retry together.
|
|
53
65
|
"""
|
|
54
66
|
|
|
55
67
|
refresh_tls_certificates_event = EventSource(RefreshTLSCertificatesEvent)
|
|
68
|
+
tls_files_pushed = EventSource(TLSFilesPushedEvent)
|
|
56
69
|
|
|
57
70
|
def __init__(self, charm, state):
|
|
58
71
|
super().__init__(charm, key="tls")
|
|
@@ -118,13 +131,16 @@ class TLS(Object):
|
|
|
118
131
|
self.refresh_tls_certificates_event.emit()
|
|
119
132
|
|
|
120
133
|
def _push_tls_files(self, event) -> None:
|
|
121
|
-
"""Guard
|
|
134
|
+
"""Guard, push, then emit ``tls_files_pushed`` on success; defer if not ready.
|
|
122
135
|
|
|
123
136
|
Two conditions must hold before files can be written:
|
|
124
137
|
1. The internal CA secret must exist — it is written by the leader on
|
|
125
138
|
leader-elected, so non-leaders and early hooks may see it absent.
|
|
126
139
|
2. The workload must accept file writes — on K8s the Pebble container
|
|
127
140
|
may not be ready yet, causing PostgreSQLFileOperationError.
|
|
141
|
+
|
|
142
|
+
Either guard defers without emitting, so nothing reloads against unwritten files;
|
|
143
|
+
the emit happens only once the push has actually landed.
|
|
128
144
|
"""
|
|
129
145
|
if not self.state.application.internal_ca:
|
|
130
146
|
logger.debug("Internal CA not yet present; deferring TLS file push.")
|
|
@@ -135,6 +151,8 @@ class TLS(Object):
|
|
|
135
151
|
except PostgreSQLFileOperationError:
|
|
136
152
|
logger.debug("Workload not ready for TLS file write; deferring.")
|
|
137
153
|
event.defer()
|
|
154
|
+
return
|
|
155
|
+
self.tls_files_pushed.emit()
|
|
138
156
|
|
|
139
157
|
def _on_certificate_available(self, event) -> None:
|
|
140
158
|
"""Push TLS files; the operator client cert/key is read live at push time."""
|