Skip to content

Commit 45912cf

Browse files
committed
Merge branch '6/edge' into DPE-5661-safe-drainage
2 parents 1e09c4e + f9224f3 commit 45912cf

40 files changed

+1593
-205
lines changed

.github/workflows/release.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ jobs:
1515
uses: actions/checkout@v4
1616
with:
1717
fetch-depth: 0
18+
- run: |
19+
# Workaround for https://github.com/canonical/charmcraft/issues/1389#issuecomment-1880921728
20+
touch requirements.txt
1821
- name: Check libs
1922
uses: canonical/charming-actions/[email protected]
2023
with:

actions.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,21 @@ set-tls-private-key:
4242
internal-key:
4343
type: string
4444
description: The content of private key for internal communications with clients. Content will be auto-generated if this option is not specified.
45+
46+
pre-refresh-check:
47+
description: Check if charm is ready to refresh.
48+
49+
resume-refresh:
50+
description: Refresh remaining units (after you manually verified that refreshed units are healthy).
51+
params:
52+
force:
53+
type: boolean
54+
default: false
55+
description: |
56+
Potential of *data loss* and *downtime*
57+
58+
Force refresh of next unit.
59+
60+
Use to
61+
- force incompatible refresh and/or
62+
- continue refresh if 1+ refreshed units have non-active status

charm_version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1

charmcraft.yaml

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,19 @@
22
# See LICENSE file for licensing details.
33

44
type: "charm"
5+
bases:
6+
- build-on:
7+
- name: "ubuntu"
8+
channel: "22.04"
9+
run-on:
10+
- name: "ubuntu"
11+
channel: "22.04"
512
parts:
613
charm:
14+
charm-strict-dependencies: true
15+
override-build: |
16+
rustup default stable
17+
craftctl default
718
build-snaps:
819
- rustup
920
build-packages:
@@ -17,18 +28,13 @@ parts:
1728
echo 'ERROR: Use "tox run -e build-dev" instead of calling "charmcraft pack" directly' >&2
1829
exit 1
1930
fi
20-
override-build: |
21-
rustup default stable
22-
craftctl default
2331
files:
2432
plugin: dump
2533
source: .
2634
prime:
2735
- charm_internal_version
28-
bases:
29-
- build-on:
30-
- name: "ubuntu"
31-
channel: "22.04"
32-
run-on:
33-
- name: "ubuntu"
34-
channel: "22.04"
36+
- charm_version
37+
- workload_version
38+
override-build: |
39+
rustup default stable
40+
craftctl default

lib/charms/mongodb/v1/mongodb_backups.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
# Increment this PATCH version before using `charmcraft publish-lib` or reset
4343
# to 0 if you are raising the major API version
44-
LIBPATCH = 5
44+
LIBPATCH = 6
4545

4646
logger = logging.getLogger(__name__)
4747

@@ -800,7 +800,7 @@ def get_backup_error_status(self, backup_id: str) -> str:
800800
"""Get the error status for a provided backup."""
801801
pbm_status = self.charm.run_pbm_command(["status", "--out=json"])
802802
pbm_status = json.loads(pbm_status)
803-
backups = pbm_status["backups"].get("snapshot", [])
803+
backups = pbm_status["backups"].get("snapshot") or []
804804
for backup in backups:
805805
if backup_id == backup["name"]:
806806
return backup.get("error", "")

lib/charms/mongodb/v1/mongodb_provider.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616
from charms.data_platform_libs.v0.data_interfaces import DatabaseProvides
1717
from charms.mongodb.v0.mongo import MongoConfiguration, MongoConnection
1818
from charms.mongodb.v1.helpers import generate_password
19-
from ops.charm import CharmBase, EventBase, RelationBrokenEvent, RelationChangedEvent
19+
from ops.charm import (
20+
CharmBase,
21+
EventBase,
22+
RelationBrokenEvent,
23+
RelationChangedEvent,
24+
RelationEvent,
25+
)
2026
from ops.framework import Object
2127
from ops.model import Relation
2228
from pymongo.errors import PyMongoError
@@ -31,7 +37,7 @@
3137

3238
# Increment this PATCH version before using `charmcraft publish-lib` or reset
3339
# to 0 if you are raising the major API version
34-
LIBPATCH = 13
40+
LIBPATCH = 15
3541

3642
logger = logging.getLogger(__name__)
3743
REL_NAME = "database"
@@ -88,6 +94,11 @@ def pass_sanity_hook_checks(self) -> bool:
8894
if not self.charm.db_initialised:
8995
return False
9096

97+
# Warning: the sanity_hook_checks can pass when the call is
98+
# issued by a config-sever because the config-server is allowed to manage the users
99+
# in MongoDB. This is not well named and does not protect integration of a config-server
100+
# to a client application. The mongos charm however doesn't care
101+
# because it supports only one integration that uses MongoDBProvider.
91102
if not self.charm.is_role(Config.Role.MONGOS) and not self.charm.is_relation_feasible(
92103
self.get_relation_name()
93104
):
@@ -99,8 +110,14 @@ def pass_sanity_hook_checks(self) -> bool:
99110

100111
return True
101112

102-
def pass_hook_checks(self, event: EventBase) -> bool:
113+
def pass_hook_checks(self, event: RelationEvent) -> bool:
103114
"""Runs the pre-hooks checks for MongoDBProvider, returns True if all pass."""
115+
# First, ensure that the relation is valid, useless to do anything else otherwise
116+
if not self.charm.is_role(Config.Role.MONGOS) and not self.charm.is_relation_feasible(
117+
event.relation.name
118+
):
119+
return False
120+
104121
if not self.pass_sanity_hook_checks():
105122
return False
106123

@@ -372,6 +389,7 @@ def _get_config(
372389
mongo_args["port"] = Config.MONGOS_PORT
373390
if self.substrate == Config.Substrate.K8S:
374391
mongo_args["hosts"] = self.charm.get_mongos_hosts_for_client()
392+
mongo_args["port"] = self.charm.get_mongos_port()
375393
else:
376394
mongo_args["replset"] = self.charm.app.name
377395

lib/charms/tls_certificates_interface/v3/tls_certificates.py

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
305305
ModelError,
306306
Relation,
307307
RelationDataContent,
308+
Secret,
308309
SecretNotFoundError,
309310
Unit,
310311
)
@@ -317,7 +318,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
317318

318319
# Increment this PATCH version before using `charmcraft publish-lib` or reset
319320
# to 0 if you are raising the major API version
320-
LIBPATCH = 17
321+
LIBPATCH = 20
321322

322323
PYDEPS = ["cryptography", "jsonschema"]
323324

@@ -735,16 +736,16 @@ def calculate_expiry_notification_time(
735736
"""
736737
if provider_recommended_notification_time is not None:
737738
provider_recommended_notification_time = abs(provider_recommended_notification_time)
738-
provider_recommendation_time_delta = (
739-
expiry_time - timedelta(hours=provider_recommended_notification_time)
739+
provider_recommendation_time_delta = expiry_time - timedelta(
740+
hours=provider_recommended_notification_time
740741
)
741742
if validity_start_time < provider_recommendation_time_delta:
742743
return provider_recommendation_time_delta
743744

744745
if requirer_recommended_notification_time is not None:
745746
requirer_recommended_notification_time = abs(requirer_recommended_notification_time)
746-
requirer_recommendation_time_delta = (
747-
expiry_time - timedelta(hours=requirer_recommended_notification_time)
747+
requirer_recommendation_time_delta = expiry_time - timedelta(
748+
hours=requirer_recommended_notification_time
748749
)
749750
if validity_start_time < requirer_recommendation_time_delta:
750751
return requirer_recommendation_time_delta
@@ -1448,18 +1449,31 @@ def _revoke_certificates_for_which_no_csr_exists(self, relation_id: int) -> None
14481449
Returns:
14491450
None
14501451
"""
1451-
provider_certificates = self.get_provider_certificates(relation_id)
1452-
requirer_csrs = self.get_requirer_csrs(relation_id)
1452+
provider_certificates = self.get_unsolicited_certificates(relation_id=relation_id)
1453+
for provider_certificate in provider_certificates:
1454+
self.on.certificate_revocation_request.emit(
1455+
certificate=provider_certificate.certificate,
1456+
certificate_signing_request=provider_certificate.csr,
1457+
ca=provider_certificate.ca,
1458+
chain=provider_certificate.chain,
1459+
)
1460+
self.remove_certificate(certificate=provider_certificate.certificate)
1461+
1462+
def get_unsolicited_certificates(
1463+
self, relation_id: Optional[int] = None
1464+
) -> List[ProviderCertificate]:
1465+
"""Return provider certificates for which no certificate requests exists.
1466+
1467+
Those certificates should be revoked.
1468+
"""
1469+
unsolicited_certificates: List[ProviderCertificate] = []
1470+
provider_certificates = self.get_provider_certificates(relation_id=relation_id)
1471+
requirer_csrs = self.get_requirer_csrs(relation_id=relation_id)
14531472
list_of_csrs = [csr.csr for csr in requirer_csrs]
14541473
for certificate in provider_certificates:
14551474
if certificate.csr not in list_of_csrs:
1456-
self.on.certificate_revocation_request.emit(
1457-
certificate=certificate.certificate,
1458-
certificate_signing_request=certificate.csr,
1459-
ca=certificate.ca,
1460-
chain=certificate.chain,
1461-
)
1462-
self.remove_certificate(certificate=certificate.certificate)
1475+
unsolicited_certificates.append(certificate)
1476+
return unsolicited_certificates
14631477

14641478
def get_outstanding_certificate_requests(
14651479
self, relation_id: Optional[int] = None
@@ -1877,8 +1891,7 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None:
18771891
"Removing secret with label %s",
18781892
f"{LIBID}-{csr_in_sha256_hex}",
18791893
)
1880-
secret = self.model.get_secret(
1881-
label=f"{LIBID}-{csr_in_sha256_hex}")
1894+
secret = self.model.get_secret(label=f"{LIBID}-{csr_in_sha256_hex}")
18821895
secret.remove_all_revisions()
18831896
self.on.certificate_invalidated.emit(
18841897
reason="revoked",
@@ -1966,9 +1979,10 @@ def _on_secret_expired(self, event: SecretExpiredEvent) -> None:
19661979
Args:
19671980
event (SecretExpiredEvent): Juju event
19681981
"""
1969-
if not event.secret.label or not event.secret.label.startswith(f"{LIBID}-"):
1982+
csr = self._get_csr_from_secret(event.secret)
1983+
if not csr:
1984+
logger.error("Failed to get CSR from secret %s", event.secret.label)
19701985
return
1971-
csr = event.secret.get_content()["csr"]
19721986
provider_certificate = self._find_certificate_in_relation_data(csr)
19731987
if not provider_certificate:
19741988
# A secret expired but we did not find matching certificate. Cleaning up
@@ -2008,3 +2022,18 @@ def _find_certificate_in_relation_data(self, csr: str) -> Optional[ProviderCerti
20082022
continue
20092023
return provider_certificate
20102024
return None
2025+
2026+
def _get_csr_from_secret(self, secret: Secret) -> str:
2027+
"""Extract the CSR from the secret label or content.
2028+
2029+
This function is a workaround to maintain backwards compatibility
2030+
and fix the issue reported in
2031+
https://github.com/canonical/tls-certificates-interface/issues/228
2032+
"""
2033+
if not (csr := secret.get_content().get("csr", "")):
2034+
# In versions <14 of the Lib we were storing the CSR in the label of the secret
2035+
# The CSR now is stored int the content of the secret, which was a breaking change
2036+
# Here we get the CSR if the secret was created by an app using libpatch 14 or lower
2037+
if secret.label and secret.label.startswith(f"{LIBID}-"):
2038+
csr = secret.label[len(f"{LIBID}-") :]
2039+
return csr

metadata.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ website:
2121
peers:
2222
database-peers:
2323
interface: mongodb-peers
24+
upgrade-version-a:
25+
interface: upgrade
26+
2427
provides:
2528
database:
2629
interface: mongodb_client

poetry.lock

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ pyyaml = "^6.0.1"
2222
jinja2 = "^3.1.3"
2323
poetry-core = "^1.9.0"
2424
data-platform-helpers = "^0.1.3"
25+
overrides = "^7.7.0"
26+
lightkube = "^0.15.3"
2527
pyOpenSSL = "^24.2.1"
2628
setuptools = "^72.0.0"
27-
lightkube = "^0.15.3"
2829

2930
[tool.poetry.group.charm-libs.dependencies]
3031
ops = "~2.15.0"

0 commit comments

Comments
 (0)