Removing pydantic from NBI charm
Change-Id: I725aabf1da99751dae0812305e01d92b801653ee
Signed-off-by: sousaedu <eduardo.sousa@canonical.com>
diff --git a/installers/charm/nbi/requirements.txt b/installers/charm/nbi/requirements.txt
index 24f1672..a26601f 100644
--- a/installers/charm/nbi/requirements.txt
+++ b/installers/charm/nbi/requirements.txt
@@ -20,5 +20,4 @@
##
ops
-pydantic
git+https://github.com/juju-solutions/resource-oci-image/@c5778285d332edf3d9a538f9d0c06154b7ec1b0b#egg=oci-image
diff --git a/installers/charm/nbi/src/charm.py b/installers/charm/nbi/src/charm.py
index 3970a33..6db99be 100755
--- a/installers/charm/nbi/src/charm.py
+++ b/installers/charm/nbi/src/charm.py
@@ -22,7 +22,6 @@
import logging
from typing import Any, Dict, NoReturn
-from pydantic import ValidationError
from ops.charm import CharmBase, CharmEvents
from ops.framework import EventBase, EventSource, StoredState
@@ -374,7 +373,7 @@
self.model.app.name,
self.port,
)
- except ValidationError as exc:
+ except ValueError as exc:
logger.exception("Config/Relation data validation error")
self.unit.status = BlockedStatus(str(exc))
return
diff --git a/installers/charm/nbi/src/pod_spec.py b/installers/charm/nbi/src/pod_spec.py
index 68915de..77fff3e 100644
--- a/installers/charm/nbi/src/pod_spec.py
+++ b/installers/charm/nbi/src/pod_spec.py
@@ -20,172 +20,146 @@
# osm-charmers@lists.launchpad.net
##
-import logging
-from pydantic import (
- BaseModel,
- conint,
- constr,
- IPvAnyNetwork,
- PositiveInt,
- validator,
-)
-from typing import Any, Dict, List, Optional
+from ipaddress import ip_network
+from typing import Any, Callable, Dict, List, NoReturn
from urllib.parse import urlparse
-logger = logging.getLogger(__name__)
+
+def _validate_max_file_size(max_file_size: int, site_url: str) -> bool:
+ """Validate max_file_size.
+
+ Args:
+ max_file_size (int): maximum file size allowed.
+ site_url (str): endpoint url.
+
+ Returns:
+ bool: True if valid, false otherwise.
+ """
+ if not site_url:
+ return True
+
+ parsed = urlparse(site_url)
+
+ if not parsed.scheme.startswith("http"):
+ return True
+
+ if max_file_size is None:
+ return False
+
+ return max_file_size >= 0
-class ConfigData(BaseModel):
- """Configuration data model."""
+def _validate_ip_network(network: str) -> bool:
+ """Validate IP network.
- enable_test: bool
- database_commonkey: constr(min_length=1)
- log_level: constr(regex=r"^(INFO|DEBUG)$")
- auth_backend: constr(regex=r"^(internal|keystone)$")
- site_url: Optional[str]
- max_file_size: Optional[conint(ge=0)]
- ingress_whitelist_source_range: Optional[IPvAnyNetwork]
- tls_secret_name: Optional[str]
+ Args:
+ network (str): IP network range.
- @validator("max_file_size", pre=True, always=True)
- def validate_max_file_size(cls, value, values, **kwargs):
- site_url = values.get("site_url")
+ Returns:
+ bool: True if valid, false otherwise.
+ """
+ if not network:
+ return True
- if not site_url:
- return value
+ try:
+ ip_network(network)
+ except ValueError:
+ return False
- parsed = urlparse(site_url)
-
- if not parsed.scheme.startswith("http"):
- return value
-
- if value is None:
- raise ValueError("max_file_size needs to be defined if site_url is defined")
-
- return value
-
- @validator("ingress_whitelist_source_range", pre=True, always=True)
- def validate_ingress_whitelist_source_range(cls, value, values, **kwargs):
- if not value:
- return None
-
- return value
+ return True
-class RelationData(BaseModel):
- """Relation data model."""
+def _validate_keystone_config(keystone: bool, value: Any, validator: Callable) -> bool:
+ """Validate keystone configurations.
- message_host: str
- message_port: PositiveInt
- database_uri: constr(regex=r"^(mongodb://)")
- prometheus_host: str
- prometheus_port: PositiveInt
- keystone: bool
- keystone_host: Optional[constr(min_length=1)]
- keystone_port: Optional[PositiveInt]
- keystone_user_domain_name: Optional[constr(min_length=1)]
- keystone_project_domain_name: Optional[constr(min_length=1)]
- keystone_username: Optional[constr(min_length=1)]
- keystone_password: Optional[constr(min_length=1)]
- keystone_service: Optional[constr(min_length=1)]
+ Args:
+ keystone (bool): is keystone enabled, true if so, false otherwise.
+ value (Any): value to be validated.
+ validator (Callable): function to validate configuration.
- @validator("keystone_host", pre=True, always=True)
- def validate_keystone_host(cls, value, values, **kwargs):
- keystone = values.get("keystone")
+ Returns:
+ bool: true if valid, false otherwise.
+ """
+ if not keystone:
+ return True
- if not keystone:
- return value
+ return validator(value)
- if value is None:
- raise ValueError(
- "keystone_host needs to be defined if keystone is configured"
- )
- return value
+def _validate_data(
+ config_data: Dict[str, Any], relation_data: Dict[str, Any], keystone: bool
+) -> NoReturn:
+ """Validate input data.
- @validator("keystone_port", pre=True, always=True)
- def validate_keystone_port(cls, value, values, **kwargs):
- keystone = values.get("keystone")
+ Args:
+ config_data (Dict[str, Any]): configuration data.
+ relation_data (Dict[str, Any]): relation data.
+ keystone (bool): is keystone to be used.
+ """
+ config_validators = {
+ "enable_test": lambda value, _: isinstance(value, bool),
+ "database_commonkey": lambda value, _: isinstance(value, str)
+ and len(value) > 1,
+ "log_level": lambda value, _: isinstance(value, str)
+ and (value == "INFO" or value == "DEBUG"),
+ "auth_backend": lambda value, _: isinstance(value, str)
+ and (value == "internal" or value == "keystone"),
+ "site_url": lambda value, _: isinstance(value, str)
+ if value is not None
+ else True,
+ "max_file_size": lambda value, values: _validate_max_file_size(
+ value, values.get("site_url")
+ ),
+ "ingress_whitelist_source_range": lambda value, _: _validate_ip_network(value),
+ "tls_secret_name": lambda value, _: isinstance(value, str)
+ if value is not None
+ else True,
+ }
+ relation_validators = {
+ "message_host": lambda value, _: isinstance(value, str),
+ "message_port": lambda value, _: isinstance(value, int) and value > 0,
+ "database_uri": lambda value, _: isinstance(value, str)
+ and value.startswith("mongodb://"),
+ "prometheus_host": lambda value, _: isinstance(value, str),
+ "prometheus_port": lambda value, _: isinstance(value, int) and value > 0,
+ "keystone_host": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, str) and len(x) > 0
+ ),
+ "keystone_port": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, int) and x > 0
+ ),
+ "keystone_user_domain_name": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, str) and len(x) > 0
+ ),
+ "keystone_project_domain_name": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, str) and len(x) > 0
+ ),
+ "keystone_username": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, str) and len(x) > 0
+ ),
+ "keystone_password": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, str) and len(x) > 0
+ ),
+ "keystone_service": lambda value, _: _validate_keystone_config(
+ keystone, value, lambda x: isinstance(x, str) and len(x) > 0
+ ),
+ }
+ problems = []
- if not keystone:
- return value
+ for key, validator in config_validators.items():
+ valid = validator(config_data.get(key), config_data)
- if value is None:
- raise ValueError(
- "keystone_port needs to be defined if keystone is configured"
- )
+ if not valid:
+ problems.append(key)
- return value
+ for key, validator in relation_validators.items():
+ valid = validator(relation_data.get(key), relation_data)
- @validator("keystone_user_domain_name", pre=True, always=True)
- def validate_keystone_user_domain_name(cls, value, values, **kwargs):
- keystone = values.get("keystone")
+ if not valid:
+ problems.append(key)
- if not keystone:
- return value
-
- if value is None:
- raise ValueError(
- "keystone_user_domain_name needs to be defined if keystone is configured"
- )
-
- return value
-
- @validator("keystone_project_domain_name", pre=True, always=True)
- def validate_keystone_project_domain_name(cls, value, values, **kwargs):
- keystone = values.get("keystone")
-
- if not keystone:
- return value
-
- if value is None:
- raise ValueError(
- "keystone_project_domain_name needs to be defined if keystone is configured"
- )
-
- return value
-
- @validator("keystone_username", pre=True, always=True)
- def validate_keystone_username(cls, value, values, **kwargs):
- keystone = values.get("keystone")
-
- if not keystone:
- return value
-
- if value is None:
- raise ValueError(
- "keystone_username needs to be defined if keystone is configured"
- )
-
- return value
-
- @validator("keystone_password", pre=True, always=True)
- def validate_keystone_password(cls, value, values, **kwargs):
- keystone = values.get("keystone")
-
- if not keystone:
- return value
-
- if value is None:
- raise ValueError(
- "keystone_password needs to be defined if keystone is configured"
- )
-
- return value
-
- @validator("keystone_service", pre=True, always=True)
- def validate_keystone_service(cls, value, values, **kwargs):
- keystone = values.get("keystone")
-
- if not keystone:
- return value
-
- if value is None:
- raise ValueError(
- "keystone_service needs to be defined if keystone is configured"
- )
-
- return value
+ if len(problems) > 0:
+ raise ValueError("Errors found in: {}".format(", ".join(problems)))
def _make_pod_ports(port: int) -> List[Dict[str, Any]]:
@@ -418,11 +392,7 @@
if not image_info:
return None
- ConfigData(**(config))
- RelationData(
- **(relation_state),
- keystone=True if config.get("auth_backend") == "keystone" else False,
- )
+ _validate_data(config, relation_state, config.get("auth_backend") == "keystone")
ports = _make_pod_ports(port)
env_config = _make_pod_envconfig(config, relation_state)
diff --git a/installers/charm/nbi/tests/test_pod_spec.py b/installers/charm/nbi/tests/test_pod_spec.py
index 7cf586c..360895f 100644
--- a/installers/charm/nbi/tests/test_pod_spec.py
+++ b/installers/charm/nbi/tests/test_pod_spec.py
@@ -20,7 +20,6 @@
# osm-charmers@lists.launchpad.net
##
-from pydantic import ValidationError
from typing import NoReturn
import unittest
@@ -463,7 +462,7 @@
app_name = "nbi"
port = 9999
- with self.assertRaises(ValidationError):
+ with self.assertRaises(ValueError):
pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port)
def test_make_pod_spec_without_relation_state(self) -> NoReturn:
@@ -480,7 +479,7 @@
app_name = "nbi"
port = 9999
- with self.assertRaises(ValidationError):
+ with self.assertRaises(ValueError):
pod_spec.make_pod_spec(image_info, config, relation_state, app_name, port)
def test_make_pod_spec(self) -> NoReturn:
diff --git a/installers/charm/nbi/tox.ini b/installers/charm/nbi/tox.ini
index 38f0954..b7b0912 100644
--- a/installers/charm/nbi/tox.ini
+++ b/installers/charm/nbi/tox.ini
@@ -56,7 +56,6 @@
stestr
mock
ops
- pydantic
setenv =
{[testenv]setenv}
PYTHON=coverage run