diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ef37446a71297fbfe4994aca039f1bcc38d394bf..7938f7ec1aa82e5d883b7af4bd8c4f6884ebe779 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,6 +4,7 @@ stages:
- test
- dependencies
- deploy
+ - integration_test
# include the individual .gitlab-ci.yml of each micro-service
include:
@@ -11,3 +12,4 @@ include:
#- local: '/src/monitoring/.gitlab-ci.yml'
- local: '/src/context/.gitlab-ci.yml'
- local: '/src/device/.gitlab-ci.yml'
+ - local: '/src/integration_tester/.gitlab-ci.yml'
diff --git a/run_integration_tests.sh b/run_integration_tests.sh
index 0da2a7e00c70546c6170f9dea69e5309647b73d4..b9232c7176ebf0cfb2c49404b0fd6194660c9214 100755
--- a/run_integration_tests.sh
+++ b/run_integration_tests.sh
@@ -5,3 +5,6 @@
#ENDPOINT=($(kubectl --namespace teraflow-development get service contextservice -o 'jsonpath={.spec.clusterIP} {.spec.ports[?(@.name=="grpc")].port}'))
#docker run -it --env TEST_TARGET_ADDRESS=${ENDPOINT[0]} --env TEST_TARGET_PORT=${ENDPOINT[1]} context_service:test
+
+
+kubectl run integration-test --restart=Never --rm -i --tty --image centos -- /bin/bash
diff --git a/src/common/tests/Assertions.py b/src/common/tests/Assertions.py
index 6cf2f757bb87174882344ff4c762716e217accb3..7e08621f107805dd89978ad380675ef7b547d582 100644
--- a/src/common/tests/Assertions.py
+++ b/src/common/tests/Assertions.py
@@ -28,3 +28,16 @@ def validate_topology(message):
assert 'topoId' in message
assert 'device' in message
assert 'link' in message
+
+def validate_topology_is_empty(message):
+ validate_topology(message)
+ assert len(message['device']) == 0
+ assert len(message['link']) == 0
+
+def validate_topology_has_devices(message):
+ validate_topology(message)
+ assert len(message['device']) > 0
+
+def validate_topology_has_links(message):
+ validate_topology(message)
+ assert len(message['link']) > 0
diff --git a/src/context/tests/Dockerfile b/src/context/tests/Dockerfile
deleted file mode 100644
index ed70aa86e5c2db5eddb8bf7fc4106bdcc7f8323c..0000000000000000000000000000000000000000
--- a/src/context/tests/Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM context:latest
-
-# Run integration tests
-ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", "context/tests/test_integration.py"]
diff --git a/src/context/tests/test_integration.py b/src/context/tests/test_integration.py
deleted file mode 100644
index b8d5de420bb9ead65b9fdf7dab3cd5147765b8cc..0000000000000000000000000000000000000000
--- a/src/context/tests/test_integration.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#import logging, os, pytest, sys
-#
-#from pathlib import Path
-#sys.path.append(__file__.split('src')[0] + 'src')
-#print(sys.path)
-#
-#from context.client.ContextClient import ContextClient
-#from context.proto.context_pb2 import Empty
-#from .tools.ValidateTopology import validate_topology_dict
-#
-#LOGGER = logging.getLogger(__name__)
-#LOGGER.setLevel(logging.DEBUG)
-#
-#@pytest.fixture(scope='session')
-#def remote_context_client():
-# address = os.environ.get('TEST_TARGET_ADDRESS')
-# if(address is None): raise Exception('EnvironmentVariable(TEST_TARGET_ADDRESS) not specified')
-# port = os.environ.get('TEST_TARGET_PORT')
-# if(port is None): raise Exception('EnvironmentVariable(TEST_TARGET_PORT) not specified')
-# return ContextClient(address=address, port=port)
-#
-#def test_remote_get_topology(remote_context_client):
-# response = remote_context_client.GetTopology(Empty())
-# validate_topology_dict(response)
diff --git a/src/device/tests/Dockerfile b/src/device/tests/Dockerfile
deleted file mode 100644
index e8789ce7442b58063f96970d2f52b8c31b1818c5..0000000000000000000000000000000000000000
--- a/src/device/tests/Dockerfile
+++ /dev/null
@@ -1,4 +0,0 @@
-FROM device:latest
-
-# Run integration tests
-ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", "device/tests/test_integration.py"]
diff --git a/src/device/tests/test_integration.py b/src/device/tests/test_integration.py
deleted file mode 100644
index 3f1519aba7b1a35972d467279eeb45c2723390b2..0000000000000000000000000000000000000000
--- a/src/device/tests/test_integration.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#import logging, os, pytest, sys
-
-#from pathlib import Path
-#sys.path.append(__file__.split('src')[0] + 'src')
-#print(sys.path)
-
-#from context.client.ContextClient import ContextClient
-#from context.proto.context_pb2 import Empty
-#from .tools.ValidateTopology import validate_topology_dict
-
-#LOGGER = logging.getLogger(__name__)
-#LOGGER.setLevel(logging.DEBUG)
-
-#@pytest.fixture(scope='session')
-#def remote_context_client():
-# address = os.environ.get('TEST_TARGET_ADDRESS')
-# if(address is None): raise Exception('EnvironmentVariable(TEST_TARGET_ADDRESS) not specified')
-# port = os.environ.get('TEST_TARGET_PORT')
-# if(port is None): raise Exception('EnvironmentVariable(TEST_TARGET_PORT) not specified')
-# return ContextClient(address=address, port=port)
-
-#def test_remote_get_topology(remote_context_client):
-# response = remote_context_client.GetTopology(Empty())
-# validate_topology_dict(response)
diff --git a/src/integration_tester/.gitlab-ci.yml b/src/integration_tester/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fef34bcf8ce20bbd98e5bd613c5571041874ebd2
--- /dev/null
+++ b/src/integration_tester/.gitlab-ci.yml
@@ -0,0 +1,56 @@
+# Build, tag, and push the Docker images to the GitLab Docker registry
+build integration_tester:
+ variables:
+ IMAGE_NAME: 'integration_tester' # name of the microservice
+ IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+ stage: build
+ before_script:
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+ script:
+ - docker build -t "$IMAGE_NAME:$IMAGE_TAG" -f ./src/$IMAGE_NAME/Dockerfile ./src/
+ - docker tag "$IMAGE_NAME:$IMAGE_TAG" "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
+ - docker push "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
+ rules:
+ - changes:
+ - src/common/**
+ - src/context/**
+ - src/device/**
+ - src/$IMAGE_NAME/**
+ - .gitlab-ci.yml
+
+# Pull, execute, and run unitary tests for the Docker image from the GitLab registry
+test integration_tester:
+ variables:
+ IMAGE_NAME: 'integration_tester' # name of the microservice
+ IMAGE_TAG: 'latest' # tag of the container image (production, development, etc)
+ stage: test
+ needs:
+ - build integration_tester
+ before_script:
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
+ script:
+ - docker pull "$CI_REGISTRY_IMAGE/$IMAGE_NAME:$IMAGE_TAG"
+ rules:
+ - changes:
+ - src/common/**
+ - src/context/**
+ - src/device/**
+ - src/$IMAGE_NAME/**
+ - .gitlab-ci.yml
+
+# Run integration tests in Kubernetes Cluster
+integration_test integration_tester:
+ stage: integration_test
+ needs:
+ - build integration_tester
+ - test integration_tester
+ - deploy context
+ - deploy device
+ - dependencies all
+ - dependencies context_device
+ script:
+ - kubectl version
+ - kubectl get all
+ - kubectl run $IMAGE_NAME --image "$IMAGE_NAME:$IMAGE_TAG" --restart=Never --rm -i --tty
+ - kubectl get all
+ when: manual
diff --git a/src/integration_tester/Dockerfile b/src/integration_tester/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..98deb984268f89c0f5beda52457347d00e5d0503
--- /dev/null
+++ b/src/integration_tester/Dockerfile
@@ -0,0 +1,33 @@
+FROM python:3-slim
+
+# Install dependencies
+RUN apt-get --yes --quiet --quiet update && \
+ apt-get --yes --quiet --quiet install wget g++ && \
+ rm -rf /var/lib/apt/lists/*
+
+# Set Python to show logs as they occur
+ENV PYTHONUNBUFFERED=0
+
+# Get generic Python packages
+RUN python3 -m pip install --upgrade pip setuptools wheel pip-tools
+
+# Set working directory
+WORKDIR /var/teraflow
+
+# Create module sub-folders
+RUN mkdir -p /var/teraflow/tests
+
+# Get Python packages per module
+COPY tests/requirements.in tests/requirements.in
+RUN pip-compile --output-file=tests/requirements.txt tests/requirements.in
+RUN python3 -m pip install -r tests/requirements.in
+
+# Add files into working directory
+COPY common/. common
+COPY context/. context
+COPY device/. device
+
+# Run integration tests
+ENTRYPOINT ["pytest", "-v", "--log-level=DEBUG", \
+ "tests/test_context_device.py" \
+]
diff --git a/src/integration_tester/__init__.py b/src/integration_tester/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/integration_tester/definitions.py b/src/integration_tester/definitions.py
new file mode 100644
index 0000000000000000000000000000000000000000..ebdc3a43de438722716bf4f8d87eab3165526980
--- /dev/null
+++ b/src/integration_tester/definitions.py
@@ -0,0 +1,92 @@
+from common.database.api.context.OperationalStatus import OperationalStatus
+
+TOPOLOGY_ID = {
+ 'contextId': {'contextUuid': {'uuid': 'admin'}},
+ 'topoId': {'uuid': 'admin'}
+}
+
+DEVICE_ID_DEV1 = {'device_id': {'uuid': 'dev1'}}
+DEVICE_DEV1 = {
+ 'device_id': {'device_id': {'uuid': 'dev1'}}, 'device_type': 'ROADM', 'device_config': {'device_config': ''},
+ 'devOperationalStatus': OperationalStatus.ENABLED.value,
+ 'endpointList' : [
+ {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev1'}}, 'port_id': {'uuid' : 'to-dev2'}}, 'port_type': 'WDM'},
+ {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev1'}}, 'port_id': {'uuid' : 'to-dev3'}}, 'port_type': 'WDM'},
+ ]
+}
+
+DEVICE_ID_DEV2 = {'device_id': {'uuid': 'dev2'}}
+DEVICE_DEV2 = {
+ 'device_id': {'device_id': {'uuid': 'dev2'}}, 'device_type': 'ROADM', 'device_config': {'device_config': ''},
+ 'devOperationalStatus': OperationalStatus.ENABLED.value,
+ 'endpointList' : [
+ {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev2'}}, 'port_id': {'uuid' : 'to-dev1'}}, 'port_type': 'WDM'},
+ {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev2'}}, 'port_id': {'uuid' : 'to-dev3'}}, 'port_type': 'WDM'},
+ ]
+}
+
+DEVICE_ID_DEV3 = {'device_id': {'uuid': 'dev3'}}
+DEVICE_DEV3 = {
+ 'device_id': {'device_id': {'uuid': 'dev3'}},
+ 'device_type': 'ROADM',
+ 'device_config': {'device_config': ''},
+ 'devOperationalStatus': OperationalStatus.ENABLED.value,
+ 'endpointList' : [
+ {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev3'}}, 'port_id': {'uuid' : 'to-dev1'}}, 'port_type': 'WDM'},
+ {'port_id': {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev3'}}, 'port_id': {'uuid' : 'to-dev2'}}, 'port_type': 'WDM'},
+ ]
+}
+
+LINK_ID_DEV1_DEV2 = {'link_id': {'uuid': 'dev1/to-dev2 ==> dev2/to-dev1'}}
+LINK_DEV1_DEV2 = {
+ 'link_id': {'link_id': {'uuid': 'dev1/to-dev2 ==> dev2/to-dev1'}},
+ 'endpointList' : [
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev1'}}, 'port_id': {'uuid' : 'to-dev2'}},
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev2'}}, 'port_id': {'uuid' : 'to-dev1'}},
+ ]
+}
+
+LINK_ID_DEV1_DEV3 = {'link_id': {'uuid': 'dev1/to-dev3 ==> dev3/to-dev1'}}
+LINK_DEV1_DEV3 = {
+ 'link_id': {'link_id': {'uuid': 'dev1/to-dev3 ==> dev3/to-dev1'}},
+ 'endpointList' : [
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev1'}}, 'port_id': {'uuid' : 'to-dev3'}},
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev3'}}, 'port_id': {'uuid' : 'to-dev1'}},
+ ]
+}
+
+LINK_ID_DEV2_DEV1 = {'link_id': {'uuid': 'dev2/to-dev1 ==> dev1/to-dev2'}}
+LINK_DEV2_DEV1 = {
+ 'link_id': {'link_id': {'uuid': 'dev2/to-dev1 ==> dev1/to-dev2'}},
+ 'endpointList' : [
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev2'}}, 'port_id': {'uuid' : 'to-dev1'}},
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev1'}}, 'port_id': {'uuid' : 'to-dev2'}},
+ ]
+}
+
+LINK_ID_DEV2_DEV3 = {'link_id': {'uuid': 'dev2/to-dev3 ==> dev3/to-dev2'}}
+LINK_DEV2_DEV3 = {
+ 'link_id': {'link_id': {'uuid': 'dev2/to-dev3 ==> dev3/to-dev2'}},
+ 'endpointList' : [
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev2'}}, 'port_id': {'uuid' : 'to-dev3'}},
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev3'}}, 'port_id': {'uuid' : 'to-dev2'}},
+ ]
+}
+
+LINK_ID_DEV3_DEV1 = {'link_id': {'uuid': 'dev3/to-dev1 ==> dev1/to-dev3'}}
+LINK_DEV3_DEV1 = {
+ 'link_id': {'link_id': {'uuid': 'dev3/to-dev1 ==> dev1/to-dev3'}},
+ 'endpointList' : [
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev3'}}, 'port_id': {'uuid' : 'to-dev1'}},
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev1'}}, 'port_id': {'uuid' : 'to-dev3'}},
+ ]
+}
+
+LINK_ID_DEV3_DEV2 = {'link_id': {'uuid': 'dev3/to-dev2 ==> dev2/to-dev3'}}
+LINK_DEV3_DEV2 = {
+ 'link_id': {'link_id': {'uuid': 'dev3/to-dev2 ==> dev2/to-dev3'}},
+ 'endpointList' : [
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev3'}}, 'port_id': {'uuid' : 'to-dev2'}},
+ {'topoId': TOPOLOGY_ID, 'dev_id': {'device_id': {'uuid': 'dev2'}}, 'port_id': {'uuid' : 'to-dev3'}},
+ ]
+}
diff --git a/src/integration_tester/requirements.in b/src/integration_tester/requirements.in
new file mode 100644
index 0000000000000000000000000000000000000000..25abdad1b5767117956a88b816399635348884c7
--- /dev/null
+++ b/src/integration_tester/requirements.in
@@ -0,0 +1,6 @@
+grpcio-health-checking
+grpcio
+prometheus-client
+pytest
+pytest-benchmark
+redis
diff --git a/src/integration_tester/test_context_device.py b/src/integration_tester/test_context_device.py
new file mode 100644
index 0000000000000000000000000000000000000000..a0eaa1fff4bcb7139c474aec6e97ea63aa377626
--- /dev/null
+++ b/src/integration_tester/test_context_device.py
@@ -0,0 +1,79 @@
+import logging, os, pytest
+from google.protobuf.json_format import MessageToDict
+from src.common.tests.Assertions import validate_device_id, validate_link_id, validate_topology_has_devices, \
+ validate_topology_has_links, validate_topology_is_empty
+from common.database.Factory import get_database, DatabaseEngineEnum
+from common.database.api.Database import Database
+from context.client.ContextClient import ContextClient
+from context.proto.context_pb2 import Device, Empty, Link
+from device.client.DeviceClient import DeviceClient
+from .definitions import DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3, LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, \
+ LINK_DEV2_DEV3, LINK_DEV3_DEV1, LINK_DEV3_DEV2
+
+LOGGER = logging.getLogger(__name__)
+LOGGER.setLevel(logging.DEBUG)
+
+def get_setting(name):
+ value = os.environ.get(name)
+ if value is None: raise Exception('Unable to find variable({})'.format(name))
+ return value
+
+@pytest.fixture(scope='session')
+def redis_database():
+ _database = get_database(engine=DatabaseEngineEnum.REDIS)
+ return _database
+
+@pytest.fixture(scope='session')
+def context_client():
+ service_host = get_setting('CONTEXTSERVICE_SERVICE_HOST')
+ service_port = get_setting('CONTEXTSERVICE_SERVICE_PORT_GRPC')
+ _client = ContextClient(address=service_host, port=service_port)
+ yield _client
+ _client.close()
+
+@pytest.fixture(scope='session')
+def device_client():
+ service_host = get_setting('DEVICESERVICE_SERVICE_HOST')
+ service_port = get_setting('DEVICESERVICE_SERVICE_PORT_GRPC')
+ _client = DeviceClient(address=service_host, port=service_port)
+ yield _client
+ _client.close()
+
+def test_clean_database(redis_database : Database):
+ # should work
+ redis_database.clear_all()
+
+def test_get_topology_empty(context_client : ContextClient):
+ # should work
+ validate_topology_is_empty(MessageToDict(
+ context_client.GetTopology(Empty()),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+def test_add_devices(device_client : DeviceClient):
+ # should work
+ for device in [DEVICE_DEV1, DEVICE_DEV2, DEVICE_DEV3]:
+ validate_device_id(MessageToDict(
+ device_client.AddDevice(Device(**device)),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+ # should work
+ validate_topology_has_devices(MessageToDict(
+ context_client.GetTopology(Empty()),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+def test_add_links(context_client : ContextClient):
+ # should work
+ for link in [LINK_DEV1_DEV2, LINK_DEV1_DEV3, LINK_DEV2_DEV1, LINK_DEV2_DEV3, LINK_DEV3_DEV1, LINK_DEV3_DEV2]:
+ validate_link_id(MessageToDict(
+ context_client.AddLink(Link(**link)),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))
+
+ # should work
+ validate_topology_has_links(MessageToDict(
+ context_client.GetTopology(Empty()),
+ including_default_value_fields=True, preserving_proto_field_name=True,
+ use_integers_for_enums=False))