44import time
55from typing import cast , Dict , List , Optional , Type , Tuple # noqa
66import subprocess
7+ import socket
78
89import docker
910
1011from sebs .cache import Cache
1112from sebs .config import SeBSConfig
12- from sebs .utils import LoggingHandlers
13+ from sebs .utils import LoggingHandlers , is_linux
1314from sebs .local .config import LocalConfig
1415from sebs .local .storage import Minio
1516from sebs .local .function import LocalFunction
@@ -174,27 +175,60 @@ def create_function(self, code_package: Benchmark, func_name: str) -> "LocalFunc
174175 self .name (), code_package .language_name
175176 ),
176177 }
177- container = self ._docker_client .containers .run (
178- image = container_name ,
179- command = f"/bin/bash /sebs/run_server.sh { self .DEFAULT_PORT } " ,
180- volumes = {code_package .code_location : {"bind" : "/function" , "mode" : "ro" }},
181- environment = environment ,
182- # FIXME: make CPUs configurable
183- # FIXME: configure memory
184- # FIXME: configure timeout
185- # cpuset_cpus=cpuset,
186- # required to access perf counters
187- # alternative: use custom seccomp profile
188- privileged = True ,
189- security_opt = ["seccomp:unconfined" ],
190- network_mode = "bridge" ,
191- # somehow removal of containers prevents checkpointing from working?
192- remove = self .remove_containers ,
193- stdout = True ,
194- stderr = True ,
195- detach = True ,
196- # tty=True,
197- )
178+
179+ # FIXME: make CPUs configurable
180+ # FIXME: configure memory
181+ # FIXME: configure timeout
182+ # cpuset_cpus=cpuset,
183+ # required to access perf counters
184+ # alternative: use custom seccomp profile
185+ container_kwargs = {
186+ "image" : container_name ,
187+ "command" : f"/bin/bash /sebs/run_server.sh { self .DEFAULT_PORT } " ,
188+ "volumes" : {code_package .code_location : {"bind" : "/function" , "mode" : "ro" }},
189+ "environment" : environment ,
190+ "privileged" : True ,
191+ "security_opt" : ["seccomp:unconfined" ],
192+ "network_mode" : "bridge" ,
193+ "remove" : self .remove_containers ,
194+ "stdout" : True ,
195+ "stderr" : True ,
196+ "detach" : True ,
197+ # "tty": True,
198+ }
199+
200+ # If SeBS is running on non-linux platforms, container port must be mapped to host port to make it reachable
201+ # Check if the system is NOT Linux or that it is WSL
202+ port = self .DEFAULT_PORT
203+ if not is_linux ():
204+ port_found = False
205+ for p in range (self .DEFAULT_PORT , self .DEFAULT_PORT + 1000 ):
206+ # check no container has been deployed on docker's port p
207+ if p not in self .config .resources .allocated_ports :
208+ # check if port p on the host is free
209+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as s :
210+ s .setsockopt (socket .SOL_SOCKET , socket .SO_REUSEADDR , 1 )
211+ try :
212+ s .bind (("127.0.0.1" , p ))
213+ # The port is available
214+ port = p
215+ port_found = True
216+ self .config .resources .allocated_ports .add (p )
217+ break
218+ except socket .error as e :
219+ # The port is already in use
220+ continue
221+
222+ if not port_found :
223+ raise RuntimeError (
224+ f"Failed to allocate port for container: No ports available between "
225+ f"{ self .DEFAULT_PORT } and { self .DEFAULT_PORT + 999 } "
226+ )
227+
228+ container_kwargs ["command" ] = f"/bin/bash /sebs/run_server.sh { port } "
229+ container_kwargs ["ports" ] = {f'{ port } /tcp' : port }
230+
231+ container = self ._docker_client .containers .run (** container_kwargs )
198232
199233 pid : Optional [int ] = None
200234 if self .measurements_enabled and self ._memory_measurement_path is not None :
@@ -216,7 +250,7 @@ def create_function(self, code_package: Benchmark, func_name: str) -> "LocalFunc
216250 function_cfg = FunctionConfig .from_benchmark (code_package )
217251 func = LocalFunction (
218252 container ,
219- self . DEFAULT_PORT ,
253+ port ,
220254 func_name ,
221255 code_package .benchmark ,
222256 code_package .hash ,
0 commit comments