mongo-charms-single-kernel 1.8.8__py3-none-any.whl → 1.8.9__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.
- {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.9.dist-info}/METADATA +1 -1
- {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.9.dist-info}/RECORD +27 -27
- single_kernel_mongo/config/literals.py +7 -0
- single_kernel_mongo/config/relations.py +2 -1
- single_kernel_mongo/config/statuses.py +127 -20
- single_kernel_mongo/core/operator.py +7 -0
- single_kernel_mongo/core/structured_config.py +2 -0
- single_kernel_mongo/core/workload.py +10 -4
- single_kernel_mongo/events/cluster.py +5 -0
- single_kernel_mongo/events/sharding.py +3 -1
- single_kernel_mongo/events/tls.py +183 -157
- single_kernel_mongo/exceptions.py +0 -8
- single_kernel_mongo/lib/charms/tls_certificates_interface/v4/tls_certificates.py +1995 -0
- single_kernel_mongo/managers/cluster.py +70 -28
- single_kernel_mongo/managers/config.py +14 -8
- single_kernel_mongo/managers/mongo.py +1 -1
- single_kernel_mongo/managers/mongodb_operator.py +44 -22
- single_kernel_mongo/managers/mongos_operator.py +16 -20
- single_kernel_mongo/managers/sharding.py +154 -127
- single_kernel_mongo/managers/tls.py +223 -206
- single_kernel_mongo/state/charm_state.py +39 -16
- single_kernel_mongo/state/cluster_state.py +8 -0
- single_kernel_mongo/state/config_server_state.py +9 -0
- single_kernel_mongo/state/tls_state.py +39 -12
- single_kernel_mongo/utils/helpers.py +4 -19
- single_kernel_mongo/lib/charms/tls_certificates_interface/v3/tls_certificates.py +0 -2123
- {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.9.dist-info}/WHEEL +0 -0
- {mongo_charms_single_kernel-1.8.8.dist-info → mongo_charms_single_kernel-1.8.9.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
|
|
13
|
-
from ops.
|
|
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
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
51
|
-
self.charm
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
self.
|
|
55
|
-
self.
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
self.manager.state.
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
"""
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
TLSStatuses.
|
|
142
|
-
|
|
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.
|
|
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
|
|
164
|
+
This event is emitted by the TLS charm when a certificates is available.
|
|
150
165
|
"""
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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
|
|