mongo-charms-single-kernel 1.8.8__py3-none-any.whl → 1.8.10__py3-none-any.whl

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 mongo-charms-single-kernel might be problematic. Click here for more details.

Files changed (31) hide show
  1. {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.10.dist-info}/METADATA +1 -1
  2. {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.10.dist-info}/RECORD +30 -30
  3. single_kernel_mongo/config/literals.py +12 -5
  4. single_kernel_mongo/config/relations.py +2 -1
  5. single_kernel_mongo/config/statuses.py +127 -20
  6. single_kernel_mongo/core/operator.py +7 -0
  7. single_kernel_mongo/core/structured_config.py +2 -0
  8. single_kernel_mongo/core/workload.py +10 -4
  9. single_kernel_mongo/events/cluster.py +5 -0
  10. single_kernel_mongo/events/sharding.py +3 -1
  11. single_kernel_mongo/events/tls.py +183 -157
  12. single_kernel_mongo/exceptions.py +0 -8
  13. single_kernel_mongo/lib/charms/tls_certificates_interface/v4/tls_certificates.py +1995 -0
  14. single_kernel_mongo/managers/cluster.py +70 -28
  15. single_kernel_mongo/managers/config.py +24 -14
  16. single_kernel_mongo/managers/mongo.py +12 -12
  17. single_kernel_mongo/managers/mongodb_operator.py +58 -34
  18. single_kernel_mongo/managers/mongos_operator.py +16 -20
  19. single_kernel_mongo/managers/sharding.py +172 -136
  20. single_kernel_mongo/managers/tls.py +223 -206
  21. single_kernel_mongo/managers/upgrade_v3.py +6 -6
  22. single_kernel_mongo/state/charm_state.py +54 -31
  23. single_kernel_mongo/state/cluster_state.py +8 -0
  24. single_kernel_mongo/state/config_server_state.py +15 -6
  25. single_kernel_mongo/state/models.py +2 -2
  26. single_kernel_mongo/state/tls_state.py +39 -12
  27. single_kernel_mongo/utils/helpers.py +4 -19
  28. single_kernel_mongo/utils/mongodb_users.py +20 -20
  29. single_kernel_mongo/lib/charms/tls_certificates_interface/v3/tls_certificates.py +0 -2123
  30. {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.10.dist-info}/WHEEL +0 -0
  31. {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.10.dist-info}/licenses/LICENSE +0 -0
@@ -9,208 +9,234 @@ from __future__ import annotations
9
9
  import logging
10
10
  from typing import TYPE_CHECKING
11
11
 
12
- from ops.charm import ActionEvent, RelationBrokenEvent, RelationJoinedEvent
13
- from ops.framework import Object
12
+ from ops import ConfigChangedEvent
13
+ from ops.charm import RelationBrokenEvent, RelationCreatedEvent
14
+ from ops.framework import EventBase, EventSource, Object
14
15
 
16
+ from single_kernel_mongo.config.literals import CharmKind, TLSType
15
17
  from single_kernel_mongo.config.relations import ExternalRequirerRelations
16
- from single_kernel_mongo.config.statuses import MongosStatuses, TLSStatuses
17
- from single_kernel_mongo.core.operator import OperatorProtocol
18
- from single_kernel_mongo.core.structured_config import MongoDBRoles
19
- from single_kernel_mongo.exceptions import (
20
- UnknownCertificateAvailableError,
21
- UnknownCertificateExpiringError,
18
+ from single_kernel_mongo.config.statuses import (
19
+ MongosStatuses,
20
+ ShardStatuses,
21
+ TLSStatuses,
22
22
  )
23
- from single_kernel_mongo.lib.charms.tls_certificates_interface.v3.tls_certificates import (
23
+ from single_kernel_mongo.core.structured_config import MongoDBRoles
24
+ from single_kernel_mongo.exceptions import DeferrableFailedHookChecksError
25
+ from single_kernel_mongo.lib.charms.tls_certificates_interface.v4.tls_certificates import (
24
26
  CertificateAvailableEvent,
25
- CertificateExpiringEvent,
26
- TLSCertificatesRequiresV3,
27
- )
28
- from single_kernel_mongo.utils.event_helpers import (
29
- fail_action_with_error_log,
27
+ TLSCertificatesRequiresV4,
30
28
  )
29
+ from single_kernel_mongo.state.tls_state import TlsManagementState
30
+ from single_kernel_mongo.utils.event_helpers import defer_event_with_info_log
31
31
 
32
32
  if TYPE_CHECKING:
33
33
  from single_kernel_mongo.abstract_charm import AbstractMongoCharm
34
-
34
+ from single_kernel_mongo.core.operator import OperatorProtocol
35
35
 
36
36
  logger = logging.getLogger(__name__)
37
37
 
38
38
 
39
+ class RefreshTLSCertificatesEvent(EventBase):
40
+ """Event for refreshing TLS certificates."""
41
+
42
+
39
43
  class TLSEventsHandler(Object):
40
44
  """Event Handler for managing TLS events."""
41
45
 
46
+ refresh_tls_certificates_event = EventSource(RefreshTLSCertificatesEvent)
47
+
42
48
  def __init__(self, dependent: OperatorProtocol):
43
49
  super().__init__(parent=dependent, key="tls")
44
50
  self.dependent = dependent
45
51
  self.manager = self.dependent.tls_manager
46
52
  self.charm: AbstractMongoCharm = dependent.charm
47
- self.relation_name = ExternalRequirerRelations.TLS.value
48
- self.certs_client = TLSCertificatesRequiresV3(self.charm, self.relation_name)
49
53
 
50
- self.framework.observe(
51
- self.charm.on.set_tls_private_key_action, self._on_set_tls_private_key
52
- )
53
- self.framework.observe(
54
- self.charm.on[self.relation_name].relation_joined,
55
- self._on_tls_relation_joined,
56
- )
57
- self.framework.observe(
58
- self.charm.on[self.relation_name].relation_broken,
59
- self._on_tls_relation_broken,
60
- )
61
- self.framework.observe(
62
- self.certs_client.on.certificate_available, self._on_certificate_available
63
- )
64
- self.framework.observe(
65
- self.certs_client.on.certificate_expiring, self._on_certificate_expiring
54
+ self.peer_certificate = TLSCertificatesRequiresV4(
55
+ charm=self.charm,
56
+ relationship_name=ExternalRequirerRelations.PEER_TLS.value,
57
+ certificate_requests=[self.manager.get_certificate_request_attributes()],
58
+ private_key=self.manager.state.tls.peer_private_key,
59
+ refresh_events=[self.refresh_tls_certificates_event],
66
60
  )
67
61
 
68
- def _on_set_tls_private_key(self, event: ActionEvent) -> None:
69
- """Set the TLS private key which will be used for requesting the certificates."""
70
- logger.debug("Request to set TLS private key received.")
71
- if (
72
- self.manager.state.is_role(MongoDBRoles.MONGOS)
73
- and self.manager.state.config_server_name is None
74
- ):
75
- logger.info(
76
- "mongos is not running (not integrated to config-server) deferring renewal of certificates."
62
+ self.client_certificate = TLSCertificatesRequiresV4(
63
+ charm=self.charm,
64
+ relationship_name=ExternalRequirerRelations.CLIENT_TLS.value,
65
+ certificate_requests=[self.manager.get_certificate_request_attributes()],
66
+ private_key=self.manager.state.tls.client_private_key,
67
+ refresh_events=[self.refresh_tls_certificates_event],
68
+ )
69
+ for cert_requires in [self.peer_certificate, self.client_certificate]:
70
+ self.framework.observe(
71
+ cert_requires.on.certificate_available, self._on_certificate_available
77
72
  )
78
- event.fail("Mongos cannot set TLS keys until integrated to config-server.")
79
- return
80
- if self.dependent.refresh_in_progress:
81
- fail_action_with_error_log(
82
- logger,
83
- event,
84
- "set-tls-private-key",
85
- "Setting TLS keys during an upgrade is not supported.",
73
+
74
+ for relation_name in [
75
+ ExternalRequirerRelations.PEER_TLS.value,
76
+ ExternalRequirerRelations.CLIENT_TLS.value,
77
+ ]:
78
+ self.framework.observe(
79
+ self.charm.on[relation_name].relation_created,
80
+ self._on_tls_relation_created,
86
81
  )
87
- return
88
- try:
89
- for internal in (True, False):
90
- param = "internal-key" if internal else "external-key"
91
- key = event.params.get(param, None)
92
- csr = self.manager.generate_certificate_request(key, internal=internal)
93
- self.certs_client.request_certificate_creation(certificate_signing_request=csr)
94
- self.manager.set_waiting_for_cert_to_update(internal=internal, waiting=True)
95
- except ValueError as e:
96
- event.fail(str(e))
97
-
98
- def _on_tls_relation_joined(self, event: RelationJoinedEvent) -> None:
99
- """Handler for relation joined."""
100
- if (
101
- self.manager.state.is_role(MongoDBRoles.MONGOS)
102
- and self.manager.state.config_server_name is None
103
- ):
104
- logger.info(
105
- "mongos is not running (not integrated to config-server) deferring renewal of certificates."
82
+ self.framework.observe(
83
+ self.charm.on[relation_name].relation_broken,
84
+ self._on_tls_relation_broken,
106
85
  )
107
- event.defer()
108
- return
109
86
 
110
- if self.dependent.refresh_in_progress:
111
- logger.warning(
112
- "Enabling TLS is not supported during an upgrade. The charm may be in a broken, unrecoverable state."
113
- )
114
- event.defer()
115
- return
87
+ self.framework.observe(self.charm.on.config_changed, self._on_config_changed)
88
+ self.framework.observe(self.charm.on.secret_changed, self._on_secret_changed)
89
+
90
+ @property
91
+ def tls_mapping(self) -> dict[bool, TLSCertificatesRequiresV4]:
92
+ """Mapping of boolean to a TLS requirer instance.
93
+
94
+ The boolean value is True if the requirer is for internal certificates,
95
+ and False for the client certificates.
96
+ """
97
+ return {True: self.peer_certificate, False: self.client_certificate}
116
98
 
117
- # When we can integrate, clean the mongos requires tls status.
99
+ def _on_tls_relation_created(self, event: RelationCreatedEvent) -> None:
100
+ """Handler for relation created."""
118
101
  if self.manager.state.is_role(MongoDBRoles.MONGOS):
119
102
  self.manager.state.statuses.delete(
120
- MongosStatuses.MISSING_TLS_REL.value, scope="unit", component=self.dependent.name
103
+ MongosStatuses.MISSING_PEER_TLS_REL.value,
104
+ scope="unit",
105
+ component=self.dependent.name,
106
+ )
107
+ self.manager.state.statuses.delete(
108
+ MongosStatuses.MISSING_CLIENT_TLS_REL.value,
109
+ scope="unit",
110
+ component=self.dependent.name,
121
111
  )
122
112
 
123
- for internal in (True, False):
124
- csr = self.manager.generate_certificate_request(None, internal=internal)
125
- self.certs_client.request_certificate_creation(certificate_signing_request=csr)
126
- self.manager.set_waiting_for_cert_to_update(internal=internal, waiting=True)
113
+ if self.manager.state.is_role(MongoDBRoles.SHARD):
114
+ self.manager.state.statuses.delete(
115
+ ShardStatuses.MISSING_PEER_TLS_REL.value,
116
+ scope="unit",
117
+ component=self.dependent.name,
118
+ )
119
+ self.manager.state.statuses.delete(
120
+ ShardStatuses.MISSING_CLIENT_TLS_REL.value,
121
+ scope="unit",
122
+ component=self.dependent.name,
123
+ )
124
+
125
+ def refresh_certificates(self) -> None:
126
+ """Trigger refresh TLS certificates event."""
127
+ logger.info(f"Requesting refresh certificates for unit: {self.charm.unit.name}.")
128
+ self.refresh_tls_certificates_event.emit()
127
129
 
128
130
  def _on_tls_relation_broken(self, event: RelationBrokenEvent) -> None:
129
- """Handler for relation joined."""
130
- if not self.manager.state.db_initialised:
131
- logger.info(f"Deferring {str(type(event))}. db is not initialised.")
132
- event.defer()
133
- return
131
+ """Handle the relation broken event."""
132
+ state = self.manager.get_tls_management_state()
133
+ match state:
134
+ case TlsManagementState.UPGRADE_IN_PROGRESS:
135
+ defer_event_with_info_log(logger, event, str(type(event)), state.value)
136
+ return
137
+ case (
138
+ TlsManagementState.DB_NOT_INTIALIZED
139
+ | TlsManagementState.MONGOS_DB_NOT_INITIALIZED
140
+ ):
141
+ logger.info("DB never initialised, removing the TLS relation.")
142
+ return
143
+ case _:
144
+ pass
134
145
 
135
- if self.dependent.refresh_in_progress:
136
- logger.warning(
137
- "Disabling TLS is not supported during an upgrade. The charm may be in a broken, unrecoverable state."
138
- )
139
- logger.debug("Disabling external and internal TLS for unit: %s", self.charm.unit.name)
140
- self.charm.status_handler.set_running_status(
141
- TLSStatuses.DISABLING_TLS.value,
142
- scope="unit",
146
+ internal = event.relation.name == ExternalRequirerRelations.PEER_TLS.value
147
+ logger.debug(
148
+ f"Disabling {TLSType.PEER.value if internal else TLSType.CLIENT.value} TLS for unit: {self.charm.unit.name}"
149
+ )
150
+
151
+ status = (
152
+ TLSStatuses.DISABLING_PEER_TLS.value
153
+ if internal
154
+ else TLSStatuses.DISABLING_CLIENT_TLS.value
143
155
  )
144
- self.manager.disable_certificates_for_unit()
156
+ self.charm.status_handler.set_running_status(status, scope="unit")
157
+ self.manager.disable_certificates_for_unit(internal)
158
+ # Recomputes the statuses for those components as the tls changes are impactful
159
+ self._recompute_statuses()
145
160
 
146
161
  def _on_certificate_available(self, event: CertificateAvailableEvent) -> None:
147
162
  """Handler for the certificate available event.
148
163
 
149
- This event is emitted by the TLS charm when the some certificates are available.
164
+ This event is emitted by the TLS charm when a certificates is available.
150
165
  """
151
- if (
152
- self.manager.state.is_role(MongoDBRoles.MONGOS)
153
- and self.manager.state.config_server_name is None
154
- ):
155
- logger.info(
156
- "mongos is not running (not integrated to config-server) deferring renewal of certificates."
157
- )
158
- event.defer()
159
- return
160
- if not self.manager.state.db_initialised and not self.dependent.state.is_role(
161
- MongoDBRoles.MONGOS
162
- ):
163
- logger.info(f"Deferring {str(type(event))}: db is not initialised")
164
- event.defer()
165
- return
166
- # Check if refresh is in progress and this is the initial integration, delay.
167
- # Otherwise it's a rotation and we're safe to continue.
168
- if self.dependent.refresh_in_progress and self.manager.initial_integration():
169
- logger.warning(
170
- "Enabling TLS is not supported during an upgrade. The charm may be in a broken, unrecoverable state."
171
- )
172
- event.defer()
173
- return
174
- try:
175
- self.manager.set_certificates(
176
- event.certificate_signing_request,
177
- event.chain,
178
- event.certificate,
179
- event.ca,
180
- )
181
- self.dependent.state.update_ca_secrets(event.ca)
182
-
183
- # If we don't have both certificates, we early return, the next
184
- # certificate available event will enable certificates for this
185
- # unit.
186
- if self.manager.is_waiting_for_both_certs():
187
- logger.info(
188
- "Waiting for both internal and external TLS certificates available to avoid second restart."
189
- )
190
- event.defer()
166
+ state = self.manager.get_tls_management_state()
167
+ match state:
168
+ case TlsManagementState.DB_NOT_INTIALIZED | TlsManagementState.UPGRADE_IN_PROGRESS:
169
+ defer_event_with_info_log(logger, event, str(type(event)), state.value)
191
170
  return
171
+ case TlsManagementState.MONGOS_MISSING_CONFIG_SERVER:
172
+ logger.info(f"{state.value} Ignoring certificate.")
173
+ return
174
+ case _:
175
+ pass
176
+
177
+ logger.info("Certificate available.")
192
178
 
193
- self.manager.enable_certificates_for_unit()
194
- except UnknownCertificateAvailableError:
195
- logger.error("An unknown certificate is available -- ignoring.")
179
+ cert = event.certificate
180
+ client_certificates, client_private_key = (
181
+ self.client_certificate.get_assigned_certificates()
182
+ )
183
+ peer_certificates, peer_private_key = self.peer_certificate.get_assigned_certificates()
184
+
185
+ if client_certificates and client_certificates[0].certificate == cert:
186
+ internal = False
187
+ provider_cert = client_certificates[0]
188
+ private_key = client_private_key.raw if client_private_key else None
189
+ elif peer_certificates and peer_certificates[0].certificate == cert:
190
+ internal = True
191
+ provider_cert = peer_certificates[0]
192
+ private_key = peer_private_key.raw if peer_private_key else None
193
+ else:
194
+ logger.error("Received certificate does not match any assigned certificates.")
196
195
  return
197
196
 
198
- def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None:
199
- """Handle certificate expiring events."""
200
- if (
201
- self.manager.state.is_role(MongoDBRoles.MONGOS)
202
- and not self.manager.state.config_server_name is not None
203
- ):
204
- logger.info(
205
- "mongos is not running (not integrated to config-server) deferring renewal of certificates."
206
- )
207
- event.defer()
197
+ logger.debug(
198
+ f"Received {TLSType.PEER.value if internal else TLSType.CLIENT.value} certificate."
199
+ )
200
+
201
+ self.manager.set_certificates(
202
+ secret_chain=[c.raw for c in provider_cert.chain],
203
+ certificate=provider_cert.certificate.raw,
204
+ csr=provider_cert.certificate_signing_request.raw,
205
+ ca=provider_cert.ca.raw,
206
+ private_key=private_key,
207
+ internal=internal,
208
+ )
209
+ if internal:
210
+ self.dependent.state.update_peer_ca_secrets(provider_cert.ca.raw)
211
+ else:
212
+ self.dependent.state.update_client_ca_secrets(provider_cert.ca.raw)
213
+
214
+ self.manager.enable_certificates_for_unit(internal)
215
+ self._recompute_statuses()
216
+
217
+ def _on_config_changed(self, event: ConfigChangedEvent) -> None:
218
+ """On Config Changed, validate private keys and refresh certs if needed."""
219
+ try:
220
+ self.manager.update_private_keys()
221
+ except DeferrableFailedHookChecksError as e:
222
+ defer_event_with_info_log(logger, event, "set-private-key", f"{e}")
208
223
  return
224
+
225
+ def _on_secret_changed(self, event: ConfigChangedEvent) -> None:
226
+ """On Secret Changed, validate private keys and refresh certs if needed."""
209
227
  try:
210
- old_csr, new_csr = self.manager.renew_expiring_certificate(event.certificate)
211
- self.certs_client.request_certificate_renewal(
212
- old_certificate_signing_request=old_csr,
213
- new_certificate_signing_request=new_csr,
228
+ self.manager.update_private_keys()
229
+ except DeferrableFailedHookChecksError as e:
230
+ defer_event_with_info_log(logger, event, "set-private-key", f"{e}")
231
+ return
232
+
233
+ def _recompute_statuses(self):
234
+ """Recomputes the statuses for those components as the tls changes are impactful."""
235
+ if self.dependent.name == CharmKind.MONGOD:
236
+ self.charm.status_handler._recompute_statuses_for_scope(
237
+ "unit", self.dependent.shard_manager
214
238
  )
215
- except UnknownCertificateExpiringError:
216
- logger.debug("An unknown certificate is expiring.")
239
+ else:
240
+ self.charm.status_handler._recompute_statuses_for_scope("unit", self.dependent)
241
+ if self.charm.unit.is_leader():
242
+ self.charm.status_handler._recompute_statuses_for_scope("app", self.dependent)
@@ -134,14 +134,6 @@ class InvalidArgumentForActionError(Exception):
134
134
  """Raised when arguments for an action are invalid."""
135
135
 
136
136
 
137
- class UnknownCertificateExpiringError(Exception):
138
- """Raised when an unknown certificate is expiring."""
139
-
140
-
141
- class UnknownCertificateAvailableError(Exception):
142
- """Raised when an unknown certificate is available."""
143
-
144
-
145
137
  class DatabaseRequestedHasNotRunYetError(Exception):
146
138
  """Raised when the database event has not run yet."""
147
139