diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 50483d71..95a362a5 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -22,7 +22,7 @@ body: attributes: label: KatharĂ¡ Version description: "Please provide the KatharĂ¡ version you are using (`kathara -v`)." - placeholder: "3.7.3" + placeholder: "3.7.4" validations: required: true - type: textarea diff --git a/SECURITY.md b/SECURITY.md index 4d19f105..5bfb470d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,7 +6,7 @@ We release patches for security vulnerabilities only for the last version: | Version | Supported Versions | |---------|--------------------| -| 3.7.3 | :white_check_mark: | +| 3.7.4 | :white_check_mark: | ## Reporting a Vulnerability diff --git a/pyproject.toml b/pyproject.toml index ab225635..149cd742 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "kathara" -version = "3.7.3" +version = "3.7.4" description = "A lightweight container-based network emulation tool." readme = "README.md" requires-python = ">=3.9" diff --git a/scripts/Linux-Deb/Makefile b/scripts/Linux-Deb/Makefile index e60c723d..4dfbe944 100644 --- a/scripts/Linux-Deb/Makefile +++ b/scripts/Linux-Deb/Makefile @@ -1,6 +1,6 @@ #!/usr/bin/make -f -VERSION=3.7.3 +VERSION=3.7.4 DEBIAN_PACKAGE_VERSION=1 LAUNCHPAD_NAME=user NO_BINARY_PACKAGES=pyroute2|pyuv|deepdiff diff --git a/scripts/Linux-Deb/debian/changelog b/scripts/Linux-Deb/debian/changelog index ccfe90a8..194d2a7b 100644 --- a/scripts/Linux-Deb/debian/changelog +++ b/scripts/Linux-Deb/debian/changelog @@ -1,9 +1,7 @@ kathara (__VERSION__-__DEBIAN_PACKAGE_VERSION____UBUNTU_VERSION__) __UBUNTU_VERSION__; urgency=low - * New CLI UI using "rich" library! - * (Docker) Show progress during Docker image pull, similarly to the official "docker pull" command - * (Docker) It is now correctly possible to specify a "linux/amd64" image on Apple Silicon (which can be run using Rosetta) - * Add "gnome-terminal" as official supported terminal emulator - * Several API improvements and bug fixes + * (Docker) Fix "rp_filter" on "eth0" that was causing startup errors on some distributions + * (Megalos) Fix watch methods while starting/stopping devices that were causing "lstart"/"lclean" to hang indefinitely + * Minor fixes -- Kathara Team __DATE__ diff --git a/scripts/Linux-Pkg/Makefile b/scripts/Linux-Pkg/Makefile index f8aad0fd..f04bf5c1 100644 --- a/scripts/Linux-Pkg/Makefile +++ b/scripts/Linux-Pkg/Makefile @@ -1,6 +1,6 @@ #!/usr/bin/make -f -VERSION=3.7.3 +VERSION=3.7.4 PACKAGE_VERSION=1 AUR_NAME=user AUR_MAIL=contact@kathara.org diff --git a/scripts/Linux-Pkg/pkginfo/kathara.changelog b/scripts/Linux-Pkg/pkginfo/kathara.changelog index 5f315e11..c68363ab 100644 --- a/scripts/Linux-Pkg/pkginfo/kathara.changelog +++ b/scripts/Linux-Pkg/pkginfo/kathara.changelog @@ -1,8 +1,6 @@ __DATE__ Kathara Team <******@kathara.org> * Release v__VERSION__ - * New CLI UI using "rich" library! - * (Docker) Show progress during Docker image pull, similarly to the official "docker pull" command - * (Docker) It is now correctly possible to specify a "linux/amd64" image on Apple Silicon (which can be run using Rosetta) - * Add "gnome-terminal" as official supported terminal emulator - * Several API improvements and bug fixes \ No newline at end of file + * (Docker) Fix "rp_filter" on "eth0" that was causing startup errors on some distributions + * (Megalos) Fix watch methods while starting/stopping devices that were causing "lstart"/"lclean" to hang indefinitely + * Minor fixes \ No newline at end of file diff --git a/scripts/Linux-Rpm/Makefile b/scripts/Linux-Rpm/Makefile index 497fd726..dc3902fd 100644 --- a/scripts/Linux-Rpm/Makefile +++ b/scripts/Linux-Rpm/Makefile @@ -1,6 +1,6 @@ #!/usr/bin/make -f -VERSION=3.7.3 +VERSION=3.7.4 PACKAGE_VERSION=1 .PHONY: all clean docker-build-image prepare-source prepare-man-pages prepare-bash-completion pack-source build diff --git a/scripts/Linux-Rpm/rpm/kathara.spec b/scripts/Linux-Rpm/rpm/kathara.spec index a8af86d3..bfe511fd 100644 --- a/scripts/Linux-Rpm/rpm/kathara.spec +++ b/scripts/Linux-Rpm/rpm/kathara.spec @@ -68,8 +68,6 @@ chmod g+s %{_libdir}/kathara/kathara %changelog * __DATE__ Kathara Team <******@kathara.org> - __VERSION__-__PACKAGE_VERSION__ -- New CLI UI using "rich" library! -- (Docker) Show progress during Docker image pull, similarly to the official "docker pull" command -- (Docker) It is now correctly possible to specify a "linux/amd64" image on Apple Silicon (which can be run using Rosetta) -- Add "gnome-terminal" as official supported terminal emulator -- Several API improvements and bug fixes \ No newline at end of file +- (Docker) Fix "rp_filter" on "eth0" that was causing startup errors on some distributions +- (Megalos) Fix watch methods while starting/stopping devices that were causing "lstart"/"lclean" to hang indefinitely +- Minor fixes \ No newline at end of file diff --git a/scripts/OSX/Makefile b/scripts/OSX/Makefile index e1830178..3082034a 100644 --- a/scripts/OSX/Makefile +++ b/scripts/OSX/Makefile @@ -1,7 +1,7 @@ #!/usr/bin/make -s PRODUCT=Kathara -VERSION=3.7.3 +VERSION=3.7.4 TARGET_DIRECTORY=Output APPLE_DEVELOPER_CERTIFICATE_ID=FakeID ROFF_DIR=../../docs/Roff diff --git a/scripts/Windows/installer.iss b/scripts/Windows/installer.iss index 7b2585f6..bbc5ae3a 100644 --- a/scripts/Windows/installer.iss +++ b/scripts/Windows/installer.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Kathara" -#define MyAppVersion "3.7.3" +#define MyAppVersion "3.7.4" #define MyAppPublisher "Kathara Team" #define MyAppURL "https://www.kathara.org" #define MyAppExeName "kathara.exe" diff --git a/setup.cfg b/setup.cfg index 4e8b9698..33233c7c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = kathara -version = 3.7.3 +version = 3.7.4 author = Kathara Framework author_email = contact@kathara.org description = A lightweight container-based network emulation tool. diff --git a/setup.py b/setup.py index 4d1c874b..8fbbdd34 100644 --- a/setup.py +++ b/setup.py @@ -7,13 +7,13 @@ package_dir={'': 'src'}, packages=find_packages('src'), py_modules=['kathara'], - version='3.7.3', + version='3.7.4', license='gpl-3.0', description='A lightweight container-based network emulation tool.', author='Kathara Framework', author_email='contact@kathara.org', url='https://www.kathara.org', - download_url='https://github.com/KatharaFramework/Kathara/archive/refs/tags/3.7.3.tar.gz', + download_url='https://github.com/KatharaFramework/Kathara/archive/refs/tags/3.7.4.tar.gz', keywords=['NETWORK-EMULATION', 'CONTAINERS', 'NFV'], install_requires=[ "binaryornot>=0.4.4", diff --git a/src/Kathara/exceptions.py b/src/Kathara/exceptions.py index 87d7aaea..78e06d98 100644 --- a/src/Kathara/exceptions.py +++ b/src/Kathara/exceptions.py @@ -28,7 +28,7 @@ def __init__(self, path: str) -> None: class DockerDaemonConnectionError(Exception): def __init__(self, message: str) -> None: - super().__init__(f"Cannot connect to Docker Daemon: {message}") + super().__init__(f"Cannot connect to Docker Daemon, this may indicate that it is not running. {message}") class NotSupportedError(Exception): diff --git a/src/Kathara/manager/docker/DockerMachine.py b/src/Kathara/manager/docker/DockerMachine.py index 40dc17f3..1271929c 100644 --- a/src/Kathara/manager/docker/DockerMachine.py +++ b/src/Kathara/manager/docker/DockerMachine.py @@ -237,12 +237,20 @@ def create(self, machine: Machine) -> None: # Sysctl params to pass to the container creation sysctl_parameters = {RP_FILTER_NAMESPACE % x: 0 for x in ["all", "default", "lo"]} + sysctl_parameters["net.ipv4.ip_forward"] = 1 + sysctl_parameters["net.ipv4.icmp_ratelimit"] = 0 + sysctl_first_interface = {} if first_machine_iface: - sysctl_parameters[RP_FILTER_NAMESPACE % "eth0"] = 0 + def sysctl_linux(): + if utils.is_wsl_platform(): + return {RP_FILTER_NAMESPACE % "eth0": 0} + return {} - sysctl_parameters["net.ipv4.ip_forward"] = 1 - sysctl_parameters["net.ipv4.icmp_ratelimit"] = 0 + def sysctl_windows(): + return {RP_FILTER_NAMESPACE % "eth0": 0} + + sysctl_first_interface = utils.exec_by_platform(sysctl_linux, sysctl_windows, lambda: {}) if machine.is_ipv6_enabled(): sysctl_parameters["net.ipv6.conf.all.forwarding"] = 1 @@ -252,7 +260,7 @@ def create(self, machine: Machine) -> None: sysctl_parameters["net.ipv6.conf.all.disable_ipv6"] = 0 # Merge machine sysctls - sysctl_parameters = {**sysctl_parameters, **machine.meta['sysctls']} + sysctl_parameters = {**sysctl_parameters, **machine.meta['sysctls'], **sysctl_first_interface} volumes = {} diff --git a/src/Kathara/manager/docker/DockerManager.py b/src/Kathara/manager/docker/DockerManager.py index 521a5c8d..6da47cab 100644 --- a/src/Kathara/manager/docker/DockerManager.py +++ b/src/Kathara/manager/docker/DockerManager.py @@ -5,6 +5,7 @@ import docker import docker.models.containers import docker.models.networks +from docker.errors import DockerException from requests.exceptions import ConnectionError as RequestsConnectionError from .DockerImage import DockerImage @@ -59,13 +60,16 @@ class DockerManager(IManager): @check_docker_status def __init__(self) -> None: remote_url = Setting.get_instance().remote_url - if remote_url is None: - self.client: docker.DockerClient = docker.from_env(timeout=None, max_pool_size=utils.get_pool_size()) - else: - tls_config = docker.tls.TLSConfig(ca_cert=Setting.get_instance().cert_path) - self.client: docker.DockerClient = docker.DockerClient(base_url=remote_url, timeout=None, - max_pool_size=utils.get_pool_size(), - tls=tls_config) + try: + if remote_url is None: + self.client: docker.DockerClient = docker.from_env(timeout=None, max_pool_size=utils.get_pool_size()) + else: + tls_config = docker.tls.TLSConfig(ca_cert=Setting.get_instance().cert_path) + self.client: docker.DockerClient = docker.DockerClient( + base_url=remote_url, timeout=None, max_pool_size=utils.get_pool_size(), tls=tls_config + ) + except DockerException as e: + raise DockerDaemonConnectionError(str(e)) docker_plugin = DockerPlugin(self.client) docker_plugin.check_and_download_plugin() diff --git a/src/Kathara/manager/kubernetes/KubernetesMachine.py b/src/Kathara/manager/kubernetes/KubernetesMachine.py index ca8a2c76..b7013e6f 100644 --- a/src/Kathara/manager/kubernetes/KubernetesMachine.py +++ b/src/Kathara/manager/kubernetes/KubernetesMachine.py @@ -157,6 +157,12 @@ def deploy_machines(self, lab: Lab, selected_machines: Set[str] = None) -> None: # Do not open terminals on Megalos Setting.get_instance().open_terminals = False + wait_thread = threading.Thread( + target=self._wait_machines_startup, + args=(lab, selected_machines if selected_machines else None) + ) + wait_thread.start() + # Deploy all lab machines. # If there is no lab.dep file, machines can be deployed using multithreading. # If not, they're started sequentially @@ -171,7 +177,7 @@ def deploy_machines(self, lab: Lab, selected_machines: Set[str] = None) -> None: for item in machines: self._deploy_machine(item) - self._wait_machines_startup(lab, selected_machines if selected_machines else None) + wait_thread.join() def _wait_machines_startup(self, lab: Lab, selected_machines: Set[str]) -> None: """Wait the startup of the selected machines. Return when the selected machines become `Ready`. @@ -492,6 +498,9 @@ def undeploy(self, lab_hash: str, selected_machines: Optional[Set[str]] = None) selected_machines = {item.metadata.labels["name"] for item in pods} if len(pods) > 0: + wait_thread = threading.Thread(target=self._wait_machines_shutdown, args=(lab_hash, selected_machines)) + wait_thread.start() + pool_size = utils.get_pool_size() items = utils.chunk_list(pods, pool_size) @@ -499,7 +508,7 @@ def undeploy(self, lab_hash: str, selected_machines: Optional[Set[str]] = None) for chunk in items: machines_pool.map(func=self._undeploy_machine, iterable=chunk) - self._wait_machines_shutdown(lab_hash, selected_machines) + wait_thread.join() def _wait_machines_shutdown(self, lab_hash: str, selected_machines: Set[str]): """Wait the shutdown of the selected machines. Return when all the selected machines are terminated. @@ -576,24 +585,15 @@ def _delete_machine(self, pod_api_object: client.V1Pod) -> None: try: shell_env_value = self.get_env_var_value_from_pod(pod_api_object, "_MEGALOS_SHELL") shell = shell_env_value if shell_env_value else Setting.get_instance().device_shell - output = self.exec(machine_namespace, - machine_name, - command=[shell, '-c', shutdown_commands_string], - ) - - try: - next(output) - except StopIteration: - pass - - deployment_name = self.get_deployment_name(machine_name) - self.kubernetes_config_map.delete_for_machine(deployment_name, machine_namespace) - - self.client.delete_namespaced_deployment(name=deployment_name, - namespace=machine_namespace - ) + self.exec(machine_namespace, machine_name, command=[shell, '-c', shutdown_commands_string], is_stream=False) except ApiException: - return + pass + except MachineNotRunningError: + pass + + deployment_name = self.get_deployment_name(machine_name) + self.kubernetes_config_map.delete_for_machine(deployment_name, machine_namespace) + self.client.delete_namespaced_deployment(name=deployment_name, namespace=machine_namespace) def connect(self, lab_hash: str, machine_name: str, shell: Union[str, List[str]] = None, logs: bool = False) \ -> None: diff --git a/src/Kathara/os/Networking.py b/src/Kathara/os/Networking.py index caa8df08..8f92f3a2 100644 --- a/src/Kathara/os/Networking.py +++ b/src/Kathara/os/Networking.py @@ -153,8 +153,7 @@ def remove_interface_ns(interface_name: str, switch_path: str, ns_pid: int) -> N logging.debug("Killing vde_ext process in namespace `%s`." % switch_path) pid_path = os.path.join(switch_path, f"pid_{interface_name}") - command = f"/bin/sh -c 'kill -2 $(cat {pid_path})'" - + command = f"/bin/sh -c 'if [ -f \"{pid_path}\" ]; then kill -2 $(cat {pid_path}); fi'" logging.debug("Running command `%s` in namespace `%s`..." % (command, switch_path)) nsenter(ns_pid, command, ns_types=['ipc', 'net', 'pid', 'uts']) diff --git a/src/Kathara/utils.py b/src/Kathara/utils.py index c9552e46..a8c2a951 100644 --- a/src/Kathara/utils.py +++ b/src/Kathara/utils.py @@ -130,6 +130,13 @@ def is_platform(desired_platform: str) -> bool: return _platform == desired_platform +def is_wsl_platform() -> bool: + if _platform == LINUX or _platform == LINUX2: + info = os.uname() + return 'microsoft' in info.release.lower() + return False + + def exec_by_platform(fun_linux: Callable, fun_windows: Callable, fun_mac: Callable) -> Any: if _platform == LINUX or _platform == LINUX2: return fun_linux() diff --git a/src/Kathara/version.py b/src/Kathara/version.py index 9d2552d0..da56c8ba 100644 --- a/src/Kathara/version.py +++ b/src/Kathara/version.py @@ -1,6 +1,6 @@ from typing import Tuple -CURRENT_VERSION = "3.7.3" +CURRENT_VERSION = "3.7.4" def parse(version: str) -> Tuple: diff --git a/tests/manager/docker/docker_machine_test.py b/tests/manager/docker/docker_machine_test.py index f171b883..9d076889 100644 --- a/tests/manager/docker/docker_machine_test.py +++ b/tests/manager/docker/docker_machine_test.py @@ -229,11 +229,13 @@ def test_create_privileged(mock_get_current_user_name, mock_setting_get_instance assert not mock_copy_files.called +@mock.patch("src.Kathara.utils._platform", "linux") @mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.get_machines_api_objects_by_filters") @mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.copy_files") @mock.patch("src.Kathara.setting.Setting.Setting.get_instance") +@mock.patch("src.Kathara.utils.is_wsl_platform") @mock.patch("src.Kathara.utils.get_current_user_name") -def test_create_interface(mock_get_current_user_name, mock_setting_get_instance, mock_copy_files, +def test_create_interface(mock_get_current_user_name, mock_is_wsl_platform, mock_setting_get_instance, mock_copy_files, mock_get_machines_api_objects_by_filters, docker_machine, default_device): class LinkApiObj: def __init__(self, name): @@ -247,6 +249,7 @@ def __init__(self, name): docker_machine.client.api.create_endpoint_config.return_value = {} mock_get_machines_api_objects_by_filters.return_value = [] mock_get_current_user_name.return_value = "test-user" + mock_is_wsl_platform.return_value = False setting_mock = Mock() setting_mock.configure_mock(**{ @@ -269,6 +272,280 @@ def __init__(self, name): network="link_a", network_mode='bridge', networking_config={"link_a": {}}, + sysctls={'net.ipv4.conf.all.rp_filter': 0, + 'net.ipv4.conf.default.rp_filter': 0, + 'net.ipv4.conf.lo.rp_filter': 0, + 'net.ipv4.ip_forward': 1, + 'net.ipv4.icmp_ratelimit': 0, + }, + environment={}, + mem_limit='64m', + nano_cpus=2000000000, + ports=None, + tty=True, + stdin_open=True, + detach=True, + volumes={}, + labels={'name': 'test_device', 'lab_hash': '9pe3y6IDMwx4PfOPu5mbNg', 'user': 'test-user', 'app': 'kathara', + 'shell': '/bin/bash'} + ) + + assert not mock_copy_files.called + + +@mock.patch("src.Kathara.utils._platform", "linux") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.get_machines_api_objects_by_filters") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.copy_files") +@mock.patch("src.Kathara.setting.Setting.Setting.get_instance") +@mock.patch("src.Kathara.utils.is_wsl_platform") +@mock.patch("src.Kathara.utils.get_current_user_name") +def test_create_interface_on_wsl(mock_get_current_user_name, mock_is_wsl_platform, mock_setting_get_instance, + mock_copy_files, mock_get_machines_api_objects_by_filters, docker_machine, + default_device): + class LinkApiObj: + def __init__(self, name): + self.name = name + + link = Link(default_device.lab, "A") + link.api_object = LinkApiObj("link_a") + + default_device.add_interface(link, 0) + + docker_machine.client.api.create_endpoint_config.return_value = {} + mock_get_machines_api_objects_by_filters.return_value = [] + mock_get_current_user_name.return_value = "test-user" + mock_is_wsl_platform.return_value = True + + setting_mock = Mock() + setting_mock.configure_mock(**{ + 'shared_cds': SharedCollisionDomainsOption.NOT_SHARED, + 'device_prefix': 'dev_prefix', + "device_shell": '/bin/bash', + 'enable_ipv6': False, + 'remote_url': None, + 'hosthome_mount': False, + 'shared_mount': False + }) + mock_setting_get_instance.return_value = setting_mock + docker_machine.create(default_device) + docker_machine.client.containers.create.assert_called_once_with( + image='kathara/test', + name='dev_prefix_test-user_test_device_9pe3y6IDMwx4PfOPu5mbNg', + hostname='test_device', + cap_add=['NET_ADMIN', 'NET_RAW', 'NET_BROADCAST', 'NET_BIND_SERVICE', 'SYS_ADMIN'], + privileged=False, + network="link_a", + network_mode='bridge', + networking_config={"link_a": {}}, + sysctls={'net.ipv4.conf.all.rp_filter': 0, + 'net.ipv4.conf.default.rp_filter': 0, + 'net.ipv4.conf.lo.rp_filter': 0, + 'net.ipv4.conf.eth0.rp_filter': 0, + 'net.ipv4.ip_forward': 1, + 'net.ipv4.icmp_ratelimit': 0, + }, + environment={}, + mem_limit='64m', + nano_cpus=2000000000, + ports=None, + tty=True, + stdin_open=True, + detach=True, + volumes={}, + labels={'name': 'test_device', 'lab_hash': '9pe3y6IDMwx4PfOPu5mbNg', 'user': 'test-user', 'app': 'kathara', + 'shell': '/bin/bash'} + ) + + assert not mock_copy_files.called + + +@mock.patch("src.Kathara.utils._platform", "win32") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.get_machines_api_objects_by_filters") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.copy_files") +@mock.patch("src.Kathara.setting.Setting.Setting.get_instance") +@mock.patch("src.Kathara.utils.get_current_user_name") +def test_create_interface_win(mock_get_current_user_name, mock_setting_get_instance, + mock_copy_files, mock_get_machines_api_objects_by_filters, docker_machine, + default_device): + class LinkApiObj: + def __init__(self, name): + self.name = name + + link = Link(default_device.lab, "A") + link.api_object = LinkApiObj("link_a") + + default_device.add_interface(link, 0) + + docker_machine.client.api.create_endpoint_config.return_value = {} + mock_get_machines_api_objects_by_filters.return_value = [] + mock_get_current_user_name.return_value = "test-user" + + setting_mock = Mock() + setting_mock.configure_mock(**{ + 'shared_cds': SharedCollisionDomainsOption.NOT_SHARED, + 'device_prefix': 'dev_prefix', + "device_shell": '/bin/bash', + 'enable_ipv6': False, + 'remote_url': None, + 'hosthome_mount': False, + 'shared_mount': False + }) + mock_setting_get_instance.return_value = setting_mock + docker_machine.create(default_device) + docker_machine.client.containers.create.assert_called_once_with( + image='kathara/test', + name='dev_prefix_test-user_test_device_9pe3y6IDMwx4PfOPu5mbNg', + hostname='test_device', + cap_add=['NET_ADMIN', 'NET_RAW', 'NET_BROADCAST', 'NET_BIND_SERVICE', 'SYS_ADMIN'], + privileged=False, + network="link_a", + network_mode='bridge', + networking_config={"link_a": {}}, + sysctls={'net.ipv4.conf.all.rp_filter': 0, + 'net.ipv4.conf.default.rp_filter': 0, + 'net.ipv4.conf.lo.rp_filter': 0, + 'net.ipv4.conf.eth0.rp_filter': 0, + 'net.ipv4.ip_forward': 1, + 'net.ipv4.icmp_ratelimit': 0, + }, + environment={}, + mem_limit='64m', + nano_cpus=2000000000, + ports=None, + tty=True, + stdin_open=True, + detach=True, + volumes={}, + labels={'name': 'test_device', 'lab_hash': '9pe3y6IDMwx4PfOPu5mbNg', 'user': 'test-user', 'app': 'kathara', + 'shell': '/bin/bash'} + ) + + assert not mock_copy_files.called + + +@mock.patch("src.Kathara.utils._platform", "linux") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.get_machines_api_objects_by_filters") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.copy_files") +@mock.patch("src.Kathara.setting.Setting.Setting.get_instance") +@mock.patch("src.Kathara.utils.is_wsl_platform") +@mock.patch("src.Kathara.utils.get_current_user_name") +def test_create_interface_mac_addr(mock_get_current_user_name, mock_is_wsl_platform, mock_setting_get_instance, + mock_copy_files, mock_get_machines_api_objects_by_filters, docker_machine, + default_device): + class LinkApiObj: + def __init__(self, name): + self.name = name + + link = Link(default_device.lab, "A") + link.api_object = LinkApiObj("link_a") + + expected_mac_addr = "00:00:00:00:ff:ff" + default_device.add_interface(link, 0, expected_mac_addr) + + docker_machine.client.api.create_endpoint_config.return_value = { + 'driver_opt': {'kathara.mac_addr': expected_mac_addr} + } + mock_get_machines_api_objects_by_filters.return_value = [] + mock_get_current_user_name.return_value = "test-user" + mock_is_wsl_platform.return_value = False + + setting_mock = Mock() + setting_mock.configure_mock(**{ + 'shared_cds': SharedCollisionDomainsOption.NOT_SHARED, + 'device_prefix': 'dev_prefix', + "device_shell": '/bin/bash', + 'enable_ipv6': False, + 'remote_url': None, + 'hosthome_mount': False, + 'shared_mount': False + }) + mock_setting_get_instance.return_value = setting_mock + docker_machine.create(default_device) + + docker_machine.client.api.create_endpoint_config.assert_called_once_with( + driver_opt={'kathara.mac_addr': expected_mac_addr} + ) + docker_machine.client.containers.create.assert_called_once_with( + image='kathara/test', + name='dev_prefix_test-user_test_device_9pe3y6IDMwx4PfOPu5mbNg', + hostname='test_device', + cap_add=['NET_ADMIN', 'NET_RAW', 'NET_BROADCAST', 'NET_BIND_SERVICE', 'SYS_ADMIN'], + privileged=False, + network="link_a", + network_mode='bridge', + networking_config={"link_a": {'driver_opt': {'kathara.mac_addr': expected_mac_addr}}}, + sysctls={'net.ipv4.conf.all.rp_filter': 0, + 'net.ipv4.conf.default.rp_filter': 0, + 'net.ipv4.conf.lo.rp_filter': 0, + 'net.ipv4.ip_forward': 1, + 'net.ipv4.icmp_ratelimit': 0, + }, + environment={}, + mem_limit='64m', + nano_cpus=2000000000, + ports=None, + tty=True, + stdin_open=True, + detach=True, + volumes={}, + labels={'name': 'test_device', 'lab_hash': '9pe3y6IDMwx4PfOPu5mbNg', 'user': 'test-user', 'app': 'kathara', + 'shell': '/bin/bash'} + ) + + assert not mock_copy_files.called + + +@mock.patch("src.Kathara.utils._platform", "linux") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.get_machines_api_objects_by_filters") +@mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.copy_files") +@mock.patch("src.Kathara.setting.Setting.Setting.get_instance") +@mock.patch("src.Kathara.utils.is_wsl_platform") +@mock.patch("src.Kathara.utils.get_current_user_name") +def test_create_interface_mac_addr_on_wsl(mock_get_current_user_name, mock_is_wsl_platform, mock_setting_get_instance, + mock_copy_files, mock_get_machines_api_objects_by_filters, docker_machine, + default_device): + class LinkApiObj: + def __init__(self, name): + self.name = name + + link = Link(default_device.lab, "A") + link.api_object = LinkApiObj("link_a") + + expected_mac_addr = "00:00:00:00:ff:ff" + default_device.add_interface(link, 0, expected_mac_addr) + + docker_machine.client.api.create_endpoint_config.return_value = { + 'driver_opt': {'kathara.mac_addr': expected_mac_addr} + } + mock_get_machines_api_objects_by_filters.return_value = [] + mock_get_current_user_name.return_value = "test-user" + mock_is_wsl_platform.return_value = True + + setting_mock = Mock() + setting_mock.configure_mock(**{ + 'shared_cds': SharedCollisionDomainsOption.NOT_SHARED, + 'device_prefix': 'dev_prefix', + "device_shell": '/bin/bash', + 'enable_ipv6': False, + 'remote_url': None, + 'hosthome_mount': False, + 'shared_mount': False + }) + mock_setting_get_instance.return_value = setting_mock + docker_machine.create(default_device) + + docker_machine.client.api.create_endpoint_config.assert_called_once_with( + driver_opt={'kathara.mac_addr': expected_mac_addr} + ) + docker_machine.client.containers.create.assert_called_once_with( + image='kathara/test', + name='dev_prefix_test-user_test_device_9pe3y6IDMwx4PfOPu5mbNg', + hostname='test_device', + cap_add=['NET_ADMIN', 'NET_RAW', 'NET_BROADCAST', 'NET_BIND_SERVICE', 'SYS_ADMIN'], + privileged=False, + network="link_a", + network_mode='bridge', + networking_config={"link_a": {'driver_opt': {'kathara.mac_addr': expected_mac_addr}}}, sysctls={'net.ipv4.conf.all.rp_filter': 0, 'net.ipv4.conf.default.rp_filter': 0, 'net.ipv4.conf.lo.rp_filter': 0, @@ -291,12 +568,14 @@ def __init__(self, name): assert not mock_copy_files.called +@mock.patch("src.Kathara.utils._platform", "win32") @mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.get_machines_api_objects_by_filters") @mock.patch("src.Kathara.manager.docker.DockerMachine.DockerMachine.copy_files") @mock.patch("src.Kathara.setting.Setting.Setting.get_instance") @mock.patch("src.Kathara.utils.get_current_user_name") -def test_create_interface_mac_addr(mock_get_current_user_name, mock_setting_get_instance, mock_copy_files, - mock_get_machines_api_objects_by_filters, docker_machine, default_device): +def test_create_interface_mac_addr_win(mock_get_current_user_name, mock_setting_get_instance, + mock_copy_files, mock_get_machines_api_objects_by_filters, docker_machine, + default_device): class LinkApiObj: def __init__(self, name): self.name = name