postgresql-charms-single-kernel 16.3.4__tar.gz → 16.3.6__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.
Potentially problematic release.
This version of postgresql-charms-single-kernel might be problematic. Click here for more details.
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/PKG-INFO +4 -1
- postgresql_charms_single_kernel-16.3.6/pyproject.toml +166 -0
- postgresql_charms_single_kernel-16.3.4/pyproject.toml → postgresql_charms_single_kernel-16.3.6/pyproject.toml.orig +10 -6
- postgresql_charms_single_kernel-16.3.6/single_kernel_postgresql/charms/abstract_charm.py +152 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/charms/k8s_charm.py +30 -1
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/charms/vm_charm.py +31 -3
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/exceptions.py +4 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/core/peer_relation.py +56 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/core/state.py +12 -0
- postgresql_charms_single_kernel-16.3.6/single_kernel_postgresql/events/database.py +243 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/events/tls.py +19 -1
- postgresql_charms_single_kernel-16.3.6/single_kernel_postgresql/managers/config.py +751 -0
- postgresql_charms_single_kernel-16.3.6/single_kernel_postgresql/managers/database.py +667 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/managers/k8s.py +85 -2
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/managers/patroni.py +1 -1
- postgresql_charms_single_kernel-16.3.6/single_kernel_postgresql/templates/__init__.py +4 -0
- postgresql_charms_single_kernel-16.3.6/single_kernel_postgresql/templates/patroni.yml.j2 +296 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/base.py +9 -5
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/k8s.py +0 -4
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/paths/base.py +5 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/paths/k8s.py +2 -2
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/paths/vm.py +1 -1
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/vm.py +10 -0
- postgresql_charms_single_kernel-16.3.4/single_kernel_postgresql/charms/abstract_charm.py +0 -86
- 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.6}/LICENSE +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/README.md +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/charmcraft.yaml +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/charms/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/compat/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/compat/postgresql.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/compat/pyproject.toml +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/enums.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/literals.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/locales.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/pyproject.toml +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/config/statuses.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/core/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/core/config.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/core/relation_state.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/events/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/events/postgresql.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/events/tls_transfer.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/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.6}/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.6}/single_kernel_postgresql/managers/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/managers/base.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/managers/cluster.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/managers/tls.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/utils/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/utils/arch.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/utils/filesystem.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/utils/postgresql.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/utils/secret.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/utils/status.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/single_kernel_postgresql/workload/__init__.py +0 -0
- {postgresql_charms_single_kernel-16.3.4 → postgresql_charms_single_kernel-16.3.6}/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.6
|
|
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.6"
|
|
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.6"
|
|
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
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright 2025 Canonical Ltd.
|
|
2
|
+
# See LICENSE file for licensing details.
|
|
3
|
+
"""Skeleton for the abstract charm."""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
|
|
7
|
+
from data_platform_helpers.advanced_statuses import StatusHandler
|
|
8
|
+
from ops import StatusBase
|
|
9
|
+
from ops.charm import CharmBase
|
|
10
|
+
|
|
11
|
+
from single_kernel_postgresql.core.state import CharmState
|
|
12
|
+
from single_kernel_postgresql.events.database import DatabaseEventsHandler
|
|
13
|
+
from single_kernel_postgresql.events.postgresql import PostgreSQLEventsHandler
|
|
14
|
+
from single_kernel_postgresql.events.tls import TLS
|
|
15
|
+
from single_kernel_postgresql.lib.charms.data_platform_libs.v0.data_interfaces import (
|
|
16
|
+
DatabaseProvides,
|
|
17
|
+
)
|
|
18
|
+
from single_kernel_postgresql.managers.cluster import ClusterManager
|
|
19
|
+
from single_kernel_postgresql.managers.config import ConfigManager
|
|
20
|
+
from single_kernel_postgresql.managers.database import DatabaseManager
|
|
21
|
+
from single_kernel_postgresql.managers.patroni import PatroniManager
|
|
22
|
+
from single_kernel_postgresql.managers.tls import TLSManager
|
|
23
|
+
from single_kernel_postgresql.workload.base import BaseWorkload, ResourceProvider
|
|
24
|
+
|
|
25
|
+
from ..config.enums import Substrates
|
|
26
|
+
from ..config.literals import DATABASE
|
|
27
|
+
from ..utils.postgresql import PostgreSQL
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class AbstractPostgreSQLCharm(CharmBase, ABC):
|
|
31
|
+
"""An abstract PostgreSQL charm."""
|
|
32
|
+
|
|
33
|
+
def __init__(self, *args):
|
|
34
|
+
super().__init__(*args)
|
|
35
|
+
|
|
36
|
+
# State
|
|
37
|
+
self.state = CharmState(charm=self, substrate=self.substrate)
|
|
38
|
+
|
|
39
|
+
# TLS events handler owns the two certificate requirers; build it before the
|
|
40
|
+
# TLS manager so the manager can constructor-inject them for its live-fetch getters.
|
|
41
|
+
self.tls = TLS(self, self.state)
|
|
42
|
+
|
|
43
|
+
# Managers
|
|
44
|
+
self.tls_manager = TLSManager(
|
|
45
|
+
state=self.state,
|
|
46
|
+
workload=self.workload,
|
|
47
|
+
client_certificate=self.tls.client_certificate,
|
|
48
|
+
peer_certificate=self.tls.peer_certificate,
|
|
49
|
+
)
|
|
50
|
+
self.patroni_manager = PatroniManager(state=self.state, workload=self.workload)
|
|
51
|
+
self.cluster_manager = ClusterManager(state=self.state, workload=self.workload)
|
|
52
|
+
|
|
53
|
+
# Client-relation subsystem: the charm is the composition root, as with the
|
|
54
|
+
# other managers; the handler owns only the observers and guard/defer decisions.
|
|
55
|
+
self.database_manager = DatabaseManager(
|
|
56
|
+
state=self.state,
|
|
57
|
+
workload=self.workload,
|
|
58
|
+
database_provides=DatabaseProvides(self, relation_name=DATABASE),
|
|
59
|
+
set_unit_status=self.set_unit_status,
|
|
60
|
+
)
|
|
61
|
+
self.database = DatabaseEventsHandler(
|
|
62
|
+
self, self.state, self.database_manager, self.patroni_manager, self.tls_manager
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
self.config_manager = ConfigManager(
|
|
66
|
+
state=self.state,
|
|
67
|
+
workload=self.workload,
|
|
68
|
+
tls_manager=self.tls_manager,
|
|
69
|
+
patroni_manager=self.patroni_manager,
|
|
70
|
+
database_manager=self.database_manager,
|
|
71
|
+
resource_provider=self.get_resource_provider,
|
|
72
|
+
request_restart=self.request_restart,
|
|
73
|
+
restart_services=self.restart_services,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Events Handler
|
|
77
|
+
self.postgresql_events_handler = PostgreSQLEventsHandler(
|
|
78
|
+
self,
|
|
79
|
+
self.workload,
|
|
80
|
+
self.state,
|
|
81
|
+
self.cluster_manager,
|
|
82
|
+
self.tls_manager,
|
|
83
|
+
self.config_manager,
|
|
84
|
+
self.patroni_manager,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Status Handler
|
|
88
|
+
self.status_handler = StatusHandler(
|
|
89
|
+
self,
|
|
90
|
+
self.cluster_manager,
|
|
91
|
+
self.tls_manager,
|
|
92
|
+
self.config_manager,
|
|
93
|
+
self.patroni_manager,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Postgresql Client
|
|
97
|
+
@property
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def postgresql(self) -> PostgreSQL:
|
|
100
|
+
"""Return a PostgreSQL client."""
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
# Postgresql Workload
|
|
104
|
+
@property
|
|
105
|
+
@abstractmethod
|
|
106
|
+
def workload(self) -> BaseWorkload:
|
|
107
|
+
"""Access current workload."""
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
# Postgresql Substrate
|
|
111
|
+
@property
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def substrate(self) -> Substrates:
|
|
114
|
+
"""Access current substrate."""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
# Charm-side bridges the lib calls back into. request_restart/restart_services are
|
|
118
|
+
# substrate-tangled and stay until their own migration phases; update_config still
|
|
119
|
+
# supplies the ldap/async/watcher values those phases own; primary_endpoint is the
|
|
120
|
+
# VM's Patroni-derived primary lookup. set_unit_status routes status writes through
|
|
121
|
+
# the charm_refresh priority gate and stays until the refresh logic itself migrates
|
|
122
|
+
# into the library, at which point the managers own their status writes.
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def get_resource_provider(self) -> ResourceProvider:
|
|
125
|
+
"""Return the substrate's (cpu_cores, memory_bytes) introspector."""
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
@abstractmethod
|
|
129
|
+
def request_restart(self) -> None:
|
|
130
|
+
"""Run the substrate pre-restart side effect and acquire the restart lock."""
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
@abstractmethod
|
|
134
|
+
def restart_services(self) -> None:
|
|
135
|
+
"""Restart the monitoring and LDAP-sync sidecar services."""
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
@abstractmethod
|
|
139
|
+
def set_unit_status(self, status: StatusBase) -> None:
|
|
140
|
+
"""Set the unit status without overriding a higher-priority refresh status."""
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
@abstractmethod
|
|
144
|
+
def update_config(self) -> bool:
|
|
145
|
+
"""Re-render the Patroni configuration and apply it."""
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
@abstractmethod
|
|
150
|
+
def primary_endpoint(self) -> str | None:
|
|
151
|
+
"""Address of the cluster primary, or None when there is not one."""
|
|
152
|
+
pass
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
+
from ops import StatusBase
|
|
10
|
+
|
|
9
11
|
from single_kernel_postgresql.charms.abstract_charm import AbstractPostgreSQLCharm, PostgreSQL
|
|
10
12
|
from single_kernel_postgresql.config.enums import Substrates
|
|
11
13
|
from single_kernel_postgresql.config.literals import CONTAINER_NAME, SYSTEM_USERS, USER
|
|
@@ -52,7 +54,8 @@ class PostgreSQLK8sCharm(AbstractPostgreSQLCharm):
|
|
|
52
54
|
BaseWorkload: The K8sWorkload instance for this charm
|
|
53
55
|
"""
|
|
54
56
|
return K8sWorkload(
|
|
55
|
-
charm_dir=self.charm_dir,
|
|
57
|
+
charm_dir=self.charm_dir,
|
|
58
|
+
container=self.unit.get_container(CONTAINER_NAME),
|
|
56
59
|
)
|
|
57
60
|
|
|
58
61
|
@property
|
|
@@ -63,3 +66,29 @@ class PostgreSQLK8sCharm(AbstractPostgreSQLCharm):
|
|
|
63
66
|
Substrates: always Substrates.K8S for this charm
|
|
64
67
|
"""
|
|
65
68
|
return Substrates.K8S
|
|
69
|
+
|
|
70
|
+
# The concrete production charm owns these bridges (update_scrape_job_spec +
|
|
71
|
+
# acquire_lock, pebble metrics/ldap restarts, the refresh-aware status write and the
|
|
72
|
+
# config re-render), so they are minimal here.
|
|
73
|
+
def get_resource_provider(self) -> K8sManager:
|
|
74
|
+
"""Return the substrate's (cpu_cores, memory_bytes) introspector."""
|
|
75
|
+
return self.k8s_manager
|
|
76
|
+
|
|
77
|
+
def request_restart(self) -> None:
|
|
78
|
+
"""Run the substrate pre-restart side effect and acquire the restart lock."""
|
|
79
|
+
|
|
80
|
+
def restart_services(self) -> None:
|
|
81
|
+
"""Restart the monitoring and LDAP-sync sidecar services."""
|
|
82
|
+
|
|
83
|
+
def set_unit_status(self, status: StatusBase) -> None:
|
|
84
|
+
"""Set the unit status without overriding a higher-priority refresh status."""
|
|
85
|
+
self.unit.status = status
|
|
86
|
+
|
|
87
|
+
def update_config(self) -> bool:
|
|
88
|
+
"""Re-render the Patroni configuration and apply it."""
|
|
89
|
+
return self.config_manager.update_config(self.postgresql)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def primary_endpoint(self) -> str | None:
|
|
93
|
+
"""Address of the cluster primary's Service."""
|
|
94
|
+
return self.state.primary_endpoint
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
|
+
from ops import StatusBase
|
|
10
|
+
|
|
9
11
|
from single_kernel_postgresql.charms.abstract_charm import AbstractPostgreSQLCharm, PostgreSQL
|
|
10
12
|
from single_kernel_postgresql.config.enums import Substrates
|
|
11
13
|
from single_kernel_postgresql.config.literals import SYSTEM_USERS, USER
|
|
12
|
-
from single_kernel_postgresql.workload.base import BaseWorkload
|
|
13
14
|
from single_kernel_postgresql.workload.vm import VMWorkload
|
|
14
15
|
|
|
15
16
|
logger = logging.getLogger(__name__)
|
|
@@ -38,13 +39,13 @@ class PostgreSQLVMCharm(AbstractPostgreSQLCharm):
|
|
|
38
39
|
)
|
|
39
40
|
|
|
40
41
|
@property
|
|
41
|
-
def workload(self) ->
|
|
42
|
+
def workload(self) -> VMWorkload:
|
|
42
43
|
"""Access current workload instance.
|
|
43
44
|
|
|
44
45
|
Returns the workload object.
|
|
45
46
|
|
|
46
47
|
Returns:
|
|
47
|
-
|
|
48
|
+
VMWorkload: The VMWorkload instance for this charm
|
|
48
49
|
"""
|
|
49
50
|
return VMWorkload(charm_dir=self.charm_dir)
|
|
50
51
|
|
|
@@ -56,3 +57,30 @@ class PostgreSQLVMCharm(AbstractPostgreSQLCharm):
|
|
|
56
57
|
Substrates: always Substrates.VM for this charm
|
|
57
58
|
"""
|
|
58
59
|
return Substrates.VM
|
|
60
|
+
|
|
61
|
+
# The concrete production charm owns these bridges (pops postgresql_restarted +
|
|
62
|
+
# acquire_lock, snap metrics/ldap restarts, the refresh-aware status write, the
|
|
63
|
+
# Patroni-derived primary lookup and the config re-render), so they are minimal here.
|
|
64
|
+
def get_resource_provider(self) -> VMWorkload:
|
|
65
|
+
"""Return the substrate's (cpu_cores, memory_bytes) introspector."""
|
|
66
|
+
return self.workload
|
|
67
|
+
|
|
68
|
+
def request_restart(self) -> None:
|
|
69
|
+
"""Run the substrate pre-restart side effect and acquire the restart lock."""
|
|
70
|
+
|
|
71
|
+
def restart_services(self) -> None:
|
|
72
|
+
"""Restart the monitoring and LDAP-sync sidecar services."""
|
|
73
|
+
|
|
74
|
+
def set_unit_status(self, status: StatusBase) -> None:
|
|
75
|
+
"""Set the unit status without overriding a higher-priority refresh status."""
|
|
76
|
+
self.unit.status = status
|
|
77
|
+
|
|
78
|
+
def update_config(self) -> bool:
|
|
79
|
+
"""Re-render the Patroni configuration and apply it."""
|
|
80
|
+
return self.config_manager.update_config(self.postgresql)
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def primary_endpoint(self) -> str | None:
|
|
84
|
+
"""Address of the cluster primary, or None when there is not one."""
|
|
85
|
+
primary = self.patroni_manager.get_primary()
|
|
86
|
+
return self.patroni_manager.get_member_ip(primary) if primary else None
|
|
@@ -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:
|
|
@@ -235,6 +235,13 @@ class CharmState(Object):
|
|
|
235
235
|
addresses.remove(current_unit_ip)
|
|
236
236
|
return addresses
|
|
237
237
|
|
|
238
|
+
def unit_database_address(self, unit: Unit, relation_name: str = DATABASE) -> str | None:
|
|
239
|
+
"""The client-facing address a peer unit published for the given relation."""
|
|
240
|
+
if not (peer_relation := self.peer_relation):
|
|
241
|
+
return None
|
|
242
|
+
with suppress(KeyError):
|
|
243
|
+
return peer_relation.data[unit].get(f"{relation_name}-address")
|
|
244
|
+
|
|
238
245
|
@property
|
|
239
246
|
def host(self) -> str:
|
|
240
247
|
"""Current unit host."""
|
|
@@ -526,3 +533,8 @@ class CharmState(Object):
|
|
|
526
533
|
def primary_endpoint(self) -> str:
|
|
527
534
|
"""Returns the endpoint of the primary instance's service."""
|
|
528
535
|
return self._build_service_name("primary")
|
|
536
|
+
|
|
537
|
+
@property
|
|
538
|
+
def replicas_endpoint(self) -> str:
|
|
539
|
+
"""Returns the endpoint of the replicas instances' service."""
|
|
540
|
+
return self._build_service_name("replicas")
|