Wrapper for simulators 92/117192/8
authorEli Halych <illia.halych@t-mobile.pl>
Thu, 28 Jan 2021 16:11:39 +0000 (16:11 +0000)
committerEli Halych <illia.halych@t-mobile.pl>
Mon, 22 Feb 2021 14:08:46 +0000 (14:08 +0000)
Implemented using Avionix. Supports Helm 3 only. The local directory path was defined relative to the package. Remote charts that are described locally are used. Starting the simulator is provided as a regular HTTP or HTTPS request.

Issue-ID: INT-1829
Signed-off-by: Eli Halych <illia.halych@t-mobile.pl>
Change-Id: Ia17c4043bedd853bf2c068e53d51cd2808a3c0db

requirements.txt
src/onaptests/configuration/settings.py
src/onaptests/steps/wrapper/__init__.py [new file with mode: 0644]
src/onaptests/steps/wrapper/helm_charts.py [new file with mode: 0644]
src/onaptests/steps/wrapper/start.py [new file with mode: 0644]
src/onaptests/templates/helm_charts/README.md [new file with mode: 0755]
src/onaptests/utils/exceptions.py
src/onaptests/utils/simulators.py [new file with mode: 0644]

index fec6816..945b8ff 100644 (file)
@@ -3,4 +3,5 @@ openstacksdk
 onapsdk==7.4.0
 jinja2
 kubernetes
-docker
\ No newline at end of file
+docker
+avionix==0.4.4
\ No newline at end of file
index ba4e163..609ca66 100644 (file)
@@ -41,6 +41,7 @@ CLEANUP_FLAG = False
 
 REPORTING_FILE_PATH = "/tmp/reporting.html"
 K8S_REGION_TYPE = "k8s"
+TILLER_HOST = "localhost"
 K8S_CONFIG = None  # None means it will use default config (~/.kube/config)
 K8S_NAMESPACE = "onap"  # Kubernetes namespace
 #SOCK_HTTP = "socks5h://127.0.0.1:8091"
diff --git a/src/onaptests/steps/wrapper/__init__.py b/src/onaptests/steps/wrapper/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/onaptests/steps/wrapper/helm_charts.py b/src/onaptests/steps/wrapper/helm_charts.py
new file mode 100644 (file)
index 0000000..4482683
--- /dev/null
@@ -0,0 +1,93 @@
+"""Basic container commands to Docker."""
+import yaml
+from avionix import ChartBuilder, ChartDependency, ChartInfo
+from avionix.errors import HelmError
+from onaptests.steps.base import BaseStep
+from onaptests.utils.simulators import get_local_dir
+from onaptests.utils.exceptions import  (
+    EnvironmentPreparationException,
+    EnvironmentCleanupException)
+
+
+
+class HelmChartStep(BaseStep):
+    """Basic operations on a docker container."""
+
+    def __init__(self,
+                 cleanup: bool = False,
+                 chart_info_file: str = None) -> None:
+        """Setup Helm chart details.
+
+        Arguments:
+            cleanup (bool): cleanup after execution. Defaults to False.
+            chart_info_file (str): description file of a chart. Default to None.
+        """
+        chart_info = None
+        dependencies = []
+
+        super().__init__(cleanup=cleanup)
+
+        chart_info_path = get_local_dir() / chart_info_file
+
+        try:
+            with open(chart_info_path, 'r') as stream:
+                chart_info = yaml.safe_load(stream)
+        except IOError as err:
+            msg = f"{chart_info_file} not found."
+            raise EnvironmentPreparationException(msg) from err
+
+
+        try:
+            for dependency in chart_info["dependencies"]:
+                dep = ChartDependency(
+                    name=dependency["name"],
+                    version=dependency["version"],
+                    repository=dependency["repository"],
+                    local_repo_name=dependency["local_repo_name"],
+                    values=dependency["values"])
+                dependencies.append(dep)
+
+            self.builder = ChartBuilder(
+                    chart_info=ChartInfo(
+                        api_version=chart_info["api_version"],
+                        name=chart_info["chart_name"],
+                        version=chart_info["version"],  # SemVer 2 version
+                        app_version=chart_info["app_version"],
+                        dependencies=dependencies
+                    ),
+                    kubernetes_objects=[],
+                    keep_chart=False
+                )
+        except KeyError as err:
+            msg = f"{chart_info_file} does not contain required keys."
+            raise EnvironmentPreparationException(msg) from err
+
+    @property
+    def description(self) -> str:
+        """Step description."""
+        return "Execute Helm charts."
+
+    @property
+    def component(self) -> str:
+        """Component name."""
+        return "Environment"
+
+    @BaseStep.store_state
+    def execute(self) -> None:
+        """Install helm release."""
+        super().execute()
+        try:
+            self.builder.install_chart({"dependency-update": None})
+        except HelmError as err:
+            msg = "Error during helm release installation."
+            raise EnvironmentPreparationException(msg) from err
+
+
+    def cleanup(self) -> None:
+        """Uninstall helm release."""
+        try:
+            self.builder.uninstall_chart()
+        except HelmError as err:
+            msg = "Error during helm release deletion."
+            raise EnvironmentCleanupException(msg) from err
+        super().cleanup()
diff --git a/src/onaptests/steps/wrapper/start.py b/src/onaptests/steps/wrapper/start.py
new file mode 100644 (file)
index 0000000..18ba5df
--- /dev/null
@@ -0,0 +1,64 @@
+"""Start simulators via simulators' API."""
+from typing import Union, Optional, Dict
+import requests
+from onaptests.steps.base import BaseStep
+from onaptests.utils.exceptions import TestConfigurationException
+
+class SimulatorStartStep(BaseStep):
+    """Basic operations on a docker container."""
+
+    def __init__(self,  # pylint: disable=R0913
+                 cleanup: bool = False,
+                 https: bool = False,
+                 host: str = None,
+                 port: Union[int, str] = None,
+                 endpoint: Optional[str] = "",
+                 method: str = "GET",
+                 data: Dict = None) -> None:
+        """Prepare request data and details.
+
+        Arguments:
+            cleanup (bool):
+                determines if cleanup action should be called.
+                Defaults to False.
+            https (bool): use https or http. Defaults to False.
+            host (str): IP or hostname. Defaults to None.
+            port (Union[int, str]): port number. Defaults to None.
+            endpoint (str):
+                additional endpoint if applicable.
+                Defautls to "".
+            method (str):
+                GET or POST strings, case insensitive.
+                Defaults tp GET.
+            data (Dict):
+                parameters, that request's post() or get() takes, besides url.
+                For example, {"json": {}, ...}. Defaults to None.
+        """
+        if not host and not port:
+            raise TestConfigurationException("Provide host and/or port.")
+
+        super().__init__(cleanup=cleanup)
+
+        default_port = "443" if https else "80"
+        protocol = "https" if https else "http"
+        endpoint = endpoint[1:] if endpoint.startswith("/") else endpoint
+
+        self.method = method
+        self.data = data if data else {}
+        self.url = f"{protocol}://{host}:{port or default_port}/{endpoint}"
+
+    @property
+    def description(self) -> str:
+        """Step description."""
+        return "Send commands to the simulator application."
+
+    @property
+    def component(self) -> str:
+        """Component name."""
+        return "Environment"
+
+    @BaseStep.store_state
+    def execute(self) -> None:
+        """Send a start command to the simulator application."""
+        super().execute()
+        requests.request(self.method.upper(), self.url, **self.data)
diff --git a/src/onaptests/templates/helm_charts/README.md b/src/onaptests/templates/helm_charts/README.md
new file mode 100755 (executable)
index 0000000..bf76ee2
--- /dev/null
@@ -0,0 +1,31 @@
+# Local helm chart directory
+
+It is adviced that a remote repository is used for simulators, to reduce local
+complexity and avoid mistakes related to the duplicate code, submodules etc.
+
+Place a .yaml file in this folder and mention it during HelmChartStep
+initialization.
+
+How a chart info .yaml file would look like:
+
+```yaml
+api_version: "v1"
+app_version: "3.11.9"
+chart_name: "mychart"
+version: "0.1.0"
+dependencies:
+- name: "cassandra"
+  version: "0.1.4"
+  repository: "https://charts.kube-ops.io"
+  local_repo_name: "kube-ops"
+  values: {}
+- name: "generate"
+  repository: https://charts.kube-ops.io
+  version: "~0.2.3"
+  local_repo_name: "kube-ops"
+  values: {}
+```
+
+All fields in the sample .yaml file above are required by the avionix library.
+For more details, refer to the
+[documentation](https://avionix.readthedocs.io/en/latest/reference/index.html).
index e453d67..c12ee2f 100644 (file)
@@ -74,3 +74,7 @@ class EnvironmentPreparationException(OnapTestException):
 
 class SubstepExecutionException(OnapTestException):
     """Exception raised if substep execution fails."""
+
+class EnvironmentCleanupException(OnapTestException):
+    """Test environment cleanup exception."""
+    error_message="Test couldn't finish a cleanup"
diff --git a/src/onaptests/utils/simulators.py b/src/onaptests/utils/simulators.py
new file mode 100644 (file)
index 0000000..08acdbd
--- /dev/null
@@ -0,0 +1,13 @@
+"""Standard functions for the simulator wrapper."""
+from importlib.resources import path
+
+def get_local_dir():
+    """Get the default path for helm charts.
+
+    Returns:
+        chart_directory (Path):
+        local helm chart folder relative to the package.
+    """
+    with path('onaptests', 'templates') as templates:
+        chart_directory = templates / 'helm_charts'
+    return chart_directory