postgresql-charms-single-kernel 16.3.9__tar.gz → 16.3.10__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.
Files changed (74) hide show
  1. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/PKG-INFO +1 -1
  2. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/pyproject.toml +1 -1
  3. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/pyproject.toml.orig +1 -1
  4. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/literals.py +19 -0
  5. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/core/peer_relation.py +36 -2
  6. postgresql_charms_single_kernel-16.3.10/single_kernel_postgresql/events/backup.py +172 -0
  7. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/events/postgresql.py +4 -6
  8. postgresql_charms_single_kernel-16.3.10/single_kernel_postgresql/managers/backup.py +1241 -0
  9. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/database.py +4 -4
  10. postgresql_charms_single_kernel-16.3.10/single_kernel_postgresql/managers/k8s.py +123 -0
  11. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/patroni.py +3 -1
  12. postgresql_charms_single_kernel-16.3.10/single_kernel_postgresql/managers/restore.py +685 -0
  13. postgresql_charms_single_kernel-16.3.10/single_kernel_postgresql/scripts/rotate_logs.py +21 -0
  14. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/backup.py +13 -0
  15. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/s3.py +20 -8
  16. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/base.py +100 -2
  17. postgresql_charms_single_kernel-16.3.10/single_kernel_postgresql/workload/k8s.py +671 -0
  18. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/vm.py +101 -1
  19. postgresql_charms_single_kernel-16.3.9/single_kernel_postgresql/managers/backup.py +0 -489
  20. postgresql_charms_single_kernel-16.3.9/single_kernel_postgresql/managers/k8s.py +0 -244
  21. postgresql_charms_single_kernel-16.3.9/single_kernel_postgresql/workload/k8s.py +0 -299
  22. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/LICENSE +0 -0
  23. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/README.md +0 -0
  24. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/__init__.py +0 -0
  25. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/charmcraft.yaml +0 -0
  26. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/charms/__init__.py +0 -0
  27. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/charms/abstract_charm.py +0 -0
  28. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/charms/k8s_charm.py +0 -0
  29. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/charms/vm_charm.py +0 -0
  30. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/compat/__init__.py +0 -0
  31. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/compat/postgresql.py +0 -0
  32. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/compat/pyproject.toml +0 -0
  33. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/__init__.py +0 -0
  34. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/enums.py +0 -0
  35. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/exceptions.py +0 -0
  36. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/locales.py +0 -0
  37. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/pyproject.toml +0 -0
  38. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/config/statuses.py +0 -0
  39. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/core/__init__.py +0 -0
  40. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/core/config.py +0 -0
  41. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/core/relation_state.py +0 -0
  42. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/core/s3.py +0 -0
  43. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/core/state.py +0 -0
  44. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/events/__init__.py +0 -0
  45. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/events/database.py +0 -0
  46. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/events/ldap.py +0 -0
  47. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/events/tls.py +0 -0
  48. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/events/tls_transfer.py +0 -0
  49. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/lib/charms/data_platform_libs/v0/data_interfaces.py +0 -0
  50. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/lib/charms/data_platform_libs/v0/s3.py +0 -0
  51. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/lib/charms/data_platform_libs/v1/data_models.py +0 -0
  52. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/lib/charms/glauth_k8s/v0/ldap.py +0 -0
  53. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/__init__.py +0 -0
  54. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/base.py +0 -0
  55. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/cluster.py +0 -0
  56. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/config.py +0 -0
  57. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/managers/tls.py +0 -0
  58. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/templates/__init__.py +0 -0
  59. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/templates/k8s/pgbackrest.conf.j2 +0 -0
  60. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/templates/k8s/pgbackrest.logrotate.j2 +0 -0
  61. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/templates/patroni.yml.j2 +0 -0
  62. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/templates/vm/pgbackrest.conf.j2 +0 -0
  63. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/templates/vm/pgbackrest.logrotate.j2 +0 -0
  64. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/__init__.py +0 -0
  65. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/arch.py +0 -0
  66. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/filesystem.py +0 -0
  67. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/postgresql.py +0 -0
  68. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/secret.py +0 -0
  69. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/utils/status.py +0 -0
  70. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/__init__.py +0 -0
  71. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/paths/__init__.py +0 -0
  72. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/paths/base.py +0 -0
  73. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/paths/k8s.py +0 -0
  74. {postgresql_charms_single_kernel-16.3.9 → postgresql_charms_single_kernel-16.3.10}/single_kernel_postgresql/workload/paths/vm.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: postgresql-charms-single-kernel
3
- Version: 16.3.9
3
+ Version: 16.3.10
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>
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "postgresql-charms-single-kernel"
3
3
  description = "Shared and reusable code for PostgreSQL-related charms"
4
- version = "16.3.9"
4
+ version = "16.3.10"
5
5
  readme = "README.md"
6
6
  classifiers = [
7
7
  "Development Status :: 3 - Alpha",
@@ -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.9"
7
+ version = "16.3.10"
8
8
  readme = "README.md"
9
9
  license = {file = "LICENSE"}
10
10
  authors = [
@@ -188,3 +188,22 @@ RAFT_PARTNER_PREFIX = "partner_node_status_server_"
188
188
  VM_PATRONI_SERVICE_NAME = f"snap.{POSTGRESQL_SNAP_NAME}.patroni.service"
189
189
  VM_PATRONI_SERVICE_DEFAULT_PATH = f"/etc/systemd/system/{VM_PATRONI_SERVICE_NAME}"
190
190
  VM_PGBACKREST_SERVICE_NAME = "pgbackrest-service"
191
+ VM_ROTATE_LOGS_LOG_FILE = "/var/log/rotate_logs.log"
192
+
193
+ ## K8s restore storage paths (must match the metadata.yaml storage locations)
194
+ K8S_LOGS_STORAGE_PATH = "var/lib/pg/logs"
195
+ K8S_ARCHIVE_PATH = "var/lib/pg/archive"
196
+ K8S_TEMP_STORAGE_PATH = "var/lib/pg/temp"
197
+ K8S_WAL_DIR = "pg_wal"
198
+ K8S_TEMP_TABLESPACE_DIR = "pgsql_tmp"
199
+ K8S_PG_LOGS_PATH = "16/main/pg_logs"
200
+ K8S_PATRONI_LOGS_PATH = "16/main/patroni_logs"
201
+ K8S_DEBIAN_DATA_SYMLINK = "var/lib/postgresql/16"
202
+ K8S_POSTGRESQL_LOGS_SYMLINK_PATH = "var/log/postgresql"
203
+ K8S_PATRONI_LOGS_SYMLINK_PATH = "var/log/patroni"
204
+ K8S_PGBACKREST_LOGS_SYMLINK_PATH = "var/log/pgbackrest"
205
+
206
+ ## Restore
207
+ VM_PATRONICTL_EXECUTABLE = "charmed-postgresql.patronictl"
208
+ PATRONICTL_REMOVE_CONFIRMATION = "Yes I am aware"
209
+ RESTORE_REPEAT_CAUSE = "restore-backup"
@@ -9,7 +9,7 @@ import json
9
9
  from collections.abc import MutableMapping
10
10
  from functools import cached_property
11
11
 
12
- from ops import Application, BlockedStatus, Relation, Unit
12
+ from ops import ActiveStatus, Application, BlockedStatus, Relation, Unit
13
13
 
14
14
  from single_kernel_postgresql.config.enums import Substrates
15
15
  from single_kernel_postgresql.config.literals import (
@@ -68,7 +68,27 @@ class PostgreSQLPeer(RelationState):
68
68
  return self.unit.is_leader()
69
69
 
70
70
  @property
71
- def is_blocked(self) -> bool:
71
+ def is_active_status(self) -> bool:
72
+ """Returns whether the unit is in an active state."""
73
+ return isinstance(self.unit.status, ActiveStatus)
74
+
75
+ @property
76
+ def rotate_logs_pid(self) -> int | None:
77
+ """Get the rotate-logs process PID from the unit peer relation data."""
78
+ if not self.relation:
79
+ return None
80
+ stored = self.relation.data[self.unit].get("rotate-logs-pid")
81
+ return int(stored) if stored else None
82
+
83
+ @rotate_logs_pid.setter
84
+ def rotate_logs_pid(self, value: int | None) -> None:
85
+ """Set or clear the rotate-logs process PID in the unit peer relation data."""
86
+ if not self.relation:
87
+ return
88
+ self.relation.data[self.unit]["rotate-logs-pid"] = str(value) if value else ""
89
+
90
+ @property
91
+ def is_blocked_status(self) -> bool:
72
92
  """Returns whether the unit is in a blocked state."""
73
93
  return isinstance(self.unit.status, BlockedStatus)
74
94
 
@@ -251,6 +271,20 @@ class PostgreSQLPeer(RelationState):
251
271
  return
252
272
  self.relation.data[self.unit]["stanza"] = value
253
273
 
274
+ @property
275
+ def s3_initialization_start(self) -> str | None:
276
+ """Get the s3-initialization-start timestamp from the unit peer relation data."""
277
+ if not self.relation:
278
+ return None
279
+ return self.relation.data[self.unit].get("s3-initialization-start")
280
+
281
+ @s3_initialization_start.setter
282
+ def s3_initialization_start(self, value: str) -> None:
283
+ """Set the s3-initialization-start timestamp in the unit peer relation data."""
284
+ if not self.relation:
285
+ return
286
+ self.relation.data[self.unit]["s3-initialization-start"] = value
287
+
254
288
  @property
255
289
  def s3_initialization_done(self) -> str | None:
256
290
  """Get the s3-initialization-done marker from the unit peer relation data."""
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2026 Canonical Ltd.
3
+ # See LICENSE file for licensing details.
4
+
5
+ """Handler for backup-related charm events.
6
+
7
+ The charm is the composition root: the handler receives the managers and owns
8
+ only the observers and the mapping of manager results/exceptions onto action
9
+ results, unit statuses and defers. No business logic lives here.
10
+ """
11
+
12
+ import logging
13
+ from typing import TYPE_CHECKING
14
+
15
+ from ops import MaintenanceStatus, Object
16
+ from ops.charm import ActionEvent
17
+ from ops.pebble import ExecError
18
+ from tenacity import RetryError
19
+
20
+ from single_kernel_postgresql.config.exceptions import ListBackupsError
21
+ from single_kernel_postgresql.managers.backup import BackupManager
22
+ from single_kernel_postgresql.utils.backup import STANDBY_CLUSTER_LIST_BACKUPS_ERROR_MESSAGE
23
+
24
+ if TYPE_CHECKING:
25
+ from single_kernel_postgresql.charms.abstract_charm import AbstractPostgreSQLCharm
26
+ from single_kernel_postgresql.core.state import CharmState
27
+ from single_kernel_postgresql.managers.restore import RestoreManager
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+
32
+ class BackupEventsHandler(Object):
33
+ """Class implementing backup-related charm events handling."""
34
+
35
+ backup_manager: BackupManager
36
+
37
+ def __init__(
38
+ self,
39
+ charm: "AbstractPostgreSQLCharm",
40
+ state: "CharmState",
41
+ backup_manager: BackupManager,
42
+ restore_manager: "RestoreManager",
43
+ ) -> None:
44
+ super().__init__(charm, key="backup_events")
45
+ self.charm = charm
46
+ self.state = state
47
+ self.backup_manager = backup_manager
48
+ self.restore_manager = restore_manager
49
+
50
+ # s3 relation handles the config options for s3 backups
51
+ self.framework.observe(
52
+ self.state.s3_requirer.on.credentials_changed, self._on_s3_credential_changed
53
+ )
54
+ # When the leader unit is being removed, s3_client.on.credentials_gone is performed
55
+ # on it (and only on it). After a new leader is elected, the S3 connection must be
56
+ # reinitialized.
57
+ self.framework.observe(self.charm.on.leader_elected, self._on_s3_credential_changed)
58
+ self.framework.observe(
59
+ self.state.s3_requirer.on.credentials_gone, self._on_s3_credential_gone
60
+ )
61
+ self.framework.observe(self.charm.on.create_backup_action, self._on_create_backup_action)
62
+ self.framework.observe(self.charm.on.list_backups_action, self._on_list_backups_action)
63
+ self.framework.observe(self.charm.on.restore_action, self._on_restore_action)
64
+
65
+ def _on_s3_credential_changed(self, event) -> None:
66
+ """Call the stanza initialization when the credentials or the connection info change."""
67
+ proceed, defer = self.backup_manager.credential_changed_checks()
68
+ if not proceed:
69
+ # The charms defer on every branch that deferred there: credentials
70
+ # arriving too early, mid-PITR, mid-restore, or on a unit that
71
+ # cannot initialise yet must be retried later, not dropped.
72
+ if defer:
73
+ event.defer()
74
+ return
75
+
76
+ # Start the pgBackRest service for the check_stanza to be successful. It's
77
+ # required to run on all the units if the tls is enabled.
78
+ try:
79
+ self.backup_manager.start_stop_pgbackrest_service()
80
+ except RetryError:
81
+ logger.debug("Cannot initialise service yet.")
82
+ event.defer()
83
+ return
84
+
85
+ self.backup_manager.reset_s3_initialization_markers()
86
+
87
+ if self.backup_manager.is_primary and self.state.peer.s3_initialization_done is None:
88
+ self.backup_manager.initialise_s3_repository()
89
+
90
+ def _on_s3_credential_gone(self, _) -> None:
91
+ """Clear the stanza and the S3 initialization markers when credentials are gone."""
92
+ self.backup_manager.clear_s3_state()
93
+
94
+ def _on_create_backup_action(self, event: ActionEvent) -> None:
95
+ """Request that pgBackRest creates a backup."""
96
+ backup_type = event.params.get("type", "full")
97
+ # The manager owns the creating-backup status bracket: the charms set
98
+ # Maintenance only after validation and the metadata upload, and reset
99
+ # Active only once the run started — never on a gate failure.
100
+ ok, message = self.backup_manager.create_backup(backup_type)
101
+ if not ok:
102
+ logger.error(f"Backup failed: {message}")
103
+ event.fail(message)
104
+ else:
105
+ event.set_results({"backup-status": "backup created"})
106
+
107
+ def _on_list_backups_action(self, event: ActionEvent) -> None:
108
+ """List the previously created backups."""
109
+ if self.backup_manager.is_standby_cluster:
110
+ logger.warning(STANDBY_CLUSTER_LIST_BACKUPS_ERROR_MESSAGE)
111
+ event.fail(STANDBY_CLUSTER_LIST_BACKUPS_ERROR_MESSAGE)
112
+ return
113
+
114
+ are_backup_settings_ok, validation_message = self.backup_manager.are_backup_settings_ok()
115
+ if not are_backup_settings_ok:
116
+ logger.warning(validation_message)
117
+ event.fail(validation_message)
118
+ return
119
+
120
+ try:
121
+ formatted_list = self.backup_manager.generate_backup_list_output()
122
+ event.set_results({"backups": formatted_list})
123
+ except (ListBackupsError, ExecError) as e:
124
+ # The K8s charm catches ExecError here (pgbackrest failures raise
125
+ # instead of returning a code); the message matches both charms.
126
+ logger.exception(e)
127
+ event.fail(f"Failed to list PostgreSQL backups with error: {e!s}")
128
+
129
+ def _on_restore_action(self, event: ActionEvent) -> None:
130
+ """Request that pgBackRest restores a backup."""
131
+ backup_id = event.params.get("backup-id")
132
+ restore_to_time = event.params.get("restore-to-time")
133
+ logger.info(
134
+ f"A restore with backup-id {backup_id}"
135
+ f"{f' to time point {restore_to_time}' if restore_to_time else ''}"
136
+ f" has been requested on the unit"
137
+ )
138
+
139
+ can_restore, error_message = self.restore_manager.pre_restore_checks(
140
+ backup_id, restore_to_time
141
+ )
142
+ if not can_restore:
143
+ event.fail(error_message)
144
+ return
145
+
146
+ # Validate the provided backup id and restore to time.
147
+ logger.info("Validating provided backup-id and restore-to-time")
148
+ try:
149
+ restore_stanza_timeline, is_backup_id_real, error_message = (
150
+ self.restore_manager.resolve_restore_target(backup_id, restore_to_time)
151
+ )
152
+ except ListBackupsError as e:
153
+ logger.exception(e)
154
+ error_message = "Failed to retrieve backups list"
155
+ logger.error(f"Restore failed: {error_message}")
156
+ event.fail(error_message)
157
+ return
158
+ if restore_stanza_timeline is None:
159
+ logger.error(f"Restore failed: {error_message}")
160
+ event.fail(error_message)
161
+ return
162
+
163
+ self.charm.set_unit_status(MaintenanceStatus("restoring backup"))
164
+ ok, message = self.restore_manager.restore(
165
+ restore_stanza_timeline, backup_id, restore_to_time, is_backup_id_real
166
+ )
167
+ if not ok:
168
+ logger.error(f"Restore failed: {message}")
169
+ event.fail(message)
170
+ return
171
+
172
+ event.set_results({"restore-status": "restore started"})
@@ -6,7 +6,7 @@
6
6
 
7
7
  import logging
8
8
  from datetime import datetime
9
- from typing import TYPE_CHECKING, cast
9
+ from typing import TYPE_CHECKING
10
10
 
11
11
  from ops import (
12
12
  InstallEvent,
@@ -30,12 +30,11 @@ from single_kernel_postgresql.managers.cluster import ClusterManager
30
30
  from single_kernel_postgresql.managers.config import ConfigManager
31
31
  from single_kernel_postgresql.managers.patroni import PatroniManager
32
32
  from single_kernel_postgresql.managers.tls import TLSManager
33
- from single_kernel_postgresql.workload.base import BaseWorkload
33
+ from single_kernel_postgresql.workload.base import BaseWorkload, PebbleLayerSpec
34
34
  from single_kernel_postgresql.workload.vm import VMWorkload
35
35
 
36
36
  if TYPE_CHECKING:
37
37
  from single_kernel_postgresql.charms.abstract_charm import AbstractPostgreSQLCharm
38
- from single_kernel_postgresql.charms.k8s_charm import PostgreSQLK8sCharm
39
38
 
40
39
  logger = logging.getLogger(__name__)
41
40
 
@@ -124,7 +123,6 @@ class PostgreSQLEventsHandler(Object):
124
123
 
125
124
  def _on_postgresql_pebble_ready(self, event: WorkloadEvent) -> None:
126
125
  """Event handler for PostgreSQL container on PebbleReadyEvent."""
127
- charm = cast("PostgreSQLK8sCharm", self.charm)
128
126
  # TODO: Safeguard against refresh
129
127
  if self.state.endpoint in self.state.endpoints:
130
128
  # TODO: Fix pod by adding services
@@ -152,7 +150,7 @@ class PostgreSQLEventsHandler(Object):
152
150
  self.tls_manager.configure_internal_peer_cert()
153
151
 
154
152
  # Start the database service
155
- charm.k8s_manager.update_pebble_layers()
153
+ self.workload.update_pebble_layers(PebbleLayerSpec.from_state(self.state))
156
154
 
157
155
  # Assert the member is up and running before marking it as initialised.
158
156
  if not self.patroni_manager.member_started:
@@ -192,7 +190,7 @@ class PostgreSQLEventsHandler(Object):
192
190
 
193
191
  # Doesn't try to bootstrap the cluster if it's in a blocked state
194
192
  # caused, for example, because a failed installation of packages.
195
- if self.state.peer.is_blocked:
193
+ if self.state.peer.is_blocked_status:
196
194
  logger.debug("Early exit on_start: Unit blocked")
197
195
  return False
198
196