From 630842fc8e461c5d684f851cca725c490f47021f Mon Sep 17 00:00:00 2001
From: EsauM10 <esau2096@gmail.com>
Date: Thu, 6 Jul 2023 18:50:22 -0300
Subject: [PATCH] feat: add endpoint to run containers out of Containernet

---
 .../apis/worker/controllers/__init__.py       |  2 ++
 .../apis/worker/controllers/run_service.py    | 27 +++++++++++++++++++
 clusternet/apis/worker/helpers.py             |  8 ++++--
 clusternet/client/worker.py                   | 14 ++++++++--
 clusternet/server/worker_app.py               | 11 ++++++--
 setup.py                                      |  2 +-
 6 files changed, 57 insertions(+), 7 deletions(-)
 create mode 100644 clusternet/apis/worker/controllers/run_service.py

diff --git a/clusternet/apis/worker/controllers/__init__.py b/clusternet/apis/worker/controllers/__init__.py
index 3771b41..2f5f085 100644
--- a/clusternet/apis/worker/controllers/__init__.py
+++ b/clusternet/apis/worker/controllers/__init__.py
@@ -9,9 +9,11 @@
 from clusternet.apis.worker.controllers.remove_link import RemoveLinkController
 from clusternet.apis.worker.controllers.run_command import RunCommandOnHostController
 from clusternet.apis.worker.controllers.run_pingall import RunPingallController
+from clusternet.apis.worker.controllers.run_service import RunServiceController
 from clusternet.apis.worker.controllers.start_docker import StartDockerController
 from clusternet.apis.worker.controllers.start_worker import StartWorkerController
 from clusternet.apis.worker.controllers.stop_docker import StopDockerController
 from clusternet.apis.worker.controllers.stop_worker import StopWorkerController
 from clusternet.apis.worker.controllers.update_cpu import UpdateCPUController
 from clusternet.apis.worker.controllers.update_memory import UpdateMemoryController
+
diff --git a/clusternet/apis/worker/controllers/run_service.py b/clusternet/apis/worker/controllers/run_service.py
new file mode 100644
index 0000000..8229bcf
--- /dev/null
+++ b/clusternet/apis/worker/controllers/run_service.py
@@ -0,0 +1,27 @@
+from clusternet.apis.presentation.exceptions import BadRequestException
+from clusternet.apis.presentation.helpers import (
+    bad_request, created, error, internal_server_error, validate_required_params
+)
+from clusternet.apis.presentation.protocols import Controller, HttpRequest, HttpResponse
+from clusternet.apis.worker.helpers import get_hostname, run_container
+
+
+class RunServiceController(Controller):
+    def __init__(self) -> None:
+        pass
+    
+    def handle(self, request: HttpRequest) -> HttpResponse:
+        required_params = ['name', 'image']
+        try:
+            hostname = get_hostname()
+            validate_required_params(request, required_params)
+            
+            name = str(request.body.pop('name'))
+            image = str(request.body.pop('image'))
+            run_container(name, image, **request.body)
+            
+            return created({'content': f'[{hostname}]: service {name} created'})
+        except BadRequestException as ex:
+            return bad_request(error(f'{ex}'))
+        except Exception as ex:
+            return internal_server_error(error(f'{ex}'))
diff --git a/clusternet/apis/worker/helpers.py b/clusternet/apis/worker/helpers.py
index 5975e02..260e2ef 100644
--- a/clusternet/apis/worker/helpers.py
+++ b/clusternet/apis/worker/helpers.py
@@ -1,10 +1,11 @@
-from typing import List
+from typing import Any, List
 import docker
 import socket
 
 from mininet.log import info
 from clusternet.apis.worker.data import WorkerInstance
 
+client = docker.from_env()
 
 def get_hostname() -> str:
     return socket.gethostname()
@@ -21,4 +22,7 @@ def clean_containers_with_prefix(prefix: str) -> List[str]:
             container.remove(force=True) # type: ignore
             removed_containers.append(name)
     
-    return removed_containers
\ No newline at end of file
+    return removed_containers
+
+def run_container(name: str, image: str, **params: Any):
+    client.containers.run(image, name=name, remove=True, detach=True, **params)
diff --git a/clusternet/client/worker.py b/clusternet/client/worker.py
index 5680163..7401aa3 100644
--- a/clusternet/client/worker.py
+++ b/clusternet/client/worker.py
@@ -1,3 +1,4 @@
+from typing import Any
 import httpx
 
 from clusternet.client.container import RemoteContainer
@@ -17,7 +18,7 @@ def add_controller(self, name: str, ip: str, port: int):
         print(f'{response.json()["content"]}')
 
     
-    def add_docker(self, name: str, **params) -> RemoteContainer:
+    def add_docker(self, name: str, **params: Any) -> RemoteContainer:
         data = {'name': name, **params}
         response = httpx.post(url=f'{self.url}/containers', json=data, timeout=None)
         
@@ -28,7 +29,7 @@ def add_docker(self, name: str, **params) -> RemoteContainer:
         return RemoteContainer(name, self.url)
 
 
-    def add_link(self, node1: str, node2: str, **params):
+    def add_link(self, node1: str, node2: str, **params: Any):
         data = {'node1': node1, 'node2': node2, **params}
         response = httpx.post(url=f'{self.url}/links', json=data, timeout=None)
         
@@ -97,6 +98,15 @@ def run_pingall(self):
         print(f'{response.json()["content"]}')
 
 
+    def run_service(self, name: str, image: str, **params: Any):
+        data = {'name': name, 'image': image, **params}
+        response = httpx.post(url=f'{self.url}/services', json=data, timeout=None)
+        
+        if(response.is_error):
+            raise Exception(response.json()['error'])
+        print(f'{response.json()["content"]}')
+    
+
     def start(self):
         response = httpx.get(url=f'{self.url}/start', timeout=None)
         
diff --git a/clusternet/server/worker_app.py b/clusternet/server/worker_app.py
index 70de606..be3e10d 100644
--- a/clusternet/server/worker_app.py
+++ b/clusternet/server/worker_app.py
@@ -7,8 +7,9 @@
 from clusternet.apis.worker.controllers import (
     AddController, AddDockerController, AddLinkController, AddSwitchController,
     CleanContainersController, ConfigDefaultController, GetDockerIPController,
-    RemoveDockerController, RemoveLinkController, RunCommandOnHostController, RunPingallController,
-    StartDockerController, StopDockerController, StartWorkerController, StopWorkerController, 
+    RemoveDockerController, RemoveLinkController, RunCommandOnHostController, 
+    RunServiceController, RunPingallController, StartDockerController, 
+    StopDockerController, StartWorkerController, StopWorkerController, 
     UpdateCPUController, UpdateMemoryController
 )
 from clusternet.apis.presentation.helpers import parse_request
@@ -112,6 +113,12 @@ def run_pingall():
     return make_response(controller, request)
 
 
+@server.route('/services', methods=['POST'])
+def run_service():
+    controller = RunServiceController()
+    return make_response(controller, request)
+
+
 @server.route('/start', methods=['GET'])
 def start():
     controller = StartWorkerController()
diff --git a/setup.py b/setup.py
index e3195a5..a180d15 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@
 
 setup(
     name='clusternet',
-    version='0.6.2',
+    version='0.7.2',
     description='Distributed Software Defined Network Emulation',
     long_description='Distributed Software Defined Network Emulation',
     keywords=['networking', 'emulator', 'containernet', 'mininet', 'OpenFlow', 'SDN', 'fog'],