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
@@ -105,6 +105,9 @@ class ClusterProvider(Object):
105
105
  if int_tls_ca := self.state.tls.get_secret(label_name=SECRET_CA_LABEL, internal=True):
106
106
  relation_data[ClusterStateKeys.INT_CA_SECRET.value] = int_tls_ca
107
107
 
108
+ if ext_tls_ca := self.state.tls.get_secret(label_name=SECRET_CA_LABEL, internal=False):
109
+ relation_data[ClusterStateKeys.EXT_CA_SECRET.value] = ext_tls_ca
110
+
108
111
  if hashed_data := self.dependent.ldap_manager.get_hash():
109
112
  relation_data[ClusterStateKeys.LDAP_HASH.value] = hashed_data
110
113
 
@@ -253,19 +256,19 @@ class ClusterRequirer(Object):
253
256
 
254
257
  def assert_pass_hook_checks(self) -> None:
255
258
  """Runs pre-hook checks, raises if one fails."""
256
- mongos_has_tls, config_server_has_tls = self.tls_status()
257
- match (mongos_has_tls, config_server_has_tls):
259
+ mongos_peer_tls, config_server_peer_tls = self.mongos_and_config_server_peer_tls_status()
260
+ match (mongos_peer_tls, config_server_peer_tls):
258
261
  case False, True:
259
262
  raise DeferrableFailedHookChecksError(
260
- "Config-Server uses TLS but mongos does not. Please synchronise encryption method."
263
+ "Config-Server uses peer TLS but mongos does not. Please synchronise encryption method."
261
264
  )
262
265
  case True, False:
263
266
  raise DeferrableFailedHookChecksError(
264
- "Mongos uses TLS but config-server does not. Please synchronise encryption method."
267
+ "Mongos uses peer TLS but config-server does not. Please synchronise encryption method."
265
268
  )
266
269
  case _:
267
270
  pass
268
- if self.is_waiting_to_request_certs():
271
+ if self.dependent.tls_manager.is_waiting_for_a_cert():
269
272
  raise DeferrableFailedHookChecksError(
270
273
  "Mongos was waiting for config-server to enable TLS. Wait for TLS to be enabled until starting mongos."
271
274
  )
@@ -431,10 +434,10 @@ class ClusterRequirer(Object):
431
434
  with MongoConnection(self.state.mongo_config) as mongo:
432
435
  mongo.drop_user(mongo.config.username)
433
436
 
434
- def is_ca_compatible(self) -> bool:
435
- """Returns true if both the mongos and the config-server use the same CA.
437
+ def is_peer_ca_compatible(self) -> bool:
438
+ """Returns true if both the mongos and the config-server use the same peer CA.
436
439
 
437
- Using the same CA is a requirement for sharded clusters.
440
+ Using the same peer CA is a requirement for sharded clusters.
438
441
  """
439
442
  if not self.state.mongos_cluster_relation:
440
443
  return True
@@ -445,38 +448,77 @@ class ClusterRequirer(Object):
445
448
 
446
449
  return config_server_tls_ca == mongos_tls_ca
447
450
 
448
- def is_waiting_to_request_certs(self) -> bool:
449
- """Returns True if mongos has been waiting for config server in order to request certs."""
450
- if not self.state.tls_relation:
451
- return False
452
- mongos_tls_ca = self.state.tls.get_secret(internal=True, label_name=SECRET_CA_LABEL)
451
+ def is_client_ca_compatible(self) -> bool:
452
+ """Returns true if both the mongos and the config-server use the same client CA.
453
453
 
454
- # our CA is none until certs have been requested. We cannot request certs until integrated
455
- # to config-server.
456
- return not mongos_tls_ca
454
+ Using the same client CA is a requirement for sharded clusters.
455
+ """
456
+ if not self.state.mongos_cluster_relation:
457
+ return True
458
+ config_server_tls_ca = self.state.cluster.external_ca_secret
459
+ mongos_tls_ca = self.state.tls.get_secret(internal=False, label_name=SECRET_CA_LABEL)
460
+ if not config_server_tls_ca or not mongos_tls_ca:
461
+ return True
457
462
 
458
- def tls_status(self) -> tuple[bool, bool]:
459
- """Returns the TLS integration status for mongos and config-server."""
463
+ return config_server_tls_ca == mongos_tls_ca
464
+
465
+ def mongos_and_config_server_peer_tls_status(self) -> tuple[bool, bool]:
466
+ """Returns the peer TLS integration status for mongos and config-server."""
460
467
  if self.state.mongos_cluster_relation:
461
- mongos_has_tls = self.state.tls_relation is not None
468
+ mongos_has_tls = self.state.peer_tls_relation is not None
462
469
  config_server_has_tls = self.state.cluster.internal_ca_secret is not None
463
470
  return mongos_has_tls, config_server_has_tls
464
471
 
465
472
  return False, False
466
473
 
467
- def get_tls_statuses(self) -> StatusObject | None:
468
- """Return statuses relevant to TLS."""
469
- mongos_has_tls, config_server_has_tls = self.tls_status()
470
- match (mongos_has_tls, config_server_has_tls):
474
+ def mongos_and_config_server_client_tls_status(self) -> tuple[bool, bool]:
475
+ """Returns the client TLS integration status for mongos and config-server."""
476
+ if self.state.mongos_cluster_relation:
477
+ mongos_has_tls = self.state.client_tls_relation is not None
478
+ config_server_has_tls = self.state.cluster.external_ca_secret is not None
479
+ return mongos_has_tls, config_server_has_tls
480
+
481
+ return False, False
482
+
483
+ def get_tls_status(self, internal: bool):
484
+ """Computes the TLS status for the scope.
485
+
486
+ Args:
487
+ internal: (bool) if true, represents the internal TLS, otherwise external TLS.
488
+ """
489
+ if internal:
490
+ shard_tls, config_server_tls = self.mongos_and_config_server_peer_tls_status()
491
+ is_ca_compatible = self.is_peer_ca_compatible()
492
+ else:
493
+ shard_tls, config_server_tls = self.mongos_and_config_server_client_tls_status()
494
+ is_ca_compatible = self.is_client_ca_compatible()
495
+
496
+ match (shard_tls, config_server_tls):
471
497
  case False, True:
472
- return MongosStatuses.MISSING_TLS_REL.value
498
+ logger.warning(
499
+ "Config-Server uses peer TLS but mongos does not. Please synchronise encryption method."
500
+ )
501
+ return MongosStatuses.missing_tls(internal=internal)
473
502
  case True, False:
474
- return MongosStatuses.INVALID_TLS_REL.value
503
+ logger.warning(
504
+ "Mongos uses peer TLS but config-server does not. Please synchronise encryption method."
505
+ )
506
+ return MongosStatuses.invalid_tls(internal=internal)
475
507
  case _:
476
508
  pass
477
- if not self.is_ca_compatible():
509
+
510
+ if not is_ca_compatible:
478
511
  logger.error(
479
- "mongos is integrated to a different CA than the config server. Please use the same CA for all cluster components."
512
+ "Mongos is integrated to a different CA than the config server. Please use the same CA for all cluster components."
480
513
  )
481
- return MongosStatuses.CA_MISMATCH.value
514
+ return MongosStatuses.incompatible_ca(internal=internal)
515
+
482
516
  return None
517
+
518
+ def tls_statuses(self) -> list[StatusObject]:
519
+ """Return statuses relevant to TLS."""
520
+ statuses = []
521
+ for internal in True, False:
522
+ if status := self.get_tls_status(internal=internal):
523
+ statuses.append(status)
524
+ return statuses
@@ -28,7 +28,11 @@ from single_kernel_mongo.core.structured_config import MongoConfigModel, MongoDB
28
28
  from single_kernel_mongo.core.workload import WorkloadBase
29
29
  from single_kernel_mongo.exceptions import WorkloadServiceError
30
30
  from single_kernel_mongo.state.charm_state import CharmState
31
- from single_kernel_mongo.utils.mongodb_users import BackupUser, LogRotateUser, MonitorUser
31
+ from single_kernel_mongo.utils.mongodb_users import (
32
+ CharmedBackupUser,
33
+ CharmedLogRotateUser,
34
+ CharmedStatsUser,
35
+ )
32
36
  from single_kernel_mongo.workload import (
33
37
  get_logrotate_workload_for_substrate,
34
38
  get_mongodb_exporter_workload_for_substrate,
@@ -135,7 +139,7 @@ class BackupConfigManager(CommonConfigManager):
135
139
  logger.info("Not starting PBM yet. Shard not added to config-server")
136
140
  return
137
141
 
138
- if not self.state.get_user_password(BackupUser):
142
+ if not self.state.get_user_password(CharmedBackupUser):
139
143
  logger.info("No password found.")
140
144
  return
141
145
 
@@ -184,7 +188,7 @@ class LogRotateConfigManager(CommonConfigManager):
184
188
  logger.info("DB is not initialised.")
185
189
  return
186
190
 
187
- if not self.state.get_user_password(LogRotateUser):
191
+ if not self.state.get_user_password(CharmedLogRotateUser):
188
192
  logger.info("No password found.")
189
193
  return
190
194
 
@@ -224,17 +228,17 @@ class MongoDBExporterConfigManager(CommonConfigManager):
224
228
 
225
229
  @override
226
230
  def build_parameters(self) -> list[list[str]]:
227
- return [[self.state.monitor_config.uri]]
231
+ return [[self.state.stats_config.uri]]
228
232
 
229
233
  def configure_and_restart(self):
230
234
  """Exposes the endpoint to mongodb_exporter."""
231
235
  if not self.state.db_initialised:
232
236
  return
233
237
 
234
- if not self.state.get_user_password(MonitorUser):
238
+ if not self.state.get_user_password(CharmedStatsUser):
235
239
  return
236
240
 
237
- if not self.workload.active() or self.get_environment() != self.state.monitor_config.uri:
241
+ if not self.workload.active() or self.get_environment() != self.state.stats_config.uri:
238
242
  try:
239
243
  # Always enable the service
240
244
  self.workload.stop()
@@ -269,7 +273,7 @@ class MongoConfigManager(FileBasedConfigManager, ABC):
269
273
  self.binding_ips,
270
274
  self.port_parameter,
271
275
  self.auth_parameter,
272
- self.tls_parameters,
276
+ self.client_tls_parameters,
273
277
  self.log_options,
274
278
  self.audit_options,
275
279
  self.ldap_parameters,
@@ -332,16 +336,20 @@ class MongoConfigManager(FileBasedConfigManager, ABC):
332
336
  def auth_parameter(self) -> dict[str, Any]:
333
337
  """The auth mode."""
334
338
  cmd = {"security": {"authorization": "enabled"}} if self.auth else {}
335
- if self.state.tls.internal_enabled and self.state.tls.external_enabled:
339
+ if self.state.tls.peer_enabled:
336
340
  return always_merger.merge(
337
341
  cmd,
338
342
  {
339
343
  "security": {"clusterAuthMode": "x509"},
340
344
  "net": {
341
345
  "tls": {
346
+ "mode": "preferTLS",
342
347
  "allowInvalidCertificates": True,
343
- "clusterCAFile": f"{self.workload.paths.int_ca_file}",
348
+ "certificateKeyFile": f"{self.workload.paths.int_pem_file}",
349
+ "CAFile": f"{self.workload.paths.int_ca_file}",
350
+ "disabledProtocols": "TLS1_0,TLS1_1",
344
351
  "clusterFile": f"{self.workload.paths.int_pem_file}",
352
+ "clusterCAFile": f"{self.workload.paths.int_ca_file}",
345
353
  "clusterAuthX509": {
346
354
  "attributes": f"O={self.state.get_subject_name()}",
347
355
  },
@@ -360,10 +368,11 @@ class MongoConfigManager(FileBasedConfigManager, ABC):
360
368
  )
361
369
 
362
370
  @property
363
- def tls_parameters(self) -> dict[str, Any]:
364
- """The TLS external parameters."""
365
- if self.state.tls.external_enabled:
366
- return {
371
+ def client_tls_parameters(self) -> dict[str, Any]:
372
+ """The client TLS parameters."""
373
+ params = {}
374
+ if self.state.tls.client_enabled:
375
+ params = {
367
376
  "net": {
368
377
  "tls": {
369
378
  "CAFile": f"{self.workload.paths.ext_ca_file}",
@@ -373,7 +382,8 @@ class MongoConfigManager(FileBasedConfigManager, ABC):
373
382
  }
374
383
  },
375
384
  }
376
- return {}
385
+
386
+ return params
377
387
 
378
388
  @property
379
389
  @abstractmethod
@@ -52,11 +52,11 @@ from single_kernel_mongo.utils.mongo_config import (
52
52
  from single_kernel_mongo.utils.mongo_connection import MongoConnection, NotReadyError
53
53
  from single_kernel_mongo.utils.mongodb_users import (
54
54
  OPERATOR_ROLE,
55
- BackupUser,
56
- LogRotateUser,
55
+ CharmedBackupUser,
56
+ CharmedLogRotateUser,
57
+ CharmedOperatorUser,
58
+ CharmedStatsUser,
57
59
  MongoDBUser,
58
- MonitorUser,
59
- OperatorUser,
60
60
  )
61
61
 
62
62
  if TYPE_CHECKING:
@@ -138,12 +138,12 @@ class MongoManager(Object, ManagerStatusProtocol):
138
138
 
139
139
  def initialise_charm_admin_users(self) -> None:
140
140
  """First initialisation of each user."""
141
- self.initialise_operator_user()
142
- self.initialise_user(MonitorUser)
143
- self.initialise_user(BackupUser)
144
- self.initialise_user(LogRotateUser)
141
+ self.initialise_charmed_operator_user()
142
+ self.initialise_user(CharmedStatsUser)
143
+ self.initialise_user(CharmedBackupUser)
144
+ self.initialise_user(CharmedLogRotateUser)
145
145
 
146
- def initialise_operator_user(self):
146
+ def initialise_charmed_operator_user(self):
147
147
  """Creates initial admin user for MongoDB.
148
148
 
149
149
  Initial admin user can be created only through localhost connection.
@@ -154,7 +154,7 @@ class MongoManager(Object, ManagerStatusProtocol):
154
154
  It is needed to install mongodb-clients inside charm container to make
155
155
  this function work correctly.
156
156
  """
157
- if self.state.app_peer_data.is_user_created(OperatorUser.username):
157
+ if self.state.app_peer_data.is_user_created(CharmedOperatorUser.username):
158
158
  return
159
159
  config = self.state.mongo_config
160
160
  cmd = [
@@ -169,7 +169,7 @@ class MongoManager(Object, ManagerStatusProtocol):
169
169
  '})"',
170
170
  ]
171
171
  self.workload.run_bin_command("mongodb://localhost/admin", cmd, input=config.password)
172
- self.state.app_peer_data.set_user_created(OperatorUser.username)
172
+ self.state.app_peer_data.set_user_created(CharmedOperatorUser.username)
173
173
 
174
174
  def initialise_user(self, user: MongoDBUser):
175
175
  """Creates a user and sets its role on the MongoDB database."""
@@ -530,7 +530,7 @@ class MongoManager(Object, ManagerStatusProtocol):
530
530
  return charm_statuses
531
531
 
532
532
  except ServerSelectionTimeoutError as e:
533
- # Usually it is du to ReplicaSetNoPrimary
533
+ # Usually it is due to ReplicaSetNoPrimary
534
534
  logger.debug(f"Got error {e} while checking replica set status")
535
535
  return [MongodStatuses.WAITING_ELECTION.value]
536
536
  except AutoReconnect as e:
@@ -33,7 +33,10 @@ from single_kernel_mongo.config.models import (
33
33
  PasswordManagementContext,
34
34
  PasswordManagementState,
35
35
  )
36
- from single_kernel_mongo.config.relations import ExternalRequirerRelations, RelationNames
36
+ from single_kernel_mongo.config.relations import (
37
+ ExternalRequirerRelations,
38
+ RelationNames,
39
+ )
37
40
  from single_kernel_mongo.config.statuses import (
38
41
  BackupStatuses,
39
42
  CharmStatuses,
@@ -47,7 +50,7 @@ from single_kernel_mongo.core.kubernetes_upgrades_v3 import KubernetesMongoDBRef
47
50
  from single_kernel_mongo.core.machine_upgrades_v3 import MachineMongoDBRefresh
48
51
  from single_kernel_mongo.core.operator import OperatorProtocol
49
52
  from single_kernel_mongo.core.secrets import generate_secret_label
50
- from single_kernel_mongo.core.structured_config import MongoDBRoles
53
+ from single_kernel_mongo.core.structured_config import MongoDBCharmConfig, MongoDBRoles
51
54
  from single_kernel_mongo.core.version_checker import VersionChecker
52
55
  from single_kernel_mongo.events.backups import BackupEventsHandler
53
56
  from single_kernel_mongo.events.cluster import ClusterConfigServerEventHandler
@@ -97,12 +100,12 @@ from single_kernel_mongo.state.charm_state import CharmState
97
100
  from single_kernel_mongo.utils.helpers import is_valid_ldap_options, is_valid_ldapusertodnmapping
98
101
  from single_kernel_mongo.utils.mongo_connection import MongoConnection, NotReadyError
99
102
  from single_kernel_mongo.utils.mongodb_users import (
100
- BackupUser,
103
+ CharmedBackupUser,
104
+ CharmedLogRotateUser,
105
+ CharmedOperatorUser,
106
+ CharmedStatsUser,
101
107
  InternalUsers,
102
- LogRotateUser,
103
108
  MongoDBUser,
104
- MonitorUser,
105
- OperatorUser,
106
109
  get_user_from_username,
107
110
  validate_charm_user_password_config,
108
111
  )
@@ -167,12 +170,7 @@ class MongoDBOperator(OperatorProtocol, Object):
167
170
  self.state,
168
171
  container,
169
172
  )
170
- self.tls_manager = TLSManager(
171
- self,
172
- self.workload,
173
- self.state,
174
- self.substrate,
175
- )
173
+ self.tls_manager = TLSManager(self, self.workload, self.state)
176
174
  self.mongo_manager = MongoManager(
177
175
  self,
178
176
  self.workload,
@@ -345,7 +343,8 @@ class MongoDBOperator(OperatorProtocol, Object):
345
343
  return
346
344
 
347
345
  @property
348
- def config(self):
346
+ @override
347
+ def config(self) -> MongoDBCharmConfig:
349
348
  """Returns the actual config."""
350
349
  return self.charm.parsed_config
351
350
 
@@ -393,6 +392,7 @@ class MongoDBOperator(OperatorProtocol, Object):
393
392
  return (
394
393
  self,
395
394
  self.mongo_manager,
395
+ self.tls_manager,
396
396
  self.shard_manager,
397
397
  self.config_server_manager,
398
398
  self.backup_manager,
@@ -487,7 +487,9 @@ class MongoDBOperator(OperatorProtocol, Object):
487
487
  except (NotReadyError, PyMongoError, WorkloadExecError) as e:
488
488
  logger.error(f"Deferring on start: error={e}")
489
489
  self.state.statuses.add(
490
- MongodStatuses.WAITING_REPL_SET_INIT.value, scope="unit", component=self.name
490
+ MongodStatuses.WAITING_REPL_SET_INIT.value,
491
+ scope="unit",
492
+ component=self.name,
491
493
  )
492
494
  raise
493
495
 
@@ -717,10 +719,10 @@ class MongoDBOperator(OperatorProtocol, Object):
717
719
 
718
720
  Adds the unit as a replica to the MongoDB replica set.
719
721
  """
720
- # Changing the monitor or the backup password will lead to non-leader
721
- # units receiving a relation changed event. We must update the monitor
722
- # and pbm URI if the password changes so that COS/pbm can continue to
723
- # work.
722
+ # Changing the charmed-stats or the charmed-backup password will lead
723
+ # to non-leader units receiving a relation changed event. We must update
724
+ # the monitor and pbm URI if the password changes so that COS/pbm can
725
+ # continue to work.
724
726
  if self.state.db_initialised and self.workload.active():
725
727
  self.mongodb_exporter_config_manager.configure_and_restart()
726
728
  self.backup_manager.configure_and_restart()
@@ -879,22 +881,17 @@ class MongoDBOperator(OperatorProtocol, Object):
879
881
  @override
880
882
  def update_status(self) -> None:
881
883
  """Status update Handler."""
882
- # TODO update the usage of this once the spec is approved and we have a consistent way of
883
- # handling statuses
884
884
  if self.basic_statuses():
885
885
  logger.info("Early return invalid statuses.")
886
886
  return
887
887
 
888
+ if self.state.is_role(MongoDBRoles.SHARD) and self._should_skip_because_of_incomplete_tls():
889
+ return
890
+
888
891
  if self.cluster_version_checker.get_cluster_mismatched_revision_status():
889
892
  logger.info("Early return, cluster mismatch version.")
890
893
  return
891
894
 
892
- if self.state.is_role(MongoDBRoles.SHARD):
893
- shard_has_tls, config_server_has_tls = self.shard_manager.tls_status()
894
- if config_server_has_tls and not shard_has_tls:
895
- logger.info("Shard is missing TLS.")
896
- return
897
-
898
895
  if not self.mongo_manager.mongod_ready():
899
896
  logger.info("Mongod not ready.")
900
897
  return
@@ -915,16 +912,18 @@ class MongoDBOperator(OperatorProtocol, Object):
915
912
  def update_single_user_password(self, user: MongoDBUser, new_password: str) -> None:
916
913
  """Set password in Mongod and restart the appropriate services."""
917
914
  self.mongo_manager.set_user_password(user, new_password)
918
- if user == BackupUser:
915
+ if user == CharmedBackupUser:
919
916
  # Update and restart PBM Agent.
920
917
  self.backup_manager.configure_and_restart()
921
- if user == MonitorUser:
918
+ if user == CharmedStatsUser:
922
919
  # Update and restart mongodb exporter.
923
920
  self.mongodb_exporter_config_manager.configure_and_restart()
924
- if user == LogRotateUser:
921
+ if user == CharmedLogRotateUser:
925
922
  # Update and restart logrotate.
926
923
  self.logrotate_config_manager.configure_and_restart()
927
- if user in (OperatorUser, BackupUser) and self.state.is_role(MongoDBRoles.CONFIG_SERVER):
924
+ if user in (CharmedOperatorUser, CharmedBackupUser) and self.state.is_role(
925
+ MongoDBRoles.CONFIG_SERVER
926
+ ):
928
927
  self.config_server_manager.update_credentials(
929
928
  user.password_key_name,
930
929
  new_password,
@@ -1055,7 +1054,9 @@ class MongoDBOperator(OperatorProtocol, Object):
1055
1054
  except WorkloadServiceError as e:
1056
1055
  logger.error("An exception occurred when starting mongod agent, error: %s.", str(e))
1057
1056
  self.charm.state.statuses.add(
1058
- MongoDBStatuses.WAITING_FOR_MONGODB_START.value, scope="unit", component=self.name
1057
+ MongoDBStatuses.WAITING_FOR_MONGODB_START.value,
1058
+ scope="unit",
1059
+ component=self.name,
1059
1060
  )
1060
1061
  raise
1061
1062
 
@@ -1075,7 +1076,9 @@ class MongoDBOperator(OperatorProtocol, Object):
1075
1076
  self.backup_manager.configure_and_restart()
1076
1077
  except WorkloadServiceError:
1077
1078
  self.state.statuses.add(
1078
- BackupStatuses.WAITING_FOR_PBM_START.value, scope="unit", component=self.name
1079
+ BackupStatuses.WAITING_FOR_PBM_START.value,
1080
+ scope="unit",
1081
+ component=self.name,
1079
1082
  )
1080
1083
  raise
1081
1084
 
@@ -1116,7 +1119,10 @@ class MongoDBOperator(OperatorProtocol, Object):
1116
1119
  logger.error("Charm is in sharding mode. Does not support %s interface.", rel_name)
1117
1120
  return MongoDBStatuses.INVALID_CFG_SRV_ON_SHARD_REL.value
1118
1121
  if self.state.is_role(MongoDBRoles.CONFIG_SERVER) and rel_name == RelationNames.SHARDING:
1119
- logger.error("Charm is in config-server mode. Does not support %s interface.", rel_name)
1122
+ logger.error(
1123
+ "Charm is in config-server mode. Does not support %s interface.",
1124
+ rel_name,
1125
+ )
1120
1126
  return MongoDBStatuses.INVALID_SHARD_ON_CFG_SRV_REL.value
1121
1127
  if not self.state.is_role(MongoDBRoles.CONFIG_SERVER) and rel_name == RelationNames.CLUSTER:
1122
1128
  logger.error("Charm is not a config-server, cannot integrate mongos")
@@ -1136,7 +1142,9 @@ class MongoDBOperator(OperatorProtocol, Object):
1136
1142
  self.build_local_tls_directory()
1137
1143
 
1138
1144
  # Push TLS files if necessary
1139
- self.tls_manager.push_tls_files_to_workload()
1145
+ for internal in [True, False]:
1146
+ self.tls_manager.push_tls_files_to_workload(internal)
1147
+
1140
1148
  self.ldap_manager.save_certificates(self.state.ldap.chain)
1141
1149
 
1142
1150
  # Update licenses
@@ -1354,3 +1362,19 @@ class MongoDBOperator(OperatorProtocol, Object):
1354
1362
  scope="app",
1355
1363
  component=self.name,
1356
1364
  )
1365
+
1366
+ def _should_skip_because_of_incomplete_tls(self) -> bool:
1367
+ """Checks if the update status hook needs skipping due to an incomplete TLS integration."""
1368
+ shard_has_peer_tls, config_server_has_peer_tls = (
1369
+ self.shard_manager.shard_and_config_server_peer_tls_status()
1370
+ )
1371
+ if config_server_has_peer_tls and not shard_has_peer_tls:
1372
+ logger.info("Shard is missing peer TLS.")
1373
+ return True
1374
+ shard_has_client_tls, config_server_has_client_tls = (
1375
+ self.shard_manager.shard_and_config_server_client_tls_status()
1376
+ )
1377
+ if config_server_has_client_tls and not shard_has_client_tls:
1378
+ logger.info("Shard is missing client TLS.")
1379
+ return True
1380
+ return False
@@ -104,12 +104,7 @@ class MongosOperator(OperatorProtocol, Object):
104
104
  self.state,
105
105
  self.substrate,
106
106
  )
107
- self.tls_manager = TLSManager(
108
- self,
109
- self.workload,
110
- self.state,
111
- self.substrate,
112
- )
107
+ self.tls_manager = TLSManager(self, self.workload, self.state)
113
108
  self.cluster_manager = ClusterRequirer(
114
109
  self, self.workload, self.state, self.substrate, RelationNames.CLUSTER
115
110
  )
@@ -201,9 +196,10 @@ class MongosOperator(OperatorProtocol, Object):
201
196
  @property
202
197
  def components(self) -> tuple[ManagerStatusProtocol, ...]:
203
198
  """The ordered list of components for this operator."""
204
- return (self, self.ldap_manager, self.upgrades_status_manager)
199
+ return (self, self.tls_manager, self.ldap_manager, self.upgrades_status_manager)
205
200
 
206
201
  @property
202
+ @override
207
203
  def config(self) -> MongosCharmConfig:
208
204
  """Returns the actual config."""
209
205
  return self.charm.parsed_config
@@ -222,8 +218,8 @@ class MongosOperator(OperatorProtocol, Object):
222
218
  # Instantiate the local directory for k8s
223
219
  self.build_local_tls_directory()
224
220
 
225
- # Push certificates
226
- self.tls_manager.push_tls_files_to_workload()
221
+ for internal in [True, False]:
222
+ self.tls_manager.push_tls_files_to_workload(internal)
227
223
 
228
224
  # Save LDAP certificates
229
225
  self.ldap_manager.save_certificates(self.state.ldap.chain)
@@ -301,8 +297,7 @@ class MongosOperator(OperatorProtocol, Object):
301
297
  component=self.name,
302
298
  )
303
299
  self.update_k8s_external_services()
304
-
305
- self.tls_manager.update_tls_sans()
300
+ self.tls_events.refresh_certificates()
306
301
  self.share_connection_info()
307
302
 
308
303
  @override
@@ -345,7 +340,7 @@ class MongosOperator(OperatorProtocol, Object):
345
340
  # our SANS as necessary.
346
341
  # The connection info will be updated when we receive the new certificates.
347
342
  if self.substrate == Substrates.K8S:
348
- self.tls_manager.update_tls_sans()
343
+ self.tls_events.refresh_certificates()
349
344
 
350
345
  @override
351
346
  def new_peer(self) -> None:
@@ -390,11 +385,10 @@ class MongosOperator(OperatorProtocol, Object):
390
385
  self.mongos_config_manager.configure_and_restart(force=force)
391
386
  except WorkloadServiceError as e:
392
387
  logger.error("An exception occurred when starting mongos agent, error: %s.", str(e))
393
- self.charm.status_handler.set_running_status(
388
+ self.charm.state.statuses.add(
394
389
  MongosStatuses.WAITING_FOR_MONGOS_START.value,
395
390
  scope="unit",
396
- statuses_state=self.state.statuses,
397
- component_name=self.name,
391
+ component=self.name,
398
392
  )
399
393
  raise
400
394
 
@@ -579,8 +573,9 @@ class MongosOperator(OperatorProtocol, Object):
579
573
  )
580
574
  return False
581
575
 
582
- if status := self.cluster_manager.get_tls_statuses():
583
- logger.info(f"Invalid TLS integration: {status.message}")
576
+ if statuses := self.cluster_manager.tls_statuses():
577
+ for status in statuses:
578
+ logger.info(f"Invalid TLS integration: {status.message}")
584
579
  return False
585
580
 
586
581
  if not self.is_mongos_running():
@@ -618,10 +613,11 @@ class MongosOperator(OperatorProtocol, Object):
618
613
  # don't bother checking remaining statuses if no config-server is present
619
614
  return charm_statuses
620
615
 
621
- if status := self.cluster_manager.get_tls_statuses():
622
- logger.info(f"Invalid TLS integration: {status.message}")
616
+ if statuses := self.cluster_manager.tls_statuses():
617
+ for status in statuses:
618
+ logger.info(f"Invalid TLS integration: {status.message}")
623
619
  # if TLS is misconfigured we will get redherrings on the remaining messages
624
- charm_statuses.append(status)
620
+ charm_statuses += statuses
625
621
  return charm_statuses
626
622
 
627
623
  if self.state.mongos_cluster_relation and not self.state.cluster.config_server_uri: