Skip to content

Commit

Permalink
Merge branch '6/edge' into DPE-5661-safe-drainage
Browse files Browse the repository at this point in the history
  • Loading branch information
MiaAltieri committed Oct 17, 2024
2 parents 1e09c4e + f9224f3 commit 45912cf
Show file tree
Hide file tree
Showing 40 changed files with 1,593 additions and 205 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
- run: |
# Workaround for https://github.com/canonical/charmcraft/issues/1389#issuecomment-1880921728
touch requirements.txt
- name: Check libs
uses: canonical/charming-actions/[email protected]
with:
Expand Down
18 changes: 18 additions & 0 deletions actions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,21 @@ set-tls-private-key:
internal-key:
type: string
description: The content of private key for internal communications with clients. Content will be auto-generated if this option is not specified.

pre-refresh-check:
description: Check if charm is ready to refresh.

resume-refresh:
description: Refresh remaining units (after you manually verified that refreshed units are healthy).
params:
force:
type: boolean
default: false
description: |
Potential of *data loss* and *downtime*
Force refresh of next unit.
Use to
- force incompatible refresh and/or
- continue refresh if 1+ refreshed units have non-active status
1 change: 1 addition & 0 deletions charm_version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
26 changes: 16 additions & 10 deletions charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,19 @@
# See LICENSE file for licensing details.

type: "charm"
bases:
- build-on:
- name: "ubuntu"
channel: "22.04"
run-on:
- name: "ubuntu"
channel: "22.04"
parts:
charm:
charm-strict-dependencies: true
override-build: |
rustup default stable
craftctl default
build-snaps:
- rustup
build-packages:
Expand All @@ -17,18 +28,13 @@ parts:
echo 'ERROR: Use "tox run -e build-dev" instead of calling "charmcraft pack" directly' >&2
exit 1
fi
override-build: |
rustup default stable
craftctl default
files:
plugin: dump
source: .
prime:
- charm_internal_version
bases:
- build-on:
- name: "ubuntu"
channel: "22.04"
run-on:
- name: "ubuntu"
channel: "22.04"
- charm_version
- workload_version
override-build: |
rustup default stable
craftctl default
4 changes: 2 additions & 2 deletions lib/charms/mongodb/v1/mongodb_backups.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

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

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -800,7 +800,7 @@ def get_backup_error_status(self, backup_id: str) -> str:
"""Get the error status for a provided backup."""
pbm_status = self.charm.run_pbm_command(["status", "--out=json"])
pbm_status = json.loads(pbm_status)
backups = pbm_status["backups"].get("snapshot", [])
backups = pbm_status["backups"].get("snapshot") or []
for backup in backups:
if backup_id == backup["name"]:
return backup.get("error", "")
Expand Down
24 changes: 21 additions & 3 deletions lib/charms/mongodb/v1/mongodb_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
from charms.data_platform_libs.v0.data_interfaces import DatabaseProvides
from charms.mongodb.v0.mongo import MongoConfiguration, MongoConnection
from charms.mongodb.v1.helpers import generate_password
from ops.charm import CharmBase, EventBase, RelationBrokenEvent, RelationChangedEvent
from ops.charm import (
CharmBase,
EventBase,
RelationBrokenEvent,
RelationChangedEvent,
RelationEvent,
)
from ops.framework import Object
from ops.model import Relation
from pymongo.errors import PyMongoError
Expand All @@ -31,7 +37,7 @@

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

logger = logging.getLogger(__name__)
REL_NAME = "database"
Expand Down Expand Up @@ -88,6 +94,11 @@ def pass_sanity_hook_checks(self) -> bool:
if not self.charm.db_initialised:
return False

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

return True

def pass_hook_checks(self, event: EventBase) -> bool:
def pass_hook_checks(self, event: RelationEvent) -> bool:
"""Runs the pre-hooks checks for MongoDBProvider, returns True if all pass."""
# First, ensure that the relation is valid, useless to do anything else otherwise
if not self.charm.is_role(Config.Role.MONGOS) and not self.charm.is_relation_feasible(
event.relation.name
):
return False

if not self.pass_sanity_hook_checks():
return False

Expand Down Expand Up @@ -372,6 +389,7 @@ def _get_config(
mongo_args["port"] = Config.MONGOS_PORT
if self.substrate == Config.Substrate.K8S:
mongo_args["hosts"] = self.charm.get_mongos_hosts_for_client()
mongo_args["port"] = self.charm.get_mongos_port()
else:
mongo_args["replset"] = self.charm.app.name

Expand Down
65 changes: 47 additions & 18 deletions lib/charms/tls_certificates_interface/v3/tls_certificates.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven
ModelError,
Relation,
RelationDataContent,
Secret,
SecretNotFoundError,
Unit,
)
Expand All @@ -317,7 +318,7 @@ def _on_all_certificates_invalidated(self, event: AllCertificatesInvalidatedEven

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

PYDEPS = ["cryptography", "jsonschema"]

Expand Down Expand Up @@ -735,16 +736,16 @@ def calculate_expiry_notification_time(
"""
if provider_recommended_notification_time is not None:
provider_recommended_notification_time = abs(provider_recommended_notification_time)
provider_recommendation_time_delta = (
expiry_time - timedelta(hours=provider_recommended_notification_time)
provider_recommendation_time_delta = expiry_time - timedelta(
hours=provider_recommended_notification_time
)
if validity_start_time < provider_recommendation_time_delta:
return provider_recommendation_time_delta

if requirer_recommended_notification_time is not None:
requirer_recommended_notification_time = abs(requirer_recommended_notification_time)
requirer_recommendation_time_delta = (
expiry_time - timedelta(hours=requirer_recommended_notification_time)
requirer_recommendation_time_delta = expiry_time - timedelta(
hours=requirer_recommended_notification_time
)
if validity_start_time < requirer_recommendation_time_delta:
return requirer_recommendation_time_delta
Expand Down Expand Up @@ -1448,18 +1449,31 @@ def _revoke_certificates_for_which_no_csr_exists(self, relation_id: int) -> None
Returns:
None
"""
provider_certificates = self.get_provider_certificates(relation_id)
requirer_csrs = self.get_requirer_csrs(relation_id)
provider_certificates = self.get_unsolicited_certificates(relation_id=relation_id)
for provider_certificate in provider_certificates:
self.on.certificate_revocation_request.emit(
certificate=provider_certificate.certificate,
certificate_signing_request=provider_certificate.csr,
ca=provider_certificate.ca,
chain=provider_certificate.chain,
)
self.remove_certificate(certificate=provider_certificate.certificate)

def get_unsolicited_certificates(
self, relation_id: Optional[int] = None
) -> List[ProviderCertificate]:
"""Return provider certificates for which no certificate requests exists.
Those certificates should be revoked.
"""
unsolicited_certificates: List[ProviderCertificate] = []
provider_certificates = self.get_provider_certificates(relation_id=relation_id)
requirer_csrs = self.get_requirer_csrs(relation_id=relation_id)
list_of_csrs = [csr.csr for csr in requirer_csrs]
for certificate in provider_certificates:
if certificate.csr not in list_of_csrs:
self.on.certificate_revocation_request.emit(
certificate=certificate.certificate,
certificate_signing_request=certificate.csr,
ca=certificate.ca,
chain=certificate.chain,
)
self.remove_certificate(certificate=certificate.certificate)
unsolicited_certificates.append(certificate)
return unsolicited_certificates

def get_outstanding_certificate_requests(
self, relation_id: Optional[int] = None
Expand Down Expand Up @@ -1877,8 +1891,7 @@ def _on_relation_changed(self, event: RelationChangedEvent) -> None:
"Removing secret with label %s",
f"{LIBID}-{csr_in_sha256_hex}",
)
secret = self.model.get_secret(
label=f"{LIBID}-{csr_in_sha256_hex}")
secret = self.model.get_secret(label=f"{LIBID}-{csr_in_sha256_hex}")
secret.remove_all_revisions()
self.on.certificate_invalidated.emit(
reason="revoked",
Expand Down Expand Up @@ -1966,9 +1979,10 @@ def _on_secret_expired(self, event: SecretExpiredEvent) -> None:
Args:
event (SecretExpiredEvent): Juju event
"""
if not event.secret.label or not event.secret.label.startswith(f"{LIBID}-"):
csr = self._get_csr_from_secret(event.secret)
if not csr:
logger.error("Failed to get CSR from secret %s", event.secret.label)
return
csr = event.secret.get_content()["csr"]
provider_certificate = self._find_certificate_in_relation_data(csr)
if not provider_certificate:
# A secret expired but we did not find matching certificate. Cleaning up
Expand Down Expand Up @@ -2008,3 +2022,18 @@ def _find_certificate_in_relation_data(self, csr: str) -> Optional[ProviderCerti
continue
return provider_certificate
return None

def _get_csr_from_secret(self, secret: Secret) -> str:
"""Extract the CSR from the secret label or content.
This function is a workaround to maintain backwards compatibility
and fix the issue reported in
https://github.com/canonical/tls-certificates-interface/issues/228
"""
if not (csr := secret.get_content().get("csr", "")):
# In versions <14 of the Lib we were storing the CSR in the label of the secret
# The CSR now is stored int the content of the secret, which was a breaking change
# Here we get the CSR if the secret was created by an app using libpatch 14 or lower
if secret.label and secret.label.startswith(f"{LIBID}-"):
csr = secret.label[len(f"{LIBID}-") :]
return csr
3 changes: 3 additions & 0 deletions metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ website:
peers:
database-peers:
interface: mongodb-peers
upgrade-version-a:
interface: upgrade

provides:
database:
interface: mongodb_client
Expand Down
13 changes: 12 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ pyyaml = "^6.0.1"
jinja2 = "^3.1.3"
poetry-core = "^1.9.0"
data-platform-helpers = "^0.1.3"
overrides = "^7.7.0"
lightkube = "^0.15.3"
pyOpenSSL = "^24.2.1"
setuptools = "^72.0.0"
lightkube = "^0.15.3"

[tool.poetry.group.charm-libs.dependencies]
ops = "~2.15.0"
Expand Down
Loading

0 comments on commit 45912cf

Please sign in to comment.