Add resource resolution gRPC client. 40/100740/2
authorMarek Szwalkiewicz <marek.szwalkiewicz@external.t-mobile.pl>
Thu, 23 Jan 2020 15:27:26 +0000 (15:27 +0000)
committerKAPIL SINGAL <ks220y@att.com>
Fri, 24 Jan 2020 14:59:47 +0000 (14:59 +0000)
Adds a python module that contains new resource resolution client that
should replace previous helpers done in Jython.

Issue-ID: CCSDK-1989
Signed-off-by: Marek Szwalkiewicz <marek.szwalkiewicz@external.t-mobile.pl>
Change-Id: I48b22acdc7fec31f28de84232c5b6b37124a0c2a

ms/py-executor/.coveragerc [new file with mode: 0644]
ms/py-executor/resource_resolution/README [new file with mode: 0644]
ms/py-executor/resource_resolution/__init__.py [new file with mode: 0644]
ms/py-executor/resource_resolution/client.py [new file with mode: 0644]
ms/py-executor/resource_resolution/tests/__init__.py [new file with mode: 0644]
ms/py-executor/resource_resolution/tests/client_test.py [new file with mode: 0644]
ms/py-executor/test-requirements.txt [new file with mode: 0644]
ms/py-executor/tox.ini [new file with mode: 0644]

diff --git a/ms/py-executor/.coveragerc b/ms/py-executor/.coveragerc
new file mode 100644 (file)
index 0000000..593c2ac
--- /dev/null
@@ -0,0 +1,2 @@
+[run]
+omit = **/proto/*
\ No newline at end of file
diff --git a/ms/py-executor/resource_resolution/README b/ms/py-executor/resource_resolution/README
new file mode 100644 (file)
index 0000000..a2d1542
--- /dev/null
@@ -0,0 +1,80 @@
+# Resource resolution client
+
+##  How to use examples
+
+### Insecure channel
+
+```
+from blueprints_grpc.proto.BluePrintCommon_pb2_grpc import ActionIdentifiers, CommonHeader
+from blueprints_grpc.proto.BluePrintProcessing_pb2_grpc import ExecutionServiceInput
+from resource_resolution.client import Client as ResourceResolutionClient
+
+
+def generate_messages():
+    commonHeader = CommonHeader()
+    commonHeader.requestId = "1234"
+    commonHeader.subRequestId = "1234-1"
+    commonHeader.originatorId = "CDS"
+
+    actionIdentifiers = ActionIdentifiers()
+    actionIdentifiers.blueprintName = "sample-cba"
+    actionIdentifiers.blueprintVersion = "1.0.0"
+    actionIdentifiers.actionName = "SampleScript"
+
+    input = ExecutionServiceInput(commonHeader=commonHeader, actionIdentifiers=actionIdentifiers)
+
+    commonHeader2 = CommonHeader()
+    commonHeader2.requestId = "1235"
+    commonHeader2.subRequestId = "1234-2"
+    commonHeader2.originatorId = "CDS"
+
+    input2 = ExecutionServiceInput(commonHeader=commonHeader2, actionIdentifiers=actionIdentifiers)
+
+    yield from [input, input2]
+
+
+if __name__ == "__main__":
+    with ResourceResolutionClient("localhost:50052") as client:
+        for response in client.process(generate_messages()):
+            print(response)
+
+```
+
+### Secure channel
+
+```
+from blueprints_grpc.proto.BluePrintCommon_pb2_grpc import ActionIdentifiers, CommonHeader
+from blueprints_grpc.proto.BluePrintProcessing_pb2_grpc import ExecutionServiceInput
+from resource_resolution.client import Client as ResourceResolutionClient
+
+
+def generate_messages():
+    commonHeader = CommonHeader()
+    commonHeader.requestId = "1234"
+    commonHeader.subRequestId = "1234-1"
+    commonHeader.originatorId = "CDS"
+
+    actionIdentifiers = ActionIdentifiers()
+    actionIdentifiers.blueprintName = "sample-cba"
+    actionIdentifiers.blueprintVersion = "1.0.0"
+    actionIdentifiers.actionName = "SampleScript"
+
+    input = ExecutionServiceInput(commonHeader=commonHeader, actionIdentifiers=actionIdentifiers)
+
+    commonHeader2 = CommonHeader()
+    commonHeader2.requestId = "1235"
+    commonHeader2.subRequestId = "1234-2"
+    commonHeader2.originatorId = "CDS"
+
+    input2 = ExecutionServiceInput(commonHeader=commonHeader2, actionIdentifiers=actionIdentifiers)
+
+    yield from [input, input2]
+
+
+if __name__ == "__main__":
+    with open("certs/py-executor/py-executor-chain.pem", "rb") as f:
+        with ResourceResolutionClient("localhost:50052", use_ssl=True, root_certificates=f.read()) as client:
+            for response in client.process(generate_messages()):
+                print(response)
+
+```
\ No newline at end of file
diff --git a/ms/py-executor/resource_resolution/__init__.py b/ms/py-executor/resource_resolution/__init__.py
new file mode 100644 (file)
index 0000000..2123690
--- /dev/null
@@ -0,0 +1,14 @@
+"""Copyright 2019 Deutsche Telekom.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
diff --git a/ms/py-executor/resource_resolution/client.py b/ms/py-executor/resource_resolution/client.py
new file mode 100644 (file)
index 0000000..913b0ed
--- /dev/null
@@ -0,0 +1,94 @@
+"""Copyright 2019 Deutsche Telekom.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+from logging import getLogger, Logger
+from types import TracebackType
+from typing import Iterable, List, Optional, Type
+
+from grpc import Channel, insecure_channel, secure_channel, ssl_channel_credentials
+
+from blueprints_grpc.proto.BluePrintProcessing_pb2 import ExecutionServiceInput, ExecutionServiceOutput
+from blueprints_grpc.proto.BluePrintProcessing_pb2_grpc import BluePrintProcessingServiceStub
+
+
+class Client:
+    """Resource resoulution client class."""
+
+    def __init__(
+        self,
+        server_address: str,
+        *,
+        use_ssl: bool = False,
+        root_certificates: bytes = None,
+        private_key: bytes = None,
+        certificate_chain: bytes = None,
+    ) -> None:
+        """Client class initialization.
+
+        :param server_address: Address to server to connect.
+        :param use_ssl: Boolean flag to determine if secure channel should be created or not. Keyword argument.
+        :param root_certificates: The PEM-encoded root certificates. None if it shouldn't be used. Keyword argument.
+        :param private_key: The PEM-encoded private key as a byte string, or None if no private key should be used. Keyword argument.
+        :param certificate_chain: The PEM-encoded certificate chain as a byte string to use or or None if no certificate chain should be used. Keyword argument.
+        """
+        self.logger = getLogger(__name__)
+        if use_ssl:
+            self.channel: Channel = secure_channel(
+                server_address, ssl_channel_credentials(root_certificates, private_key, certificate_chain)
+            )
+            self.logger.debug(f"Create secure channel to connect with {server_address}")
+        else:
+            self.channel: Channel = insecure_channel(server_address)
+            self.logger.debug(f"Create insecure channel to connect to {server_address}")
+        self.stub: BluePrintProcessingServiceStub = BluePrintProcessingServiceStub(self.channel)
+
+    def close(self) -> None:
+        """Close client session.
+
+        Closes client's channel.
+        """
+        self.logger.debug("Close channel connection")
+        self.channel.close()
+
+    def __enter__(self) -> Channel:
+        """Enter Client instance context.
+
+        Return Client instance. In the context user can call methods to communicate with server.
+        On exit connection with the server is going to be closed.
+        """
+        self.logger.debug("Enter Client instance context")
+        return self
+
+    def __exit__(
+        self,
+        unused_exc_type: Optional[Type[BaseException]],
+        unused_exc_value: Optional[BaseException],
+        unused_traceback: Optional[TracebackType],
+    ) -> None:
+        """Exit Client instance context.
+
+        Close connection with the server.
+        """
+        self.logger.debug("Exit Client instance context")
+        self.close()
+
+    def process(self, messages: Iterable[ExecutionServiceInput]) -> Iterable[ExecutionServiceOutput]:
+        """Send messages to server and return responses.
+
+        :param messages: Iterable messages to send
+        :return: Iterable responses
+        """
+        for message in self.stub.process(messages):
+            self.logger.debug(f"Get response message: {message}")
+            yield message
diff --git a/ms/py-executor/resource_resolution/tests/__init__.py b/ms/py-executor/resource_resolution/tests/__init__.py
new file mode 100644 (file)
index 0000000..2123690
--- /dev/null
@@ -0,0 +1,14 @@
+"""Copyright 2019 Deutsche Telekom.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
diff --git a/ms/py-executor/resource_resolution/tests/client_test.py b/ms/py-executor/resource_resolution/tests/client_test.py
new file mode 100644 (file)
index 0000000..2b94220
--- /dev/null
@@ -0,0 +1,28 @@
+"""Copyright 2019 Deutsche Telekom.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+from unittest.mock import MagicMock, patch
+
+from resource_resolution.client import Client
+
+
+@patch("resource_resolution.client.insecure_channel")
+def test_resource_resoulution_insecure_channel(insecure_channel_mock: MagicMock):
+    """Test if insecure_channel connection is called."""
+    with patch.object(Client, "close") as client_close_method_mock:  # Type MagicMock
+        with Client("127.0.0.1:3333"):
+            pass
+    insecure_channel_mock.called_once_with()
+    client_close_method_mock.called_once_with()
diff --git a/ms/py-executor/test-requirements.txt b/ms/py-executor/test-requirements.txt
new file mode 100644 (file)
index 0000000..79ed6ee
--- /dev/null
@@ -0,0 +1,3 @@
+pytest==5.3.1
+pytest-grpc==0.7.0
+-r requirements.txt
\ No newline at end of file
diff --git a/ms/py-executor/tox.ini b/ms/py-executor/tox.ini
new file mode 100644 (file)
index 0000000..8cf1776
--- /dev/null
@@ -0,0 +1,25 @@
+[tox]
+envlist=py37,py38
+skipsdist=True
+[testenv]
+setenv =
+    CONFIGURATION = configuration-local.ini
+deps =
+    -rtest-requirements.txt
+commands = pytest resource_resolution/
+[testenv:codelint]
+deps =
+    black
+commands = black -l 120 --check {posargs:.}
+[testenv:doclint]
+deps =
+    flake8-docstrings
+commands = flake8 --doctest --docstring-convention google --max-line-length 120 --exclude .svn,CVS,.bzr,.hg,.git,__pycache__,.tox,.eggs,*.egg,*test.py --select=D {posargs:.}
+[testenv:coverage]
+basepython = python3.7
+setenv = 
+    CONFIGURATION = configuration-local.ini
+deps =
+    -rtest-requirements.txt
+    pytest-cov
+commands = pytest --cov=manager --cov=resource_resolution --cov-fail-under=60 --cov-config={toxinidir}/.coveragerc resource_resolution/