Make tcpsocket readiness and liveness configurable 86/13086/9
authorGuillermo Calvino <guillermo.calvino@canonical.com>
Thu, 23 Mar 2023 12:40:33 +0000 (13:40 +0100)
committerGuillermo Calvino <guillermo.calvino@canonical.com>
Wed, 5 Apr 2023 14:41:45 +0000 (16:41 +0200)
Change-Id: If0c466aa5a0b64fff0ed7cee037bd3cf8672ec46
Signed-off-by: Guillermo Calvino <guillermo.calvino@canonical.com>
installers/charm/kafka-exporter/src/charm.py
installers/charm/keystone/src/charm.py
installers/charm/lcm/src/charm.py
installers/charm/mon/src/charm.py
installers/charm/nbi/config.yaml
installers/charm/nbi/src/charm.py
installers/charm/nbi/tests/test_charm.py
installers/charm/pla/src/charm.py
installers/charm/pol/src/charm.py
installers/charm/prometheus/charmcraft.yaml
installers/charm/prometheus/src/charm.py

index ec6eaab..07a854f 100755 (executable)
@@ -96,7 +96,6 @@ class KafkaEndpoint:
 
 
 class KafkaExporterCharm(CharmedOsmBase):
-
     on = KafkaEvents()
 
     def __init__(self, *args) -> NoReturn:
index 39a7a00..446d2e0 100755 (executable)
@@ -248,9 +248,9 @@ class KeystoneCharm(CharmedOsmBase):
     ):
         credentials_files_builder = FilesV3Builder()
         fernet_files_builder = FilesV3Builder()
-        for (key_id, _) in enumerate(credential_keys):
+        for key_id, _ in enumerate(credential_keys):
             credentials_files_builder.add_file(str(key_id), str(key_id), secret=True)
-        for (key_id, _) in enumerate(fernet_keys):
+        for key_id, _ in enumerate(fernet_keys):
             fernet_files_builder.add_file(str(key_id), str(key_id), secret=True)
         return credentials_files_builder.build(), fernet_files_builder.build()
 
index 7c64418..5319763 100755 (executable)
@@ -140,7 +140,6 @@ class ConfigModel(ModelValidator):
 
 
 class LcmCharm(CharmedOsmBase):
-
     on = KafkaEvents()
 
     def __init__(self, *args) -> NoReturn:
index d04779a..6cb7745 100755 (executable)
@@ -125,7 +125,6 @@ class ConfigModel(ModelValidator):
 
 
 class MonCharm(CharmedOsmBase):
-
     on = KafkaEvents()
 
     def __init__(self, *args) -> NoReturn:
index f10304f..eabd968 100644 (file)
@@ -107,3 +107,31 @@ options:
     description: Enables the security context of the pods
     type: boolean
     default: false
+  tcpsocket_readiness_probe:
+    description: |
+      Configuration of tcpsocket readiness probe.
+
+      Default values (when configuration is empty):
+        initial_delay_seconds = 5
+        timeout_seconds = 5
+        period_seconds = 10
+        success_threshold = 1
+        failure_threshold = 3
+
+      Example of configuration:
+        juju config nbi tcpsocket_readiness_probe='{"failure_threshold": 7}'
+    type: string
+  tcpsocket_liveness_probe:
+    description: |
+      Configuration of tcpsocket liveness probe.
+
+      Default values (when configuration is empty):
+        initial_delay_seconds = 5
+        timeout_seconds = 5
+        period_seconds = 10
+        success_threshold = 1
+        failure_threshold = 3
+
+      Example of configuration:
+        juju config nbi tcpsocket_liveness_probe='{"initial_delay_seconds": 10}'
+    type: string
index 4aaecb9..e120756 100755 (executable)
@@ -24,6 +24,7 @@
 
 
 from ipaddress import ip_network
+import json
 import logging
 from typing import NoReturn, Optional
 from urllib.parse import urlparse
@@ -65,6 +66,8 @@ class ConfigModel(ModelValidator):
     image_pull_policy: str
     debug_mode: bool
     security_context: bool
+    tcpsocket_liveness_probe: Optional[str]
+    tcpsocket_readiness_probe: Optional[str]
 
     @validator("auth_backend")
     def validate_auth_backend(cls, v):
@@ -116,9 +119,40 @@ class ConfigModel(ModelValidator):
             raise ValueError("value must be always, ifnotpresent or never")
         return values[v]
 
+    @staticmethod
+    def _validate_tcpsocket_probe(probe_str) -> dict:
+        valid_attributes = (
+            "initial_delay_seconds",
+            "timeout_seconds",
+            "period_seconds",
+            "success_threshold",
+            "failure_threshold",
+        )
+        if probe_str:
+            probe_dict = json.loads(probe_str)
+            if all(attribute in valid_attributes for attribute in probe_dict):
+                return probe_dict
+            raise ValueError(
+                "One or more attributes are not accepted by the tcpsocket probe configuration"
+            )
+        return {}
 
-class NbiCharm(CharmedOsmBase):
+    @validator("tcpsocket_readiness_probe")
+    def validate_tcpsocket_readiness_probe(cls, v):
+        try:
+            return ConfigModel._validate_tcpsocket_probe(v)
+        except Exception as e:
+            raise ValueError(f"tcpsocket_readiness_probe configuration error: {e}")
+
+    @validator("tcpsocket_liveness_probe")
+    def validate_tcpsocket_liveness_probe(cls, v):
+        try:
+            return ConfigModel._validate_tcpsocket_probe(v)
+        except Exception as e:
+            raise ValueError(f"tcpsocket_liveness_probe configuration error: {e}")
 
+
+class NbiCharm(CharmedOsmBase):
     on = KafkaEvents()
 
     def __init__(self, *args) -> NoReturn:
@@ -193,7 +227,6 @@ class NbiCharm(CharmedOsmBase):
     def build_pod_spec(self, image_info):
         # Validate config
         config = ConfigModel(**dict(self.config))
-
         if config.mongodb_uri and not self.mongodb_client.is_missing_data_in_unit():
             raise Exception("Mongodb data cannot be provided via config and relation")
 
@@ -242,13 +275,31 @@ class NbiCharm(CharmedOsmBase):
         container_builder.add_port(name=self.app.name, port=PORT)
         container_builder.add_tcpsocket_readiness_probe(
             PORT,
-            initial_delay_seconds=5,
-            timeout_seconds=5,
+            initial_delay_seconds=config.tcpsocket_readiness_probe.get(
+                "initial_delay_seconds", 5
+            ),
+            timeout_seconds=config.tcpsocket_readiness_probe.get("timeout_seconds", 5),
+            period_seconds=config.tcpsocket_readiness_probe.get("period_seconds", 10),
+            success_threshold=config.tcpsocket_readiness_probe.get(
+                "success_threshold", 1
+            ),
+            failure_threshold=config.tcpsocket_readiness_probe.get(
+                "failure_threshold", 3
+            ),
         )
         container_builder.add_tcpsocket_liveness_probe(
             PORT,
-            initial_delay_seconds=45,
-            timeout_seconds=10,
+            initial_delay_seconds=config.tcpsocket_liveness_probe.get(
+                "initial_delay_seconds", 45
+            ),
+            timeout_seconds=config.tcpsocket_liveness_probe.get("timeout_seconds", 10),
+            period_seconds=config.tcpsocket_liveness_probe.get("period_seconds", 10),
+            success_threshold=config.tcpsocket_liveness_probe.get(
+                "success_threshold", 1
+            ),
+            failure_threshold=config.tcpsocket_liveness_probe.get(
+                "failure_threshold", 3
+            ),
         )
         container_builder.add_envs(
             {
index 92c2980..59a27fb 100644 (file)
@@ -155,6 +155,42 @@ class TestCharm(unittest.TestCase):
         # Verifying status
         self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)
 
+    def test_config_liveness_probe_with_relations(
+        self,
+    ) -> NoReturn:
+        "Test configuration of liveness probe with relations"
+        self.initialize_nbi_with_keystone()
+        self.config_liveness_probe()
+        # Verifying status
+        self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus)
+
+    def test_wrong_config_liveness_probe_with_relations(
+        self,
+    ) -> NoReturn:
+        "Test wrong configuration of liveness probe with relations"
+        self.initialize_nbi_with_keystone()
+        self.wrong_config_liveness_probe()
+        # Verifying status
+        self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)
+
+    def test_config_readiness_probe_with_relations(
+        self,
+    ) -> NoReturn:
+        "Test configuration of readiness probe with relations"
+        self.initialize_nbi_with_keystone()
+        self.config_readiness_probe()
+        # Verifying status
+        self.assertIsInstance(self.harness.charm.unit.status, ActiveStatus)
+
+    def test_wrong_config_readiness_probe_with_relations(
+        self,
+    ) -> NoReturn:
+        "Test wrong configuration of readiness probe with relations"
+        self.initialize_nbi_with_keystone()
+        self.wrong_config_readiness_probe()
+        # Verifying status
+        self.assertIsInstance(self.harness.charm.unit.status, BlockedStatus)
+
     def initialize_kafka_relation(self):
         kafka_relation_id = self.harness.add_relation("kafka", "kafka")
         self.harness.add_relation_unit(kafka_relation_id, "kafka/0")
@@ -165,6 +201,33 @@ class TestCharm(unittest.TestCase):
     def initialize_mongo_config(self):
         self.harness.update_config({"mongodb_uri": "mongodb://mongo:27017"})
 
+    def initialize_nbi_with_keystone(self):
+        self.harness.update_config({"auth_backend": "keystone"})
+        self.initialize_kafka_relation()
+        self.initialize_mongo_relation()
+        self.initialize_prometheus_relation()
+        self.initialize_keystone_relation()
+
+    def config_liveness_probe(self):
+        self.harness.update_config(
+            {"tcpsocket_liveness_probe": '{"initial_delay_seconds": 10}'}
+        )
+
+    def wrong_config_liveness_probe(self):
+        self.harness.update_config(
+            {"tcpsocket_liveness_probe": "{initial_delay_seconds: 10}"}
+        )
+
+    def wrong_config_readiness_probe(self):
+        self.harness.update_config(
+            {"tcpsocket_readiness_probe": "{initial_delay_seconds: 10}"}
+        )
+
+    def config_readiness_probe(self):
+        self.harness.update_config(
+            {"tcpsocket_readiness_probe": '{"initial_delay_seconds": 10}'}
+        )
+
     def initialize_mongo_relation(self):
         mongodb_relation_id = self.harness.add_relation("mongodb", "mongodb")
         self.harness.add_relation_unit(mongodb_relation_id, "mongodb/0")
index 36a11dc..d907f0b 100755 (executable)
@@ -76,7 +76,6 @@ class ConfigModel(ModelValidator):
 
 
 class PlaCharm(CharmedOsmBase):
-
     on = KafkaEvents()
 
     def __init__(self, *args) -> NoReturn:
index 7b92b45..94f6ecb 100755 (executable)
@@ -87,7 +87,6 @@ class ConfigModel(ModelValidator):
 
 
 class PolCharm(CharmedOsmBase):
-
     on = KafkaEvents()
 
     def __init__(self, *args) -> NoReturn:
index 87d0463..d677a76 100644 (file)
@@ -34,8 +34,7 @@ bases:
           - arm64
 parts:
   charm:
+    charm-binary-python-packages: [cryptography, bcrypt]
     build-packages:
-      - cargo
       - git
       - libffi-dev
-      - rustc
index af39a13..07793e0 100755 (executable)
@@ -107,7 +107,6 @@ class ConfigModel(ModelValidator):
 
 
 class PrometheusCharm(CharmedOsmBase):
-
     """Prometheus Charm."""
 
     def __init__(self, *args) -> NoReturn: