Skip to content
This repository has been archived by the owner on Nov 3, 2023. It is now read-only.

Commit

Permalink
Telco 192 fixing charms not getting back to active idle after juju re…
Browse files Browse the repository at this point in the history
…start pt2 (#43)

* Updating orc8r_base_db lib to propagate the fix across all affected charms

* Adding interfaces to the charms

* Fixing lint
  • Loading branch information
Gmerold authored Jun 3, 2022
1 parent bac337f commit 76a11f7
Show file tree
Hide file tree
Showing 49 changed files with 1,250 additions and 352 deletions.
2 changes: 2 additions & 0 deletions orchestrator-bundle/orc8r-accessd-operator/charmcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ parts:
charm:
build-packages:
- git
charm-binary-python-packages:
- psycopg2-binary
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
EOF
```
Then, to initialise the library:
For ClusterIP services:
```python
from charms.magma_orc8r_libs.v0.orc8r_base_db import Orc8rBase
from charms.observability_libs.v0.kubernetes_service_patch import KubernetesServicePatch
Expand All @@ -31,7 +30,7 @@
class MagmaOrc8rDirectorydCharm(CharmBase):
def __init__(self, *args):
super().__init__(*args)
self._service_patcher = KubernetesServicePatch(self, [("grpc", 9180, 9106)])
self._service_patcher = KubernetesServicePatch(self, [("grpc", 9180)])
startup_command = (
"/usr/bin/envdir "
"/var/opt/magma/envdir "
Expand All @@ -42,14 +41,31 @@ def __init__(self, *args):
self._orc8r_base = Orc8rBase(self, startup_command=startup_command)
```
Charms that leverage this library also need to specify a `provides` relation in their
`metadata.yaml` file. For example:
```yaml
provides:
magma-orc8r-directoryd:
interface: magma-orc8r-directoryd
```
"""

import logging

import ops.lib
import psycopg2 # type: ignore[import]
from ops.charm import CharmBase
from ops.framework import Object
from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus, WaitingStatus
from ops.model import (
ActiveStatus,
BlockedStatus,
MaintenanceStatus,
ModelError,
WaitingStatus,
)
from ops.pebble import Layer
from pgconnstr import ConnectionString # type: ignore[import]

Expand All @@ -61,7 +77,7 @@ def __init__(self, *args):

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


logger = logging.getLogger(__name__)
Expand All @@ -80,10 +96,16 @@ def __init__(
super().__init__(charm, "orc8r-base")
self.charm = charm
self.startup_command = startup_command
self._container_name = self._service_name = self.charm.meta.name
self._container = self.charm.unit.get_container(self._container_name)
self.container_name = self.service_name = self.charm.meta.name
self.container = self.charm.unit.get_container(self.container_name)
service_name_with_underscores = self.service_name.replace("-", "_")
provided_relation_name = list(self.charm.meta.provides.keys())[0]
provided_relation_name_with_underscores = provided_relation_name.replace("-", "_")
pebble_ready_event = getattr(
self.charm.on, f"{self._service_name.replace('-', '_')}_pebble_ready"
self.charm.on, f"{provided_relation_name_with_underscores}_pebble_ready"
)
relation_joined_event = getattr(
self.charm.on, f"{service_name_with_underscores}_relation_joined"
)
self.framework.observe(pebble_ready_event, self._on_magma_orc8r_pebble_ready)

Expand All @@ -92,10 +114,11 @@ def __init__(
else:
self.additional_environment_variables = {}

self._db = pgsql.PostgreSQLClient(self.charm, "db")
self.db = pgsql.PostgreSQLClient(self.charm, "db")
self.framework.observe(
self._db.on.database_relation_joined, self._on_database_relation_joined
self.db.on.database_relation_joined, self._on_database_relation_joined
)
self.framework.observe(relation_joined_event, self._on_relation_joined)

@property
def _db_relation_created(self) -> bool:
Expand All @@ -109,9 +132,9 @@ def _on_magma_orc8r_pebble_ready(self, event):
self.charm.unit.status = BlockedStatus("Waiting for database relation to be created")
event.defer()
return
if not self._db_relation_established():
if not self._db_relation_established:
self.charm.unit.status = WaitingStatus(
"Waiting for database relation to be established..."
"Waiting for database relation to be established"
)
event.defer()
return
Expand All @@ -121,14 +144,14 @@ def _configure_orc8r(self, event):
"""
Adds layer to pebble config if the proposed config is different from the current one
"""
if self._container.can_connect():
if self.container.can_connect():
self.charm.unit.status = MaintenanceStatus("Configuring pod")
pebble_layer = self._pebble_layer()
plan = self._container.get_plan()
plan = self.container.get_plan()
if plan.services != pebble_layer.services:
self._container.add_layer(self._container_name, pebble_layer, combine=True)
self._container.restart(self._service_name)
logger.info(f"Restarted container {self._service_name}")
self.container.add_layer(self.container_name, pebble_layer, combine=True)
self.container.restart(self.service_name)
logger.info(f"Restarted container {self.service_name}")
self.charm.unit.status = ActiveStatus()
else:
self.charm.unit.status = WaitingStatus("Waiting for container to be ready...")
Expand All @@ -138,12 +161,12 @@ def _pebble_layer(self) -> Layer:
"""Returns pebble layer for the charm."""
return Layer(
{
"summary": f"{self._service_name} layer",
"description": f"pebble config layer for {self._service_name}",
"summary": f"{self.service_name} layer",
"description": f"pebble config layer for {self.service_name}",
"services": {
self._service_name: {
self.service_name: {
"override": "replace",
"summary": self._service_name,
"summary": self.service_name,
"startup": "enabled",
"command": self.startup_command,
"environment": self._environment_variables,
Expand All @@ -166,12 +189,23 @@ def _on_database_relation_joined(self, event):
else:
event.defer()

def _db_relation_established(self):
"""Validates that database relation is ready (that there is a relation and that credentials
have been passed)."""
if not self._get_db_connection_string:
@property
def _db_relation_established(self) -> bool:
"""Validates that database relation is ready (that there is a relation, credentials have
been passed and the database can be connected to)."""
db_connection_string = self._get_db_connection_string
if not db_connection_string:
return False
try:
psycopg2.connect(
f"dbname='{self.DB_NAME}' "
f"user='{db_connection_string.user}' "
f"host='{db_connection_string.host}' "
f"password='{db_connection_string.password}'"
)
return True
except psycopg2.OperationalError:
return False
return True

@property
def _get_db_connection_string(self):
Expand All @@ -186,9 +220,9 @@ def _get_db_connection_string(self):
def _environment_variables(self):
environment_variables = {}
default_environment_variables = {
"SERVICE_HOSTNAME": self._container_name,
"SERVICE_HOSTNAME": self.container_name,
"SERVICE_REGISTRY_MODE": "k8s",
"SERVICE_REGISTRY_NAMESPACE": self._namespace,
"SERVICE_REGISTRY_NAMESPACE": self.namespace,
}
environment_variables.update(self.additional_environment_variables)
environment_variables.update(default_environment_variables)
Expand All @@ -200,11 +234,44 @@ def _environment_variables(self):
f"sslmode=disable",
"SQL_DRIVER": "postgres",
"SQL_DIALECT": "psql",
"SERVICE_HOSTNAME": self._container_name,
"SERVICE_HOSTNAME": self.container_name,
}
environment_variables.update(sql_environment_variables)
return environment_variables

@property
def _namespace(self) -> str:
def namespace(self) -> str:
return self.charm.model.name

def _update_relations(self):
if not self.charm.unit.is_leader():
return
relations = self.charm.model.relations[self.charm.meta.name]
for relation in relations:
self._update_relation_active_status(
relation=relation, is_active=self._service_is_running
)

def _on_relation_joined(self, event):
if not self.charm.unit.is_leader():
return
self._update_relation_active_status(
relation=event.relation, is_active=self._service_is_running
)

@property
def _service_is_running(self) -> bool:
if self.container.can_connect():
try:
self.container.get_service(self.service_name)
return True
except ModelError:
pass
return False

def _update_relation_active_status(self, relation, is_active: bool):
relation.data[self.charm.unit].update(
{
"active": str(is_active),
}
)
5 changes: 5 additions & 0 deletions orchestrator-bundle/orc8r-accessd-operator/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ resources:
description: OCI image for magma-orc8r-accessd
upstream-source: docker.artifactory.magmacore.org/controller:1.6.0

provides:
magma-orc8r-accessd:
interface: magma-orc8r-accessd
limit: 1

requires:
db:
interface: pgsql
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
git+https://github.com/canonical/operator/#egg=ops
ops-lib-pgsql
psycopg2-binary # Package only used for testing, charm uses charm-binary-python-packages from charmcraft.yaml
lightkube
lightkube-models
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ parts:
charm:
build-packages:
- git
charm-binary-python-packages:
- psycopg2-binary
Loading

0 comments on commit 76a11f7

Please sign in to comment.