Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Remove defer #76

Merged
merged 7 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 58 additions & 65 deletions src/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@
from charms.data_platform_libs.v0.data_interfaces import DatabaseRequires # type: ignore[import]
from charms.sdcore_nrf_k8s.v0.fiveg_nrf import NRFProvides # type: ignore[import]
from charms.tls_certificates_interface.v3.tls_certificates import ( # type: ignore[import]
CertificateAvailableEvent,
CertificateExpiringEvent,
TLSCertificatesRequiresV3,
generate_csr,
generate_private_key,
)
from jinja2 import Environment, FileSystemLoader # type: ignore[import]
from ops.charm import CharmBase, EventBase, RelationJoinedEvent
from ops.charm import CharmBase, RelationJoinedEvent
from ops.framework import EventBase
from ops.main import main
from ops.model import ActiveStatus, BlockedStatus, ModelError, WaitingStatus
from ops.pebble import Layer
Expand Down Expand Up @@ -110,67 +110,62 @@ def __init__(self, *args):
self.framework.observe(
self.on.fiveg_nrf_relation_joined, self._on_fiveg_nrf_relation_joined
)
self.framework.observe(
self.on.certificates_relation_created, self._on_certificates_relation_created
)
self.framework.observe(
self.on.certificates_relation_joined, self._on_certificates_relation_joined
)
self.framework.observe(self.on.certificates_relation_joined, self._configure_nrf)
self.framework.observe(self._certificates.on.certificate_available, self._configure_nrf)
self.framework.observe(
self.on.certificates_relation_broken, self._on_certificates_relation_broken
)
self.framework.observe(
self._certificates.on.certificate_available, self._on_certificate_available
)
self.framework.observe(
self._certificates.on.certificate_expiring, self._on_certificate_expiring
)

def _configure_nrf(self, event: EventBase) -> None:
"""Adds pebble layer and manages Juju unit status.

Args:
event: Juju event
"""
def ready_to_configure(self) -> bool:
"""Returns whether all preconditions are met to proceed with configuration."""
if not self._container.can_connect():
self.unit.status = WaitingStatus("Waiting for container to be ready")
event.defer()
return
return False
for relation in [DATABASE_RELATION_NAME, "certificates"]:
if not self._relation_created(relation):
self.unit.status = BlockedStatus(f"Waiting for {relation} relation to be created")
return
return False
if not self._database_is_available():
self.unit.status = WaitingStatus("Waiting for the database to be available")
return
return False
if not self._get_database_uri():
self.unit.status = WaitingStatus("Waiting for database URI")
event.defer()
return
if not self._container.exists(path=BASE_CONFIG_PATH):
return False
if not self._container.exists(path=BASE_CONFIG_PATH) or not self._container.exists(
path=CERTS_DIR_PATH
):
self.unit.status = WaitingStatus("Waiting for storage to be attached")
event.defer()
return
return False
if not _get_pod_ip():
self.unit.status = WaitingStatus("Waiting for pod IP address to be available")
event.defer()
return False
return True

def _configure_nrf(self, event: EventBase) -> None:
"""Adds pebble layer and manages Juju unit status.

Args:
event: Juju event
"""
if not self.ready_to_configure():
return
if not self._certificate_is_stored():
if not self._private_key_is_stored():
self._generate_private_key()
if not self._csr_is_stored():
self._request_new_certificate()
provider_certificate = self._get_current_provider_certificate()
if not provider_certificate:
self.unit.status = WaitingStatus("Waiting for certificates to be stored")
event.defer()
return
needs_restart = self._generate_config_file()
self._configure_workload(restart=needs_restart)
certificate_changed = self._update_certificate(provider_certificate=provider_certificate)
config_file_changed = self._generate_config_file()
self._configure_workload(restart=(config_file_changed or certificate_changed))
self._publish_nrf_info_for_all_requirers()
self.unit.status = ActiveStatus()

def _on_certificates_relation_created(self, event: EventBase) -> None:
"""Generates Private key."""
if not self._container.can_connect():
event.defer()
return
self._generate_private_key()

def _on_certificates_relation_broken(self, event: EventBase) -> None:
"""Deletes TLS related artifacts and reconfigures workload."""
if not self._container.can_connect():
Expand All @@ -181,33 +176,6 @@ def _on_certificates_relation_broken(self, event: EventBase) -> None:
self._delete_certificate()
self.unit.status = BlockedStatus("Waiting for certificates relation to be created")

def _on_certificates_relation_joined(self, event: EventBase) -> None:
"""Generates CSR and requests new certificate."""
if not self._container.can_connect():
event.defer()
return
if not self._private_key_is_stored():
event.defer()
return
if self._certificate_is_stored():
return

self._request_new_certificate()

def _on_certificate_available(self, event: CertificateAvailableEvent) -> None:
"""Pushes certificate to workload and configures workload."""
if not self._container.can_connect():
event.defer()
return
if not self._csr_is_stored():
logger.warning("Certificate is available but no CSR is stored")
return
if event.certificate_signing_request != self._get_stored_csr():
logger.debug("Stored CSR doesn't match one in certificate available event")
return
self._store_certificate(event.certificate)
self._configure_nrf(event)

def _on_certificate_expiring(self, event: CertificateExpiringEvent) -> None:
"""Requests new certificate."""
if not self._container.can_connect():
Expand All @@ -226,6 +194,31 @@ def _on_database_relation_broken(self, event: EventBase) -> None:
"""
self.unit.status = BlockedStatus("Waiting for database relation")

def _get_current_provider_certificate(self) -> str | None:
"""Compares the current certificate request to what is in the interface.

Returns The current valid provider certificate if present
"""
csr = self._get_stored_csr()
for provider_certificate in self._certificates.get_assigned_certificates():
if provider_certificate.csr == csr:
return provider_certificate.certificate
return None

def _update_certificate(self, provider_certificate) -> bool:
"""Compares the provided certificate to what is stored.

Returns True if the certificate was updated
"""
existing_certificate = (
self._get_stored_certificate() if self._certificate_is_stored() else ""
)

if existing_certificate != provider_certificate:
self._store_certificate(certificate=provider_certificate)
return True
return False

def _generate_private_key(self) -> None:
"""Generates and stores private key."""
private_key = generate_private_key()
Expand Down
Loading
Loading