mongo-charms-single-kernel 1.8.8__tar.gz → 1.8.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.

Potentially problematic release.


This version of mongo-charms-single-kernel might be problematic. Click here for more details.

Files changed (94) hide show
  1. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/PKG-INFO +1 -1
  2. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/pyproject.toml +2 -8
  3. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/config/literals.py +12 -5
  4. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/config/relations.py +2 -1
  5. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/config/statuses.py +127 -20
  6. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/operator.py +7 -0
  7. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/structured_config.py +2 -0
  8. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/workload.py +10 -4
  9. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/cluster.py +5 -0
  10. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/sharding.py +3 -1
  11. mongo_charms_single_kernel-1.8.10/single_kernel_mongo/events/tls.py +242 -0
  12. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/exceptions.py +0 -8
  13. mongo_charms_single_kernel-1.8.10/single_kernel_mongo/lib/charms/tls_certificates_interface/v4/tls_certificates.py +1995 -0
  14. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/cluster.py +70 -28
  15. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/config.py +24 -14
  16. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/mongo.py +12 -12
  17. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/mongodb_operator.py +58 -34
  18. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/mongos_operator.py +16 -20
  19. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/sharding.py +172 -136
  20. mongo_charms_single_kernel-1.8.10/single_kernel_mongo/managers/tls.py +413 -0
  21. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/upgrade_v3.py +6 -6
  22. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/charm_state.py +54 -31
  23. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/cluster_state.py +8 -0
  24. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/config_server_state.py +15 -6
  25. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/models.py +2 -2
  26. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/tls_state.py +39 -12
  27. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/helpers.py +4 -19
  28. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/mongodb_users.py +20 -20
  29. mongo_charms_single_kernel-1.8.8/single_kernel_mongo/events/tls.py +0 -216
  30. mongo_charms_single_kernel-1.8.8/single_kernel_mongo/lib/charms/tls_certificates_interface/v3/tls_certificates.py +0 -2123
  31. mongo_charms_single_kernel-1.8.8/single_kernel_mongo/managers/tls.py +0 -396
  32. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/LICENSE +0 -0
  33. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/README.md +0 -0
  34. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/__init__.py +0 -0
  35. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/abstract_charm.py +0 -0
  36. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/config/__init__.py +0 -0
  37. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/config/models.py +0 -0
  38. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/__init__.py +0 -0
  39. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/abstract_upgrades_v3.py +0 -0
  40. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/k8s_workload.py +0 -0
  41. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/kubernetes_upgrades_v3.py +0 -0
  42. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/machine_upgrades_v3.py +0 -0
  43. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/secrets.py +0 -0
  44. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/version_checker.py +0 -0
  45. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/core/vm_workload.py +0 -0
  46. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/__init__.py +0 -0
  47. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/backups.py +0 -0
  48. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/database.py +0 -0
  49. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/ldap.py +0 -0
  50. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/lifecycle.py +0 -0
  51. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/events/primary_action.py +0 -0
  52. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/certificate_transfer_interface/v0/certificate_transfer.py +0 -0
  53. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/data_platform_libs/v0/data_interfaces.py +0 -0
  54. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/data_platform_libs/v0/s3.py +0 -0
  55. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/glauth_k8s/v0/ldap.py +0 -0
  56. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/grafana_agent/v0/cos_agent.py +0 -0
  57. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/grafana_k8s/v0/grafana_dashboard.py +0 -0
  58. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/loki_k8s/v0/loki_push_api.py +0 -0
  59. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/operator_libs_linux/v0/sysctl.py +0 -0
  60. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/operator_libs_linux/v1/systemd.py +0 -0
  61. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/operator_libs_linux/v2/snap.py +0 -0
  62. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/lib/charms/prometheus_k8s/v0/prometheus_scrape.py +0 -0
  63. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/__init__.py +0 -0
  64. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/backups.py +0 -0
  65. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/k8s.py +0 -0
  66. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/ldap.py +0 -0
  67. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/observability.py +0 -0
  68. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/managers/upgrade_v3_status.py +0 -0
  69. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/observability_rules/__init__.py +0 -0
  70. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/observability_rules/grafana_dashboards/MongoDB_Cluster_Summary.json +0 -0
  71. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/observability_rules/grafana_dashboards/MongoDB_ReplSet_Summary.json +0 -0
  72. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/observability_rules/k8s_prometheus_alert_rules/percona-mongodb-exporter.rule +0 -0
  73. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/observability_rules/loki/.gitkeep +0 -0
  74. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/observability_rules/vm_prometheus_alert_rules/percona-mongodb-exporter.yml +0 -0
  75. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/__init__.py +0 -0
  76. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/abstract_state.py +0 -0
  77. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/app_peer_state.py +0 -0
  78. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/ldap_state.py +0 -0
  79. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/state/unit_peer_state.py +0 -0
  80. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/templates/__init__.py +0 -0
  81. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/templates/enable-transparent-huge-pages.service.j2 +0 -0
  82. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/templates/ldap.conf.j2 +0 -0
  83. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/templates/logrotate.j2 +0 -0
  84. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/__init__.py +0 -0
  85. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/event_helpers.py +0 -0
  86. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/mongo_config.py +0 -0
  87. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/mongo_connection.py +0 -0
  88. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/utils/mongo_error_codes.py +0 -0
  89. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/workload/__init__.py +0 -0
  90. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/workload/backup_workload.py +0 -0
  91. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/workload/log_rotate_workload.py +0 -0
  92. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/workload/mongodb_workload.py +0 -0
  93. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/workload/mongos_workload.py +0 -0
  94. {mongo_charms_single_kernel-1.8.8 → mongo_charms_single_kernel-1.8.10}/single_kernel_mongo/workload/monitor_workload.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mongo-charms-single-kernel
3
- Version: 1.8.8
3
+ Version: 1.8.10
4
4
  Summary: Shared and reusable code for Mongo-related charms
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -38,9 +38,9 @@ dependencies = [
38
38
  "boto3 (~=1.37.12)",
39
39
  "mypy-boto3-s3 (~=1.37.0)",
40
40
  "python-ldap",
41
- "charm-refresh (>=3.1.0.2,<4.0.0.0)",
41
+ "charm-refresh (>=3.1.0.2,<4.0.0.0)"
42
42
  ]
43
- version = "1.8.8"
43
+ version = "1.8.10"
44
44
 
45
45
  [project.urls]
46
46
  homepage = "https://github.com/canonical/mongo-single-kernel-library"
@@ -137,12 +137,6 @@ httpx = "*"
137
137
  boto3 = "^1.37.12"
138
138
  mypy-boto3-s3 = "^1.37.0"
139
139
 
140
- [tool.poetry.group.build-refresh-version]
141
- optional = true
142
-
143
- [tool.poetry.group.build-refresh-version.dependencies]
144
- charm-refresh-build-version = "^0.4.0"
145
-
146
140
  [tool.ruff]
147
141
  target-version = "py310"
148
142
  line-length = 100
@@ -34,6 +34,13 @@ class Scope(str, Enum):
34
34
  UNIT = "unit"
35
35
 
36
36
 
37
+ class TLSType(str, Enum):
38
+ """TLS types."""
39
+
40
+ PEER = "peer"
41
+ CLIENT = "client"
42
+
43
+
37
44
  class MongoPorts(IntEnum):
38
45
  """The default Mongo ports."""
39
46
 
@@ -44,13 +51,13 @@ class MongoPorts(IntEnum):
44
51
  class InternalUsernames(str, Enum):
45
52
  """The allowed internal usernames."""
46
53
 
47
- OPERATOR = "operator"
48
- BACKUP = "backup"
49
- MONITOR = "monitor"
50
- LOGROTATE = "logrotate"
54
+ CHARMED_OPERATOR = "charmed-operator"
55
+ CHARMED_BACKUP = "charmed-backup"
56
+ CHARMED_STATS = "charmed-stats"
57
+ CHARMED_LOGROTATE = "charmed-logrotate"
51
58
 
52
59
 
53
- SECRETS_APP = [f"{user}-password" for user in InternalUsernames] + ["keyfile"]
60
+ SECRETS_APP = [f"{username}-password" for username in InternalUsernames] + ["keyfile"]
54
61
 
55
62
  VERSIONS_FILE = Path("refresh_versions.toml")
56
63
 
@@ -35,7 +35,8 @@ class Scopes(str, Enum):
35
35
  class ExternalRequirerRelations(str, Enum):
36
36
  """The relations we require externally."""
37
37
 
38
- TLS = "certificates"
38
+ CLIENT_TLS = "client-certificates"
39
+ PEER_TLS = "peer-certificates"
39
40
  S3_CREDENTIALS = "s3-credentials"
40
41
  LDAP = "ldap"
41
42
  LDAP_CERT = "ldap-certificate-transfer"
@@ -118,24 +118,45 @@ class MongosStatuses(Enum):
118
118
  check="Config validation failed.",
119
119
  action="Set the expose-external config to a valid value: `nodeport` or `none`.",
120
120
  )
121
- MISSING_TLS_REL = StatusObject(
121
+ MISSING_PEER_TLS_REL = StatusObject(
122
122
  status="blocked",
123
- message="TLS must be enabled in mongos, since it is enabled on the config-server in the cluster relation.",
124
- short_message="Missing certificates relation.",
123
+ message="Peer TLS must be enabled in mongos, since it is enabled on the config-server in the cluster relation.",
124
+ short_message="Missing peer-certificates relation.",
125
125
  check="Relation validation failed.",
126
- action="Add the certificates relation (tls-certificates interface) to mongos.",
126
+ action="Add the peer-certificates relation to mongos.",
127
127
  )
128
- INVALID_TLS_REL = StatusObject(
128
+ INVALID_PEER_TLS_REL = StatusObject(
129
129
  status="blocked",
130
- message="TLS must be disabled in mongos, since it is disabled on the config-server in the cluster relation.",
131
- short_message="Invalid certificates relation.",
130
+ message="Peer TLS must be disabled in mongos, since it is disabled on the config-server in the cluster relation.",
131
+ short_message="Invalid peer-certificates relation.",
132
132
  check="Relation validation failed.",
133
- action="Remove the certificates relation (tls-certificates interface) from this application.",
133
+ action="Remove the peer-certificates relation from this application.",
134
134
  )
135
- CA_MISMATCH = StatusObject(
135
+ MISSING_CLIENT_TLS_REL = StatusObject(
136
136
  status="blocked",
137
- message="The mongos CA and Config-Server CA don't match.",
138
- short_message="CA mismatch.",
137
+ message="Client TLS must be enabled in mongos, since it is enabled on the config-server in the cluster relation.",
138
+ short_message="Missing client-certificates relation.",
139
+ check="Relation validation failed.",
140
+ action="Add the client-certificates relation to mongos.",
141
+ )
142
+ INVALID_CLIENT_TLS_REL = StatusObject(
143
+ status="blocked",
144
+ message="Client TLS must be disabled in mongos, since it is disabled on the config-server in the cluster relation.",
145
+ short_message="Invalid client-certificates relation.",
146
+ check="Relation validation failed.",
147
+ action="Remove the client-certificates relation from this application.",
148
+ )
149
+ PEER_CA_MISMATCH = StatusObject(
150
+ status="blocked",
151
+ message="The mongos peer CA and Config-Server peer CA don't match.",
152
+ short_message="Peer CA mismatch.",
153
+ check="Relation validation failed.",
154
+ action="Verify the certificates relations. Use the same CA for all cluster components.",
155
+ )
156
+ CLIENT_CA_MISMATCH = StatusObject(
157
+ status="blocked",
158
+ message="The mongos client CA and Config-Server client CA don't match.",
159
+ short_message="Client CA mismatch.",
139
160
  check="Relation validation failed.",
140
161
  action="Verify the certificates relations. Use the same CA for all cluster components.",
141
162
  )
@@ -153,6 +174,27 @@ class MongosStatuses(Enum):
153
174
  status="maintenance", message="Starting mongos.", running="blocking"
154
175
  )
155
176
 
177
+ @classmethod
178
+ def missing_tls(cls, internal: bool) -> StatusObject:
179
+ """Correct status."""
180
+ if internal:
181
+ return cls.MISSING_PEER_TLS_REL.value
182
+ return cls.MISSING_CLIENT_TLS_REL.value
183
+
184
+ @classmethod
185
+ def invalid_tls(cls, internal: bool) -> StatusObject:
186
+ """Correct status."""
187
+ if internal:
188
+ return cls.INVALID_PEER_TLS_REL.value
189
+ return cls.INVALID_CLIENT_TLS_REL.value
190
+
191
+ @classmethod
192
+ def incompatible_ca(cls, internal: bool) -> StatusObject:
193
+ """Correct status."""
194
+ if internal:
195
+ return cls.PEER_CA_MISMATCH.value
196
+ return cls.CLIENT_CA_MISMATCH.value
197
+
156
198
 
157
199
  class CharmStatuses(Enum):
158
200
  """Charm Statuses."""
@@ -182,11 +224,28 @@ class CharmStatuses(Enum):
182
224
  class TLSStatuses(Enum):
183
225
  """TLS statuses."""
184
226
 
185
- # RUNNING statuses:
186
- DISABLING_TLS = StatusObject(
227
+ INVALID_PEER_PRIVATE_KEY = StatusObject(
228
+ status="blocked",
229
+ message="Invalid peer private key",
230
+ check="Peer private key format validation failed",
231
+ action="Update the peer private key secret.",
232
+ )
233
+ INVALID_CLIENT_PRIVATE_KEY = StatusObject(
234
+ status="blocked",
235
+ message="Invalid client private key",
236
+ check="Client private key format validation failed.",
237
+ action="Update the client privatekey secret.",
238
+ )
239
+ DISABLING_PEER_TLS = StatusObject(
187
240
  status="maintenance",
188
- message="Disabling TLS...",
189
- check="Certificates relation (tls-certificates interface) removed.",
241
+ message="Disabling peer TLS...",
242
+ check="Peer certificates relation (tls-certificates interface) removed.",
243
+ running="blocking",
244
+ )
245
+ DISABLING_CLIENT_TLS = StatusObject(
246
+ status="maintenance",
247
+ message="Disabling client TLS...",
248
+ check="Client certificates relation (tls-certificates interface) removed.",
190
249
  running="blocking",
191
250
  )
192
251
  # Enabling TLS takes a while because we wait for multiple certs so it's
@@ -343,12 +402,39 @@ class ConfigServerStatuses(Enum):
343
402
  class ShardStatuses(Enum):
344
403
  """Shard statuses."""
345
404
 
346
- REQUIRES_TLS = StatusObject(status="blocked", message="Shard requires TLS to be enabled.")
347
- REQUIRES_NO_TLS = StatusObject(
348
- status="blocked", message="Shard has TLS enabled, but config-server does not."
405
+ MISSING_PEER_TLS_REL = StatusObject(
406
+ status="blocked", message="Shard requires peer TLS to be enabled."
349
407
  )
350
- CA_MISMATCH = StatusObject(
351
- status="blocked", message="Shard CA and Config-Server CA don't match."
408
+ INVALID_PEER_TLS_REL = StatusObject(
409
+ status="blocked",
410
+ message="Peer TLS must be disabled in shard, since it is disabled in the related config-server.",
411
+ short_message="Invalid peer-certificates relation.",
412
+ check="Relation validation failed.",
413
+ action="Align the peer TLS configuration in all the cluster components: remove the peer-certificates relation from the shard.",
414
+ )
415
+ MISSING_CLIENT_TLS_REL = StatusObject(
416
+ status="blocked", message="Shard requires client TLS to be enabled."
417
+ )
418
+ INVALID_CLIENT_TLS_REL = StatusObject(
419
+ status="blocked",
420
+ message="Peer TLS must be disabled in shard, since it is disabled in the related config-server.",
421
+ short_message="Invalid client-certificates relation.",
422
+ check="Relation validation failed.",
423
+ action="Align the peer TLS configuration in all the cluster components: remove the client-certificates relation from the shard.",
424
+ )
425
+ PEER_CA_MISMATCH = StatusObject(
426
+ status="blocked",
427
+ message="Shard internal CA and Config-Server internal CA don't match.",
428
+ short_message="Peer CA mismatch.",
429
+ check="Relation validation failed.",
430
+ action="Verify the peer-certificates relations. Use the same CA for all cluster components.",
431
+ )
432
+ CLIENT_CA_MISMATCH = StatusObject(
433
+ status="blocked",
434
+ message="Shard client CA and Config-Server client CA don't match.",
435
+ short_message="Client CA mismatch.",
436
+ check="Relation validation failed.",
437
+ action="Verify the client-certificates relations. Use the same CA for all cluster components.",
352
438
  )
353
439
 
354
440
  MISSING_CONF_SERVER_REL = StatusObject(
@@ -399,6 +485,27 @@ class ShardStatuses(Enum):
399
485
  message=f"Charm revision ({current_charms_version}{local_identifier}) is not up-to date with config-server.",
400
486
  )
401
487
 
488
+ @classmethod
489
+ def missing_tls(cls, internal: bool) -> StatusObject:
490
+ """Correct status."""
491
+ if internal:
492
+ return cls.MISSING_PEER_TLS_REL.value
493
+ return cls.MISSING_CLIENT_TLS_REL.value
494
+
495
+ @classmethod
496
+ def invalid_tls(cls, internal: bool) -> StatusObject:
497
+ """Correct status."""
498
+ if internal:
499
+ return cls.INVALID_PEER_TLS_REL.value
500
+ return cls.INVALID_CLIENT_TLS_REL.value
501
+
502
+ @classmethod
503
+ def incompatible_ca(cls, internal: bool) -> StatusObject:
504
+ """Correct status."""
505
+ if internal:
506
+ return cls.PEER_CA_MISMATCH.value
507
+ return cls.CLIENT_CA_MISMATCH.value
508
+
402
509
 
403
510
  class MongodStatuses(Enum):
404
511
  """MongoD statuses."""
@@ -37,6 +37,7 @@ from single_kernel_mongo.config.literals import (
37
37
  TrustStoreFiles,
38
38
  )
39
39
  from single_kernel_mongo.config.models import SNAP_NAME, THP_CONFIG, CharmSpec, LogRotateConfig
40
+ from single_kernel_mongo.core.structured_config import MongoConfigModel
40
41
  from single_kernel_mongo.events.ldap import LDAPEventHandler
41
42
  from single_kernel_mongo.exceptions import (
42
43
  DeferrableFailedHookChecksError,
@@ -106,6 +107,12 @@ class OperatorProtocol(ABC, Object, ManagerStatusProtocol):
106
107
 
107
108
  def __init__(self, dependent: AbstractMongoCharm): ...
108
109
 
110
+ @property
111
+ @abstractmethod
112
+ def config(self) -> MongoConfigModel:
113
+ """The pydantic model of the config."""
114
+ ...
115
+
109
116
  @property
110
117
  @abstractmethod
111
118
  def components(self) -> tuple[ManagerStatusProtocol, ...]:
@@ -100,6 +100,8 @@ class MongoConfigModel(BaseConfigModel):
100
100
  role: SerializeLiteralAsStr[MongoDBRoles]
101
101
  ldap_user_to_dn_mapping: str | None = Field(default=None, alias="ldap-user-to-dn-mapping")
102
102
  ldap_query_template: str | None = Field(default=None, alias="ldap-query-template")
103
+ tls_peer_private_key_id: str | None = Field(default=None, alias="tls-peer-private-key")
104
+ tls_client_private_key_id: str | None = Field(default=None, alias="tls-client-private-key")
103
105
 
104
106
  @field_validator("expose_external", mode="before")
105
107
  @classmethod
@@ -93,15 +93,21 @@ class MongoPaths:
93
93
  return Path(f"{self.conf_path}/internal-ca.crt")
94
94
 
95
95
  @property
96
- def tls_files(self) -> set[Path]:
97
- """Set of all TLS files."""
96
+ def tls_peer_files(self) -> set[Path]:
97
+ """Set of peer TLS files."""
98
98
  return {
99
- self.ext_pem_file,
100
- self.ext_ca_file,
101
99
  self.int_pem_file,
102
100
  self.int_ca_file,
103
101
  }
104
102
 
103
+ @property
104
+ def tls_client_files(self) -> set[Path]:
105
+ """Set of client TLS files."""
106
+ return {
107
+ self.ext_pem_file,
108
+ self.ext_ca_file,
109
+ }
110
+
105
111
  @property
106
112
  def ldap_path(self) -> Path:
107
113
  """The LDAP conf path."""
@@ -136,6 +136,11 @@ class ClusterMongosEventHandler(Object):
136
136
  def _on_relation_created(self, event: RelationCreatedEvent) -> None:
137
137
  """Relation created event handler."""
138
138
  self.manager.set_relation_created_status()
139
+ # Edge condition: mongos was integrated with the certificates provider
140
+ # before being integrated with the config-server. We trigger the refresh
141
+ # of the certificates to use the config-server as CSR subject.
142
+ if self.manager.state.peer_tls_relation or self.manager.state.client_tls_relation:
143
+ self.dependent.tls_events.refresh_certificates()
139
144
 
140
145
  def _on_database_created(self, event: DatabaseCreatedEvent) -> None:
141
146
  """Database Created event handler.
@@ -162,8 +162,10 @@ class ShardEventHandler(Object):
162
162
  """SecretChanged event handler, which is used to propagate the updated passwords."""
163
163
  try:
164
164
  self.manager.handle_secret_changed(event.secret.label or "")
165
- except (NotReadyError, FailedToUpdateCredentialsError):
165
+ except (NotReadyError, FailedToUpdateCredentialsError, DeferrableFailedHookChecksError):
166
166
  event.defer()
167
+ except NonDeferrableFailedHookChecksError as e:
168
+ logger.info(f"Skipping {str(type(event))}: {str(e)}")
167
169
  except WaitingForSecretsError:
168
170
  logger.info("Missing secrets, ignoring")
169
171
 
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/env python3
2
+ # Copyright 2024 Canonical Ltd.
3
+ # See LICENSE file for licensing details.
4
+
5
+ """Manager for handling TLS events."""
6
+
7
+ from __future__ import annotations
8
+
9
+ import logging
10
+ from typing import TYPE_CHECKING
11
+
12
+ from ops import ConfigChangedEvent
13
+ from ops.charm import RelationBrokenEvent, RelationCreatedEvent
14
+ from ops.framework import EventBase, EventSource, Object
15
+
16
+ from single_kernel_mongo.config.literals import CharmKind, TLSType
17
+ from single_kernel_mongo.config.relations import ExternalRequirerRelations
18
+ from single_kernel_mongo.config.statuses import (
19
+ MongosStatuses,
20
+ ShardStatuses,
21
+ TLSStatuses,
22
+ )
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 (
26
+ CertificateAvailableEvent,
27
+ TLSCertificatesRequiresV4,
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
+
32
+ if TYPE_CHECKING:
33
+ from single_kernel_mongo.abstract_charm import AbstractMongoCharm
34
+ from single_kernel_mongo.core.operator import OperatorProtocol
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+
39
+ class RefreshTLSCertificatesEvent(EventBase):
40
+ """Event for refreshing TLS certificates."""
41
+
42
+
43
+ class TLSEventsHandler(Object):
44
+ """Event Handler for managing TLS events."""
45
+
46
+ refresh_tls_certificates_event = EventSource(RefreshTLSCertificatesEvent)
47
+
48
+ def __init__(self, dependent: OperatorProtocol):
49
+ super().__init__(parent=dependent, key="tls")
50
+ self.dependent = dependent
51
+ self.manager = self.dependent.tls_manager
52
+ self.charm: AbstractMongoCharm = dependent.charm
53
+
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],
60
+ )
61
+
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
72
+ )
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,
81
+ )
82
+ self.framework.observe(
83
+ self.charm.on[relation_name].relation_broken,
84
+ self._on_tls_relation_broken,
85
+ )
86
+
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}
98
+
99
+ def _on_tls_relation_created(self, event: RelationCreatedEvent) -> None:
100
+ """Handler for relation created."""
101
+ if self.manager.state.is_role(MongoDBRoles.MONGOS):
102
+ self.manager.state.statuses.delete(
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,
111
+ )
112
+
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()
129
+
130
+ def _on_tls_relation_broken(self, event: RelationBrokenEvent) -> None:
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
145
+
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
155
+ )
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()
160
+
161
+ def _on_certificate_available(self, event: CertificateAvailableEvent) -> None:
162
+ """Handler for the certificate available event.
163
+
164
+ This event is emitted by the TLS charm when a certificates is available.
165
+ """
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)
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.")
178
+
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.")
195
+ return
196
+
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}")
223
+ return
224
+
225
+ def _on_secret_changed(self, event: ConfigChangedEvent) -> None:
226
+ """On Secret Changed, validate private keys and refresh certs if needed."""
227
+ try:
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
238
+ )
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