From f6d4a198852203b742fec9c7e5918eda380dde5c Mon Sep 17 00:00:00 2001 From: cHJlaXpoZXI <54248704+cHJlaXpoZXI@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:05:35 +0100 Subject: [PATCH 01/31] Update ContainerConfig.py Delete line that block Orbstack to mount /etc/localtime --- exegol/model/ContainerConfig.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 79fd6502..56321e17 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -963,8 +963,6 @@ def addVolume(self, logger.critical(msg) raise CancelOperation(msg) if path_match.startswith("/etc/"): - if EnvInfo.isOrbstack(): - raise CancelOperation(f"{EnvInfo.getDockerEngine().value} doesn't support sharing [magenta]/etc[/magenta] files with the container") path_match = path_match.replace("/etc/", "/private/etc/") if EnvInfo.isDockerDesktop(): match = False From 4a4e9f47e53028fc236f3defb105e3d4f10b5036 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Tue, 21 May 2024 18:08:51 +0200 Subject: [PATCH 02/31] timezone sharing, fallback to TZ --- exegol/config/ConstantConfig.py | 2 +- exegol/model/ContainerConfig.py | 91 +++++++++++++++++---------------- requirements.txt | 2 +- tests/test_exegol.py | 2 +- 4 files changed, 51 insertions(+), 46 deletions(-) diff --git a/exegol/config/ConstantConfig.py b/exegol/config/ConstantConfig.py index 5bd0e4c0..65ede8d4 100644 --- a/exegol/config/ConstantConfig.py +++ b/exegol/config/ConstantConfig.py @@ -5,7 +5,7 @@ class ConstantConfig: """Constant parameters information""" # Exegol Version - version: str = "4.3.4" + version: str = "4.3.5b1" # Exegol documentation link documentation: str = "https://exegol.rtfd.io/" diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 58d9c494..0cae12b1 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -8,6 +8,7 @@ from datetime import datetime from enum import Enum from pathlib import Path, PurePath +from time import tzname from typing import Optional, List, Dict, Union, Tuple, cast from docker.models.containers import Container @@ -38,6 +39,12 @@ class ContainerConfig: __static_gui_envs = {"_JAVA_AWT_WM_NONREPARENTING": "1", "QT_X11_NO_MITSHM": "1"} __default_desktop_port = {"http": 6080, "vnc": 5900} + # Verbose only filters + __verbose_only_envs = ["DISPLAY", "WAYLAND_DISPLAY", "XDG_SESSION_TYPE", "XDG_RUNTIME_DIR", "PATH", "TZ"] + __verbose_only_mounts = ['/tmp/.X11-unix', '/opt/resources', '/etc/localtime', + '/etc/timezone', '/my-resources', '/opt/my-resources', + '/.exegol/entrypoint.sh', '/.exegol/spawn.sh', '/tmp/wayland-0', '/tmp/wayland-1'] + class ExegolFeatures(Enum): shell_logging = "org.exegol.feature.shell_logging" desktop = "org.exegol.feature.desktop" @@ -123,6 +130,9 @@ def __parseContainerConfig(self, container: Container): """Parse Docker object to setup self configuration""" # Reset default attributes self.__passwd = None + self.__share_timezone = False + self.__my_resources = False + self.__enable_gui = False # Container Config section container_config = container.attrs.get("Config", {}) self.tty = container_config.get("Tty", True) @@ -130,14 +140,6 @@ def __parseContainerConfig(self, container: Container): self.__parseLabels(container_config.get("Labels", {})) self.interactive = container_config.get("OpenStdin", True) self.legacy_entrypoint = container_config.get("Entrypoint") is None - self.__enable_gui = False - envs_key = self.__envs.keys() - if "DISPLAY" in envs_key: - self.__enable_gui = True - self.__gui_engine.append("X11") - if "WAYLAND_DISPLAY" in envs_key: - self.__enable_gui = True - self.__gui_engine.append("Wayland") # Host Config section host_config = container.attrs.get("HostConfig", {}) @@ -155,8 +157,6 @@ def __parseContainerConfig(self, container: Container): logger.debug(f"└── Load devices : {self.__devices}") # Volumes section - self.__share_timezone = False - self.__my_resources = False self.__parseMounts(container.attrs.get("Mounts", []), container.name.replace('exegol-', '')) # Network section @@ -170,6 +170,15 @@ def __parseEnvs(self, envs: List[str]): logger.debug(f"└── Parsing envs : {env}") # Removing " and ' at the beginning and the end of the string before splitting key / value self.addRawEnv(env.strip("'").strip('"')) + envs_key = self.__envs.keys() + if "DISPLAY" in envs_key: + self.__enable_gui = True + self.__gui_engine.append("X11") + if "WAYLAND_DISPLAY" in envs_key: + self.__enable_gui = True + self.__gui_engine.append("Wayland") + if "TZ" in envs_key: + self.__share_timezone = True def __parseLabels(self, labels: Dict[str, str]): """Parse envs object syntax""" @@ -416,37 +425,36 @@ def __disableGUI(self): def enableSharedTimezone(self): """Procedure to enable shared timezone feature""" - if EnvInfo.is_windows_shell: - logger.warning("Timezone sharing is not supported from a Windows shell. Skipping.") - return - elif EnvInfo.isMacHost(): - # On Orbstack /etc cannot be shared + we should test how Orbstack handle symlink - # With docker desktop, symlink are resolved as full path on container creation. When tzdata is updated on the host, the container can no longer be started because the files of the previous package version are missing. - # TODO Test if env var can be used as replacement - logger.warning("Timezone sharing on Mac is not supported (for stability reasons). Skipping.") - return if not self.__share_timezone: logger.verbose("Config: Enabling host timezones") - # Try to share /etc/timezone (deprecated old timezone file) - try: - self.addVolume("/etc/timezone", "/etc/timezone", read_only=True, must_exist=True) - logger.verbose("Volume was successfully added for [magenta]/etc/timezone[/magenta]") - timezone_loaded = True - except CancelOperation: - logger.verbose("File /etc/timezone is missing on host, cannot create volume for this.") - timezone_loaded = False - # Try to share /etc/localtime (new timezone file) - try: - self.addVolume("/etc/localtime", "/etc/localtime", read_only=True, must_exist=True) - logger.verbose("Volume was successfully added for [magenta]/etc/localtime[/magenta]") - except CancelOperation as e: - if not timezone_loaded: - # If neither file was found, disable the functionality - logger.error(f"The host's timezone could not be shared: {e}") - return + if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): + if len(tzname) > 0: + logger.debug(f"Sharing timezone via TZ env var: '{tzname[0]}'") + self.addEnv("TZ", tzname[0]) else: - logger.warning("File [magenta]/etc/localtime[/magenta] is [orange3]missing[/orange3] on host, " - "cannot create volume for this. Relying instead on [magenta]/etc/timezone[/magenta] [orange3](deprecated)[/orange3].") + logger.warning("Your system timezone cannot be shared.") + return + else: + # Try to share /etc/timezone (deprecated old timezone file) + try: + self.addVolume("/etc/timezone", "/etc/timezone", read_only=True, must_exist=True) + logger.verbose("Volume was successfully added for [magenta]/etc/timezone[/magenta]") + timezone_loaded = True + except CancelOperation: + logger.verbose("File /etc/timezone is missing on host, cannot create volume for this.") + timezone_loaded = False + # Try to share /etc/localtime (new timezone file) + try: + self.addVolume("/etc/localtime", "/etc/localtime", read_only=True, must_exist=True) + logger.verbose("Volume was successfully added for [magenta]/etc/localtime[/magenta]") + except CancelOperation as e: + if not timezone_loaded: + # If neither file was found, disable the functionality + logger.error(f"The host's timezone could not be shared: {e}") + return + else: + logger.warning("File [magenta]/etc/localtime[/magenta] is [orange3]missing[/orange3] on host, " + "cannot create volume for this. Relying instead on [magenta]/etc/timezone[/magenta] [orange3](deprecated)[/orange3].") self.__share_timezone = True def __disableSharedTimezone(self): @@ -1378,12 +1386,9 @@ def getTextCreationDate(self) -> str: def getTextMounts(self, verbose: bool = False) -> str: """Text formatter for Mounts configurations. The verbose mode does not exclude technical volumes.""" result = '' - hidden_mounts = ['/tmp/.X11-unix', '/opt/resources', '/etc/localtime', - '/etc/timezone', '/my-resources', '/opt/my-resources', - '/.exegol/entrypoint.sh', '/.exegol/spawn.sh', '/tmp/wayland-0', '/tmp/wayland-1'] for mount in self.__mounts: # Not showing technical mounts - if not verbose and mount.get('Target') in hidden_mounts: + if not verbose and mount.get('Target') in self.__verbose_only_mounts: continue read_only_text = f"[bright_black](RO)[/bright_black] " if verbose else '' read_write_text = f"[orange3](RW)[/orange3] " if verbose else '' @@ -1409,7 +1414,7 @@ def getTextEnvs(self, verbose: bool = False) -> str: result = '' for k, v in self.__envs.items(): # Blacklist technical variables, only shown in verbose - if not verbose and k in list(self.__static_gui_envs.keys()) + [v.value for v in self.ExegolEnv] + ["DISPLAY", "WAYLAND_DISPLAY", "XDG_SESSION_TYPE", "XDG_RUNTIME_DIR", "PATH"]: + if not verbose and k in list(self.__static_gui_envs.keys()) + [v.value for v in self.ExegolEnv] + self.__verbose_only_envs: continue result += f"{k}={v}{os.linesep}" return result diff --git a/requirements.txt b/requirements.txt index b18d4f68..0bae87ca 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ requests~=2.31.0 rich~=13.7.1 GitPython~=3.1.43 PyYAML>=6.0.1 -argcomplete~=3.3.0 \ No newline at end of file +argcomplete~=3.3.0 diff --git a/tests/test_exegol.py b/tests/test_exegol.py index 564e78ef..2de37b16 100644 --- a/tests/test_exegol.py +++ b/tests/test_exegol.py @@ -2,4 +2,4 @@ def test_version(): - assert __version__ == '4.3.4' + assert __version__ == '4.3.5' From 8a76e49a91bc6f68f35bd11c42a6a564f4dfe403 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Tue, 21 May 2024 18:19:19 +0200 Subject: [PATCH 03/31] Update TODO --- exegol/model/ContainerConfig.py | 1 + exegol/utils/GuiUtils.py | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 0cae12b1..5a0d8e6b 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -428,6 +428,7 @@ def enableSharedTimezone(self): if not self.__share_timezone: logger.verbose("Config: Enabling host timezones") if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): + # TODO improve tz resolution if len(tzname) > 0: logger.debug(f"Sharing timezone via TZ env var: '{tzname[0]}'") self.addEnv("TZ", tzname[0]) diff --git a/exegol/utils/GuiUtils.py b/exegol/utils/GuiUtils.py index 607a912e..aabd93be 100644 --- a/exegol/utils/GuiUtils.py +++ b/exegol/utils/GuiUtils.py @@ -41,10 +41,9 @@ def isWaylandGuiAvailable(cls) -> bool: :return: bool """ if EnvInfo.isWindowsHost(): - return False # TODO To Be defined (WSLg works fine for now) - # elif EnvInfo.isMacHost(): - # return False - # Linux or Mac, rely on var env settings + return False + elif EnvInfo.isMacHost(): + return False return EnvInfo.isWaylandAvailable() @classmethod From d75e8a9acc8947e96fbf4d553bcfbc44da537814 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 22 May 2024 13:32:04 +0200 Subject: [PATCH 04/31] improved timezone resolution --- exegol/model/ContainerConfig.py | 13 +++++++------ requirements.txt | 1 + 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 5a0d8e6b..eefcb111 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -8,9 +8,9 @@ from datetime import datetime from enum import Enum from pathlib import Path, PurePath -from time import tzname from typing import Optional, List, Dict, Union, Tuple, cast +import tzlocal from docker.models.containers import Container from docker.types import Mount from rich.prompt import Prompt @@ -428,10 +428,10 @@ def enableSharedTimezone(self): if not self.__share_timezone: logger.verbose("Config: Enabling host timezones") if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): - # TODO improve tz resolution - if len(tzname) > 0: - logger.debug(f"Sharing timezone via TZ env var: '{tzname[0]}'") - self.addEnv("TZ", tzname[0]) + current_tz = tzlocal.get_localzone_name() + if current_tz: + logger.debug(f"Sharing timezone via TZ env var: '{current_tz}'") + self.addEnv("TZ", current_tz) else: logger.warning("Your system timezone cannot be shared.") return @@ -1019,7 +1019,8 @@ def addVolume(self, break if not match: logger.error(f"Bind volume from {host_path} is not possible, Docker Desktop configuration is [red]incorrect[/red].") - logger.critical(f"You need to modify the [green]Docker Desktop[/green] config and [green]add[/green] this path (or the root directory) in [magenta]Docker Desktop > Preferences > Resources > File Sharing[/magenta] configuration.") + logger.critical(f"You need to modify the [green]Docker Desktop[/green] config and [green]add[/green] this path (or the root directory) in " + f"[magenta]Docker Desktop > Preferences > Resources > File Sharing[/magenta] configuration.") # Choose to update fs directory perms if available and depending on user choice # if force_sticky_group is set, user choice is bypassed, fs will be updated. execute_update_fs = force_sticky_group or (enable_sticky_group and (UserConfig().auto_update_workspace_fs ^ ParametersManager().update_fs_perms)) diff --git a/requirements.txt b/requirements.txt index 0bae87ca..1a0b8d04 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,3 +5,4 @@ rich~=13.7.1 GitPython~=3.1.43 PyYAML>=6.0.1 argcomplete~=3.3.0 +tzlocal~=5.2 From 1fe745f370a598c8425ea8dddda8d01efe26a00d Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 22 May 2024 13:36:11 +0200 Subject: [PATCH 05/31] local import lzlocal --- exegol/model/ContainerConfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index eefcb111..61122fb1 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -10,7 +10,6 @@ from pathlib import Path, PurePath from typing import Optional, List, Dict, Union, Tuple, cast -import tzlocal from docker.models.containers import Container from docker.types import Mount from rich.prompt import Prompt @@ -428,7 +427,8 @@ def enableSharedTimezone(self): if not self.__share_timezone: logger.verbose("Config: Enabling host timezones") if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): - current_tz = tzlocal.get_localzone_name() + from tzlocal import get_localzone_name + current_tz = get_localzone_name() if current_tz: logger.debug(f"Sharing timezone via TZ env var: '{current_tz}'") self.addEnv("TZ", current_tz) From cbfcba32f1b748ec01694d4339c5eb30bce5da89 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 22 May 2024 14:53:09 +0200 Subject: [PATCH 06/31] Add lzlocal types for mypy tests --- .github/workflows/sub_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sub_testing.yml b/.github/workflows/sub_testing.yml index cd7db48c..ccf313fd 100644 --- a/.github/workflows/sub_testing.yml +++ b/.github/workflows/sub_testing.yml @@ -16,7 +16,7 @@ jobs: with: python-version: "3.12" - name: Install requirements - run: python -m pip install --user mypy types-requests types-PyYAML + run: python -m pip install --user mypy types-requests types-PyYAML types-tzlocal - name: Run code analysis (package) run: mypy ./exegol/ --ignore-missing-imports --check-untyped-defs --pretty # TODO add --disallow-untyped-defs - name: Run code analysis (source) From bc20e98b53e7de486ce57ed96c9b546b9653c223 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 22 May 2024 15:14:31 +0200 Subject: [PATCH 07/31] Update xhost command --- exegol/model/ExegolContainer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exegol/model/ExegolContainer.py b/exegol/model/ExegolContainer.py index c3238278..901c92c9 100644 --- a/exegol/model/ExegolContainer.py +++ b/exegol/model/ExegolContainer.py @@ -341,9 +341,9 @@ def __applyXhostACL(self): with console.status(f"Starting XQuartz...", spinner_style="blue"): os.system(f"xhost + localhost > /dev/null") else: - logger.debug(f"Adding xhost ACL to local:{self.config.hostname}") + logger.debug(f"Adding xhost ACL to local:{self.config.getUsername()}") # add linux local ACL - os.system(f"xhost +local:{self.config.hostname} > /dev/null") + os.system(f"xhost +local:{self.config.getUsername()} > /dev/null") def __updatePasswd(self): """ From a717a748629d689e557f93b65cebff64563192e8 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 22 May 2024 15:14:54 +0200 Subject: [PATCH 08/31] Add WSL debug logs --- exegol/utils/GuiUtils.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/exegol/utils/GuiUtils.py b/exegol/utils/GuiUtils.py index aabd93be..d64d1fb3 100644 --- a/exegol/utils/GuiUtils.py +++ b/exegol/utils/GuiUtils.py @@ -281,11 +281,16 @@ def __wsl_available(cls) -> bool: if EnvInfo.isWindowsHost(): wsl = shutil.which("wsl.exe") if not wsl: + logger.debug("wsl.exe not found on the local system.") return False ret = subprocess.Popen(["wsl.exe", "--status"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) ret.wait() if ret.returncode == 0: return True + else: + logger.debug(f"wsl.exe --status return code {ret.returncode}") + logger.debug(str(ret.stdout)) + logger.debug(str(ret.stderr)) logger.debug("WSL status command failed.. Trying a fallback check method.") return cls.__wsl_test("/etc/os-release", name=None) or cls.__wsl_test("/etc/os-release") From 85132edf2af3f1a22669e6e01cb7e22ba67e1d31 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 22 May 2024 15:16:43 +0200 Subject: [PATCH 09/31] Install missing types-tzlocal for tests --- .github/workflows/sub_testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/sub_testing.yml b/.github/workflows/sub_testing.yml index ccf313fd..15c201b1 100644 --- a/.github/workflows/sub_testing.yml +++ b/.github/workflows/sub_testing.yml @@ -42,6 +42,6 @@ jobs: with: python-version: "3.12" - name: Install requirements - run: python -m pip install --user mypy types-requests types-PyYAML + run: python -m pip install --user mypy types-requests types-PyYAML types-tzlocal - name: Check python compatibility for ${{ matrix.os }}/${{ matrix.version }} run: mypy ./exegol.py --ignore-missing-imports --check-untyped-defs --python-version ${{ matrix.version }} --platform ${{ matrix.os }} From 948fbafaa312fca14f4ccaf92f7e56a650a16c90 Mon Sep 17 00:00:00 2001 From: QU35T-code Date: Wed, 5 Jun 2024 19:57:19 +0200 Subject: [PATCH 10/31] Update setup.py adding dynamic requirements --- setup.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 07dec25f..89d4c500 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,9 @@ for k, v in data_files_dict.items(): data_files.append((k, v)) +with open("requirements.txt", "r", encoding="utf-8") as f: + requirements = [x.strip() for x in f.readlines()] + setup( name='Exegol', version=__version__, @@ -54,14 +57,7 @@ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", "Operating System :: OS Independent", ], - install_requires=[ - 'docker~=7.0.0', - 'requests~=2.31.0', - 'rich~=13.7.1', - 'PyYAML', - 'GitPython~=3.1.43', - 'argcomplete~=3.3.0' - ], + install_requires=requirements, packages=find_packages(exclude=["tests"]), include_package_data=True, data_files=data_files, From f8726a3da889a7839d4d5c94a58ffe0ea7a6b96d Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sun, 23 Jun 2024 19:43:37 +0200 Subject: [PATCH 11/31] import tzlocal on startup Signed-off-by: Dramelac --- exegol/model/ContainerConfig.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 61122fb1..764b0c73 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -26,6 +26,9 @@ from exegol.utils.ExeLog import logger, ExeLog from exegol.utils.GuiUtils import GuiUtils +if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): + from tzlocal import get_localzone_name + class ContainerConfig: """Configuration class of an exegol container""" @@ -427,7 +430,6 @@ def enableSharedTimezone(self): if not self.__share_timezone: logger.verbose("Config: Enabling host timezones") if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): - from tzlocal import get_localzone_name current_tz = get_localzone_name() if current_tz: logger.debug(f"Sharing timezone via TZ env var: '{current_tz}'") From e57c5d670528be199e3f6cd16e3c44f354d90293 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sun, 23 Jun 2024 19:44:21 +0200 Subject: [PATCH 12/31] Add container status to recap table Signed-off-by: Dramelac --- exegol/console/TUI.py | 7 +++++-- exegol/model/ExegolContainer.py | 6 +++--- exegol/model/ExegolContainerTemplate.py | 3 +++ 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/exegol/console/TUI.py b/exegol/console/TUI.py index 893d4cbe..0364bc73 100644 --- a/exegol/console/TUI.py +++ b/exegol/console/TUI.py @@ -7,6 +7,7 @@ from rich.prompt import Prompt from rich.table import Table +from exegol.config.EnvInfo import EnvInfo from exegol.console import ConsoleFormat from exegol.console.ConsoleFormat import boolFormatter, getColor, richLen from exegol.console.ExegolProgress import ExegolProgress @@ -17,7 +18,6 @@ from exegol.model.ExegolContainerTemplate import ExegolContainerTemplate from exegol.model.ExegolImage import ExegolImage from exegol.model.SelectableInterface import SelectableInterface -from exegol.config.EnvInfo import EnvInfo from exegol.utils.ExeLog import logger, console, ExeLog @@ -437,7 +437,10 @@ def __buildContainerRecapTable(container: ExegolContainerTemplate): recap.title = "[not italic]:white_medium_star: [/not italic][gold3][g]Container summary[/g][/gold3]" # Header recap.add_column(f"[bold blue]Name[/bold blue]{os.linesep}[bold blue]Image[/bold blue]", justify="right") - container_info_header = f"{container.getDisplayName()}{os.linesep}{container.image.getName()}" + container_status = container.getTextStatus() + + container_info_header = (f"{container.getDisplayName()} {'(' + container_status + ')' if container_status else ''}{os.linesep}" + f"{container.image.getName()}") if "N/A" not in container.image.getImageVersion(): container_info_header += f" - v.{container.image.getImageVersion()}" if "Unknown" not in container.image.getStatus(): diff --git a/exegol/model/ExegolContainer.py b/exegol/model/ExegolContainer.py index 901c92c9..e49f4800 100644 --- a/exegol/model/ExegolContainer.py +++ b/exegol/model/ExegolContainer.py @@ -77,10 +77,10 @@ def getTextStatus(self) -> str: if status == "unknown": return "Unknown" elif status == "exited": - return "[red]Stopped" + return "[red]Stopped[/red]" elif status == "running": - return "[green]Running" - return status + return "[green]Running[/green]" + return f"[orange3]{status}[/orange3]" def isNew(self) -> bool: """Check if the container has just been created or not""" diff --git a/exegol/model/ExegolContainerTemplate.py b/exegol/model/ExegolContainerTemplate.py index b0f3b678..2b4b9fed 100644 --- a/exegol/model/ExegolContainerTemplate.py +++ b/exegol/model/ExegolContainerTemplate.py @@ -46,3 +46,6 @@ def getDisplayName(self) -> str: if self.container_name != self.config.hostname: return f"{self.name} [bright_black]({self.config.hostname})[/bright_black]" return self.name + + def getTextStatus(self) -> str: + return "" From 8d737b67b1a23ee2f504c07ad48625ea520c1858 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sun, 23 Jun 2024 19:48:07 +0200 Subject: [PATCH 13/31] Upgrade docker and requests libs Signed-off-by: Dramelac --- requirements.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 1a0b8d04..b1542428 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,5 @@ -docker~=7.0.0 -# Request holdback: temp fix for https://github.com/docker/docker-py/issues/3256 -requests~=2.31.0 +docker~=7.1.0 +requests~=2.32.3 rich~=13.7.1 GitPython~=3.1.43 PyYAML>=6.0.1 From 6bb665b2c4ff28040d2f9a449f6ccbcb7be300a1 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Mon, 1 Jul 2024 19:03:12 +0200 Subject: [PATCH 14/31] Fix dep check race error --- exegol/model/ContainerConfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 764b0c73..303a1150 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -26,7 +26,7 @@ from exegol.utils.ExeLog import logger, ExeLog from exegol.utils.GuiUtils import GuiUtils -if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): +if EnvInfo.is_windows_shell or EnvInfo.is_mac_shell: from tzlocal import get_localzone_name @@ -429,7 +429,7 @@ def enableSharedTimezone(self): """Procedure to enable shared timezone feature""" if not self.__share_timezone: logger.verbose("Config: Enabling host timezones") - if EnvInfo.is_windows_shell or EnvInfo.isMacHost(): + if EnvInfo.is_windows_shell or EnvInfo.is_mac_shell: current_tz = get_localzone_name() if current_tz: logger.debug(f"Sharing timezone via TZ env var: '{current_tz}'") From 71476a726d1b174b8b483058ff849573e64b8a59 Mon Sep 17 00:00:00 2001 From: Ranma <54248704+cHJlaXpoZXI@users.noreply.github.com> Date: Tue, 2 Jul 2024 15:36:20 +0000 Subject: [PATCH 15/31] Update ContainerConfig.py --- exegol/model/ContainerConfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index dd367767..393f7860 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -1008,7 +1008,7 @@ def addVolume(self, msg += " Your exegol installation cannot be stored under this directory." logger.critical(msg) raise CancelOperation(msg) - if path_match.startswith("/etc/"): + if path_match.startswith("/etc/") and EnvInfo.isDockerDesktop(): path_match = path_match.replace("/etc/", "/private/etc/") if EnvInfo.isDockerDesktop(): match = False From c11ff72486f4067202ec6bbff42f35b6caa7f21d Mon Sep 17 00:00:00 2001 From: Dramelac Date: Mon, 8 Jul 2024 21:37:01 +0200 Subject: [PATCH 16/31] Orbstack warning about device sharing Signed-off-by: Dramelac --- exegol/model/ContainerConfig.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 62ed4b06..f72bcf8e 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -1280,9 +1280,13 @@ def addRawVolume(self, volume_string): def addUserDevice(self, user_device_config: str): """Add a device from a user parameters""" - if EnvInfo.isDockerDesktop() and user_device_config not in self.__whitelist_dd_devices: - logger.warning("Docker desktop (Windows & macOS) does not support USB device passthrough.") - logger.verbose("Official doc: https://docs.docker.com/desktop/faqs/#can-i-pass-through-a-usb-device-to-a-container") + if (EnvInfo.isDockerDesktop() or EnvInfo.isOrbstack()) and user_device_config not in self.__whitelist_dd_devices: + if EnvInfo.isDockerDesktop(): + logger.warning("Docker desktop (Windows & macOS) does not support USB device passthrough.") + logger.verbose("Official doc: https://docs.docker.com/desktop/faqs/#can-i-pass-through-a-usb-device-to-a-container") + elif EnvInfo.isOrbstack(): + logger.warning("Orbstack does not support (yet) USB device passthrough.") + logger.verbose("Official doc: https://docs.orbstack.dev/machines/#usb-devices") logger.critical("Device configuration cannot be applied, aborting operation.") self.__addDevice(user_device_config) From c4c477dadbfe6773ae4d16724776e72eb47df12c Mon Sep 17 00:00:00 2001 From: Dramelac Date: Mon, 8 Jul 2024 22:12:50 +0200 Subject: [PATCH 17/31] Add debug logging for Windows WSLg Signed-off-by: Dramelac --- exegol/utils/GuiUtils.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/exegol/utils/GuiUtils.py b/exegol/utils/GuiUtils.py index d64d1fb3..07178c65 100644 --- a/exegol/utils/GuiUtils.py +++ b/exegol/utils/GuiUtils.py @@ -212,14 +212,16 @@ def __windowsGuiChecks(cls) -> bool: if not cls.__wsl_available(): logger.error("WSL is [orange3]not available[/orange3] on your system. X11 sharing is not supported.") return False + logger.debug("WSL is [green]available[/green] on the local system") # Only WSL2 support WSLg if EnvInfo.getDockerEngine() != EnvInfo.DockerEngine.WLS2: + logger.debug(f"Docker current engine: {EnvInfo.getDockerEngine().value}") logger.error("Docker must be run with [orange3]WSL2[/orange3] engine in order to support X11 sharing (i.e. GUI apps).") return False - logger.debug("WSL is [green]available[/green] and docker is using WSL2") + logger.debug("Docker is using [green]WSL2[/green]") # X11 socket can only be shared from a WSL (to find WSLg mount point) if EnvInfo.current_platform != "WSL": - logger.debug("Exegol is running from a Windows context (e.g. Powershell), a WSL instance must be found to share WSLg X11 socket") + logger.debug("Exegol is running from a Windows context (e.g. Powershell), a WSL instance must be found to share the WSLg X11 socket") cls.__distro_name = cls.__find_wsl_distro() logger.debug(f"Set WSL Distro as: '{cls.__distro_name}'") # If no WSL is found, propose to continue without GUI (X11 sharing) @@ -251,12 +253,16 @@ def __wsl_test(path, name: Optional[str] = "docker-desktop") -> bool: if EnvInfo.isWindowsHost(): wsl = shutil.which("wsl.exe") if not wsl: + logger.warning("wsl.exe seems to be unavailable on your system.") return False if name is None: + logger.debug(f"Running: wsl.exe test -f {path}") ret = subprocess.run(["wsl.exe", "test", "-f", path]) else: + logger.debug(f"Running: wsl.exe test -d {name} -f {path}") ret = subprocess.run(["wsl.exe", "-d", name, "test", "-f", path]) return ret.returncode == 0 + logger.debug("Trying to run a WSL test without Windows?") return False @classmethod @@ -283,6 +289,7 @@ def __wsl_available(cls) -> bool: if not wsl: logger.debug("wsl.exe not found on the local system.") return False + logger.debug("running: wsl.exe --status") ret = subprocess.Popen(["wsl.exe", "--status"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) ret.wait() if ret.returncode == 0: @@ -304,10 +311,12 @@ def __wslg_installed(cls) -> bool: if (Path("/mnt/host/wslg/versions.txt").is_file() or Path("/mnt/wslg/versions.txt").is_file()): return True + logger.debug("Unable to find WSLg locally.. Check /mnt/wslg/ or /mnt/host/wslg/") else: if (cls.__wsl_test("/mnt/host/wslg/versions.txt", name=cls.__distro_name) or cls.__wsl_test("/mnt/wslg/versions.txt", name=cls.__distro_name)): return True + logger.debug(f"Unable to find WSLg.. Check /mnt/wslg/ or /mnt/host/wslg/ on {cls.__distro_name}") logger.debug("WSLg check failed.. Trying a fallback check method.") return cls.__wsl_test("/mnt/host/wslg/versions.txt") or cls.__wsl_test("/mnt/wslg/versions.txt", name=None) @@ -322,14 +331,15 @@ def __wslg_eligible() -> bool: return True try: os_version_raw, _, build_number_raw = EnvInfo.getWindowsRelease().split('.')[:3] + os_version = int(os_version_raw) except ValueError: logger.debug(f"Impossible to find the version of windows: '{EnvInfo.getWindowsRelease()}'") logger.error("Exegol can't know if your [orange3]version of Windows[/orange3] can support dockerized GUIs (X11 sharing).") return False # Available for Windows 10 & 11 - os_version = int(os_version_raw) if os_version >= 10: return True + logger.debug(f"Current version of Windows doesn't support WSLg: {os_version_raw}.?.{build_number_raw}") return False @classmethod @@ -337,6 +347,7 @@ def __find_wsl_distro(cls) -> str: distro_name = "" # these distros cannot be used to load WSLg socket blacklisted_distro = ["docker-desktop", "docker-desktop-data"] + logger.debug("Running: C:\\Windows\\system32\\wsl.exe -l") ret = subprocess.Popen(["C:\\Windows\\system32\\wsl.exe", "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # Wait for WSL process to end ret.wait() @@ -391,6 +402,7 @@ def __find_wsl_distro(cls) -> str: @classmethod def __create_default_wsl(cls) -> bool: logger.info("Creating Ubuntu WSL distribution. Please wait.") + logger.debug("Running: C:\\Windows\\system32\\wsl.exe --install -d Ubuntu") ret = subprocess.Popen(["C:\\Windows\\system32\\wsl.exe", "--install", "-d", "Ubuntu"], stderr=subprocess.PIPE) ret.wait() logger.info("Please follow installation instructions on the new window.") @@ -406,6 +418,7 @@ def __create_default_wsl(cls) -> bool: docker_settings = EnvInfo.getDockerDesktopSettings() if docker_settings is not None and docker_settings.get("enableIntegrationWithDefaultWslDistro", False): logger.verbose("Set WSL Ubuntu as default to automatically enable docker integration") + logger.debug("Running: C:\\Windows\\system32\\wsl.exe -s Ubuntu") # Set new WSL distribution as default to start it and enable docker integration ret = subprocess.Popen(["C:\\Windows\\system32\\wsl.exe", "-s", "Ubuntu"], stderr=subprocess.PIPE) ret.wait() From efaed58375930f767d358794ad99c59de3fb3b80 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Mon, 8 Jul 2024 22:38:17 +0200 Subject: [PATCH 18/31] Add link to the official doc on -h Signed-off-by: Dramelac --- exegol/utils/argParse.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/exegol/utils/argParse.py b/exegol/utils/argParse.py index ca3aa63a..4d65f06f 100644 --- a/exegol/utils/argParse.py +++ b/exegol/utils/argParse.py @@ -1,8 +1,9 @@ import argparse -import argcomplete from logging import CRITICAL from typing import IO, Optional, List, Union, Dict, cast +import argcomplete + from exegol.console.cli.actions.Command import Command, Option from exegol.utils.ExeLog import logger @@ -19,8 +20,9 @@ def _print_message(self, message: str, file: Optional[IO[str]] = None) -> None: class Parser: """Custom Exegol CLI Parser. Main controller of argument building and parsing.""" - __description = "This Python script is a wrapper for Exegol. It can be used to easily manage Exegol on " \ - "your machine." + __description = """This Python script is a wrapper for Exegol. It can be used to easily manage Exegol on your machine. + +[red]Official documentation[/red]: https://exegol.rtfd.io""" __formatter_class = argparse.RawTextHelpFormatter def __init__(self, actions: List[Command]): @@ -54,7 +56,9 @@ def __set_action_parser(self) -> None: # the 'help' description of the current action is retrieved # from the comment of the corresponding action class sub_parser = self.subParser.add_parser(action.name, help=action.__doc__, - description=action.__doc__, + description=action.__doc__ + f""" + +[red]Official documentation[/red]: https://exegol.rtfd.io/en/latest/exegol-wrapper/{action.name}.html""", epilog=action.formatEpilog(), formatter_class=self.__formatter_class) sub_parser.set_defaults(action=action) From 0fbbbd4ab0984799ee4977611911aaeec78967b0 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Tue, 9 Jul 2024 22:06:12 +0200 Subject: [PATCH 19/31] Improve sysctl handling --- exegol/model/ContainerConfig.py | 13 +++++++------ exegol/utils/FsUtils.py | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index f72bcf8e..b8086dbb 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -24,6 +24,7 @@ from exegol.model.ExegolModules import ExegolModules from exegol.utils import FsUtils from exegol.utils.ExeLog import logger, ExeLog +from exegol.utils.FsUtils import check_sysctl_value from exegol.utils.GuiUtils import GuiUtils if EnvInfo.is_windows_shell or EnvInfo.is_mac_shell: @@ -638,9 +639,8 @@ def enableVPN(self, config_path: Optional[str] = None): skip_sysctl = False if self.__network_host and EnvInfo.is_linux_shell: # Check if IPv6 have been disabled on the host with sysctl - with open('/proc/sys/net/ipv6/conf/all/disable_ipv6', 'r') as conf: - if int(conf.read()) == 0: - skip_sysctl = True + if check_sysctl_value("net.ipv6.conf.all.disable_ipv6", "0"): + skip_sysctl = True if not skip_sysctl: self.__addSysctl("net.ipv6.conf.all.disable_ipv6", "0") # Add tun device, this device is needed to create VPN tunnels @@ -883,17 +883,18 @@ def __removeCapability(self, cap_string: str): # When the capability is not present return False - def __addSysctl(self, sysctl_key: str, config: str): + def __addSysctl(self, sysctl_key: str, config: Union[str, int]): """Add a linux sysctl to the container""" if sysctl_key in self.__sysctls.keys(): logger.warning(f"Sysctl {sysctl_key} already setup to '{self.__sysctls[sysctl_key]}'. Skipping.") return - if self.__network_host: + # Docs of supported sysctl by linux / docker: https://docs.docker.com/reference/cli/docker/container/run/#currently-supported-sysctls + if self.__network_host and sysctl_key.startswith('net.'): logger.warning(f"The sysctl container configuration is [red]not[/red] supported by docker in [blue]host[/blue] network mode.") logger.warning(f"Skipping the sysctl config: [magenta]{sysctl_key}[/magenta] = [orange3]{config}[/orange3].") logger.warning(f"If this configuration is mandatory in your situation, try to change it in sudo mode on your host.") return - self.__sysctls[sysctl_key] = config + self.__sysctls[sysctl_key] = str(config) def __removeSysctl(self, sysctl_key: str): """Remove a linux capability from the container's config""" diff --git a/exegol/utils/FsUtils.py b/exegol/utils/FsUtils.py index 2e1267c7..5d81f58e 100644 --- a/exegol/utils/FsUtils.py +++ b/exegol/utils/FsUtils.py @@ -90,3 +90,17 @@ def setGidPermission(root_folder: Path): logger.raw(f"sudo chgrp -R $(id -g) {root_folder} && sudo find {root_folder} -type d -exec chmod g+rws {{}} \\;", level=logging.WARNING) logger.empty_line() logger.empty_line() + + +def check_sysctl_value(sysctl: str, compare_to: str) -> bool: + sysctl_path = "/proc/sys/" + sysctl.replace('.', '/') + try: + with open(sysctl_path, 'r') as conf: + config = conf.read().strip() + logger.debug(f"Checking sysctl value {sysctl}={config} (compare to {compare_to})") + return conf.read().strip() == compare_to + except FileNotFoundError: + logger.debug(f"Sysctl file {sysctl} not found!") + except PermissionError: + logger.debug(f"Unable to read sysctl {sysctl} permission!") + return False From ed4ae9d9832f66c6ec5b3abac29395f81bb35d42 Mon Sep 17 00:00:00 2001 From: Shutdown <40902872+ShutdownRepo@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:38:07 +0200 Subject: [PATCH 20/31] Reformating doc link print and mounting limitations msg --- exegol/model/ContainerConfig.py | 4 ++-- exegol/utils/argParse.py | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index b8086dbb..649a30d6 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -1007,14 +1007,14 @@ def addVolume(self, # TODO check if path_match + replace really useful , path_match rever used path_match = host_path if path_match.startswith("/opt/") and EnvInfo.isOrbstack(): - msg = f"{EnvInfo.getDockerEngine().value} cannot mount directory from [magenta]/opt/[/magenta] host path." + msg = f"{EnvInfo.getDockerEngine().value} cannot mount directory from /opt/ host path." if path_match.endswith("entrypoint.sh") or path_match.endswith("spawn.sh"): msg += " Your exegol installation cannot be stored under this directory." logger.critical(msg) raise CancelOperation(msg) if path_match.startswith("/etc/"): if EnvInfo.isOrbstack(): - raise CancelOperation(f"{EnvInfo.getDockerEngine().value} doesn't support sharing [magenta]/etc[/magenta] files with the container") + raise CancelOperation(f"{EnvInfo.getDockerEngine().value} doesn't support sharing /etc files with the container") path_match = path_match.replace("/etc/", "/private/etc/") if EnvInfo.isDockerDesktop(): match = False diff --git a/exegol/utils/argParse.py b/exegol/utils/argParse.py index 4d65f06f..b0e7cc45 100644 --- a/exegol/utils/argParse.py +++ b/exegol/utils/argParse.py @@ -22,7 +22,7 @@ class Parser: __description = """This Python script is a wrapper for Exegol. It can be used to easily manage Exegol on your machine. -[red]Official documentation[/red]: https://exegol.rtfd.io""" +[bold magenta]Exegol documentation:[/bold magenta] [underline magenta]https://exegol.rtfd.io[/underline magenta]""" __formatter_class = argparse.RawTextHelpFormatter def __init__(self, actions: List[Command]): @@ -56,9 +56,8 @@ def __set_action_parser(self) -> None: # the 'help' description of the current action is retrieved # from the comment of the corresponding action class sub_parser = self.subParser.add_parser(action.name, help=action.__doc__, - description=action.__doc__ + f""" - -[red]Official documentation[/red]: https://exegol.rtfd.io/en/latest/exegol-wrapper/{action.name}.html""", + description=action.__doc__ + f"""\n +[bold magenta]Exegol documentation:[/bold magenta] [underline magenta]https://exegol.rtfd.io/en/latest/exegol-wrapper/{action.name}.html[/underline magenta]""", epilog=action.formatEpilog(), formatter_class=self.__formatter_class) sub_parser.set_defaults(action=action) From 265163a38723d627eed2f82d8b95fa9158f60326 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Tue, 23 Jul 2024 18:19:16 +0200 Subject: [PATCH 21/31] Fix multi same tag removal --- exegol/manager/ExegolManager.py | 8 +++++++- exegol/utils/argParse.py | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/exegol/manager/ExegolManager.py b/exegol/manager/ExegolManager.py index ab93d90f..3cef4cbb 100644 --- a/exegol/manager/ExegolManager.py +++ b/exegol/manager/ExegolManager.py @@ -362,7 +362,13 @@ def __loadOrCreateContainer(cls, # Return cache return cls.__container container_tag: Optional[str] = override_container if override_container is not None else ParametersManager().containertag - container_tags: Optional[Sequence[str]] = ParametersManager().multicontainertag + container_tags: Optional[List[str]] = None + if ParametersManager().multicontainertag: + container_tags = [] + for tag in ParametersManager().multicontainertag: + # Prevent duplicate tag selection + if tag not in container_tags: + container_tags.append(tag) try: if container_tag is None and (container_tags is None or len(container_tags) == 0): # Interactive container selection diff --git a/exegol/utils/argParse.py b/exegol/utils/argParse.py index b0e7cc45..31a5e900 100644 --- a/exegol/utils/argParse.py +++ b/exegol/utils/argParse.py @@ -55,6 +55,8 @@ def __set_action_parser(self) -> None: # Each action has a dedicated sub-parser with different options # the 'help' description of the current action is retrieved # from the comment of the corresponding action class + if action.__doc__ is None: + action.__doc__ = "Unknown action" sub_parser = self.subParser.add_parser(action.name, help=action.__doc__, description=action.__doc__ + f"""\n [bold magenta]Exegol documentation:[/bold magenta] [underline magenta]https://exegol.rtfd.io/en/latest/exegol-wrapper/{action.name}.html[/underline magenta]""", From f0491a98161b05869de32e685ca7a2fd3e70a32b Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sun, 11 Aug 2024 17:30:34 +0200 Subject: [PATCH 22/31] Remove legacy /etc volume fix --- exegol/model/ContainerConfig.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 393f7860..40371474 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -84,7 +84,7 @@ def __init__(self, container: Optional[Container] = None): self.__gui_engine: List[str] = [] self.__share_timezone: bool = False self.__my_resources: bool = False - self.__my_resources_path: str = "/opt/my-resources" + self.__my_resources_: str = "/opt/my-resources" self.__exegol_resources: bool = False self.__network_host: bool = True self.__privileged: bool = False @@ -1000,21 +1000,17 @@ def addVolume(self, # Docker Desktop for Windows based on WSL2 don't have filesystem limitation if EnvInfo.isMacHost(): # Add support for /etc - # TODO check if path_match + replace really useful , path_match rever used - path_match = host_path - if path_match.startswith("/opt/") and EnvInfo.isOrbstack(): + if host_path.startswith("/opt/") and EnvInfo.isOrbstack(): msg = f"{EnvInfo.getDockerEngine().value} cannot mount directory from [magenta]/opt/[/magenta] host path." - if path_match.endswith("entrypoint.sh") or path_match.endswith("spawn.sh"): + if host_path.endswith("entrypoint.sh") or host_path.endswith("spawn.sh"): msg += " Your exegol installation cannot be stored under this directory." logger.critical(msg) raise CancelOperation(msg) - if path_match.startswith("/etc/") and EnvInfo.isDockerDesktop(): - path_match = path_match.replace("/etc/", "/private/etc/") if EnvInfo.isDockerDesktop(): match = False # Find a match for resource in EnvInfo.getDockerDesktopResources(): - if path_match.startswith(resource): + if host_path.startswith(resource): match = True break if not match: From e6cd786b79f4fb9cf09249cd85ed5178a3884d70 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sun, 11 Aug 2024 17:32:00 +0200 Subject: [PATCH 23/31] Fix typo --- exegol/model/ContainerConfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 40371474..b1df71e9 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -84,7 +84,7 @@ def __init__(self, container: Optional[Container] = None): self.__gui_engine: List[str] = [] self.__share_timezone: bool = False self.__my_resources: bool = False - self.__my_resources_: str = "/opt/my-resources" + self.__my_resources_path: str = "/opt/my-resources" self.__exegol_resources: bool = False self.__network_host: bool = True self.__privileged: bool = False From a385ec543075054c8d6564bec727553b5a3cc88e Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sun, 11 Aug 2024 17:19:18 +0200 Subject: [PATCH 24/31] Prevent using root on Windows WSL if the path to wsl.exe is incomplete Signed-off-by: Dramelac --- exegol/utils/GuiUtils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/exegol/utils/GuiUtils.py b/exegol/utils/GuiUtils.py index 07178c65..47ccc741 100644 --- a/exegol/utils/GuiUtils.py +++ b/exegol/utils/GuiUtils.py @@ -210,6 +210,8 @@ def __windowsGuiChecks(cls) -> bool: logger.debug("Testing WSLg availability") # WSL + WSLg must be available on the Windows host for the GUI to work through X11 sharing if not cls.__wsl_available(): + if sys.platform != "win32" and os.getuid() == 0: + logger.critical("You are running exegol as [red]root[/red]! The root user cannot be used to run Exegol on a Windows environment.") logger.error("WSL is [orange3]not available[/orange3] on your system. X11 sharing is not supported.") return False logger.debug("WSL is [green]available[/green] on the local system") From 62ab2ac3540e0c7f412e50eec970331d7c3a55b0 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Mon, 12 Aug 2024 20:22:27 +0200 Subject: [PATCH 25/31] Add log symlink for vnc service --- exegol/utils/imgsync/entrypoint.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/exegol/utils/imgsync/entrypoint.sh b/exegol/utils/imgsync/entrypoint.sh index d5a2262b..0141d166 100755 --- a/exegol/utils/imgsync/entrypoint.sh +++ b/exegol/utils/imgsync/entrypoint.sh @@ -93,6 +93,7 @@ function desktop() { if command -v desktop-start &> /dev/null then echo "Starting Exegol [green]desktop[/green] with [blue]${EXEGOL_DESKTOP_PROTO}[/blue]" + ln -sf /root/.vnc /var/log/exegol/vnc desktop-start &>> ~/.vnc/startup.log # Disable logging sleep 2 # Waiting 2 seconds for the Desktop to start before continuing else From e4c0e911ac978a9d95fc42e6a4447cc19040acf4 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 14 Aug 2024 16:20:32 +0200 Subject: [PATCH 26/31] Fix xquartz render --- exegol/model/ContainerConfig.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index 7f401adf..b903d49c 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -43,7 +43,7 @@ class ContainerConfig: __default_desktop_port = {"http": 6080, "vnc": 5900} # Verbose only filters - __verbose_only_envs = ["DISPLAY", "WAYLAND_DISPLAY", "XDG_SESSION_TYPE", "XDG_RUNTIME_DIR", "PATH", "TZ"] + __verbose_only_envs = ["DISPLAY", "WAYLAND_DISPLAY", "XDG_SESSION_TYPE", "XDG_RUNTIME_DIR", "PATH", "TZ", "_JAVA_OPTIONS"] __verbose_only_mounts = ['/tmp/.X11-unix', '/opt/resources', '/etc/localtime', '/etc/timezone', '/my-resources', '/opt/my-resources', '/.exegol/entrypoint.sh', '/.exegol/spawn.sh', '/tmp/wayland-0', '/tmp/wayland-1'] @@ -413,6 +413,11 @@ def enableGUI(self): # TODO support pulseaudio for k, v in self.__static_gui_envs.items(): self.addEnv(k, v) + + # Fix XQuartz render: https://github.com/ThePorgs/Exegol/issues/229 + if EnvInfo.isMacHost(): + self.addEnv("_JAVA_OPTIONS", '-Dsun.java2d.xrender=false') + self.__enable_gui = True def __disableGUI(self): From 5742934fc6dd2b77decdfc3c0389a257378d94c0 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Wed, 14 Aug 2024 16:54:46 +0200 Subject: [PATCH 27/31] Add NO_PROXY support --- exegol/utils/WebUtils.py | 3 +++ exegol/utils/imgsync/entrypoint.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/exegol/utils/WebUtils.py b/exegol/utils/WebUtils.py index c3da213d..d6da2580 100644 --- a/exegol/utils/WebUtils.py +++ b/exegol/utils/WebUtils.py @@ -132,6 +132,9 @@ def __runRequest(cls, url: str, service_name: str, headers: Optional[Dict] = Non https_proxy = os.environ.get('HTTPS_PROXY') or os.environ.get('https_proxy') if https_proxy: proxies['https'] = https_proxy + no_proxy = os.environ.get('NO_PROXY') or os.environ.get('no_proxy') + if no_proxy: + proxies['no_proxy'] = no_proxy response = requests.request(method=method, url=url, timeout=(10, 20), verify=ParametersManager().verify, headers=headers, data=data, proxies=proxies if len(proxies) > 0 else None) return response except requests.exceptions.HTTPError as e: diff --git a/exegol/utils/imgsync/entrypoint.sh b/exegol/utils/imgsync/entrypoint.sh index 0141d166..cc700c38 100755 --- a/exegol/utils/imgsync/entrypoint.sh +++ b/exegol/utils/imgsync/entrypoint.sh @@ -93,7 +93,7 @@ function desktop() { if command -v desktop-start &> /dev/null then echo "Starting Exegol [green]desktop[/green] with [blue]${EXEGOL_DESKTOP_PROTO}[/blue]" - ln -sf /root/.vnc /var/log/exegol/vnc + ln -sf /root/.vnc /var/log/exegol/desktop desktop-start &>> ~/.vnc/startup.log # Disable logging sleep 2 # Waiting 2 seconds for the Desktop to start before continuing else From a9af859ab1a671147b6123223d46cece22aa9154 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sat, 17 Aug 2024 12:48:37 +0200 Subject: [PATCH 28/31] Handle cancel operation when creating new container --- exegol/manager/ExegolManager.py | 94 +++++++++++++++++---------------- exegol/model/ContainerConfig.py | 2 + 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/exegol/manager/ExegolManager.py b/exegol/manager/ExegolManager.py index 3cef4cbb..627f8cc9 100644 --- a/exegol/manager/ExegolManager.py +++ b/exegol/manager/ExegolManager.py @@ -444,51 +444,55 @@ def __interactiveSelection(cls, @classmethod def __prepareContainerConfig(cls): """Create Exegol configuration with user input""" - # Create default exegol config - config = ContainerConfig() - # Container configuration from user CLI options - if ParametersManager().X11: - config.enableGUI() - if ParametersManager().share_timezone: - config.enableSharedTimezone() - config.setNetworkMode(ParametersManager().host_network) - if ParametersManager().ports is not None: - for port in ParametersManager().ports: - config.addRawPort(port) - if ParametersManager().my_resources: - config.enableMyResources() - if ParametersManager().exegol_resources: - config.enableExegolResources() - if ParametersManager().log: - config.enableShellLogging(ParametersManager().log_method, - UserConfig().shell_logging_compress ^ ParametersManager().log_compress) - if ParametersManager().workspace_path: - if ParametersManager().mount_current_dir: - logger.warning(f'Workspace conflict detected (-cwd cannot be use with -w). Using: {ParametersManager().workspace_path}') - config.setWorkspaceShare(ParametersManager().workspace_path) - elif ParametersManager().mount_current_dir: - config.enableCwdShare() - if ParametersManager().privileged: - config.setPrivileged() - elif ParametersManager().capabilities is not None: - for cap in ParametersManager().capabilities: - config.addCapability(cap) - if ParametersManager().volumes is not None: - for volume in ParametersManager().volumes: - config.addRawVolume(volume) - if ParametersManager().devices is not None: - for device in ParametersManager().devices: - config.addUserDevice(device) - if ParametersManager().vpn is not None: - config.enableVPN() - if ParametersManager().envs is not None: - for env in ParametersManager().envs: - config.addRawEnv(env) - if UserConfig().desktop_default_enable ^ ParametersManager().desktop: - config.enableDesktop(ParametersManager().desktop_config) - if ParametersManager().comment: - config.addComment(ParametersManager().comment) - return config + try: + # Create default exegol config + config = ContainerConfig() + # Container configuration from user CLI options + if ParametersManager().X11: + config.enableGUI() + if ParametersManager().share_timezone: + config.enableSharedTimezone() + config.setNetworkMode(ParametersManager().host_network) + if ParametersManager().ports is not None: + for port in ParametersManager().ports: + config.addRawPort(port) + if ParametersManager().my_resources: + config.enableMyResources() + if ParametersManager().exegol_resources: + config.enableExegolResources() + if ParametersManager().log: + config.enableShellLogging(ParametersManager().log_method, + UserConfig().shell_logging_compress ^ ParametersManager().log_compress) + if ParametersManager().workspace_path: + if ParametersManager().mount_current_dir: + logger.warning(f'Workspace conflict detected (-cwd cannot be use with -w). Using: {ParametersManager().workspace_path}') + config.setWorkspaceShare(ParametersManager().workspace_path) + elif ParametersManager().mount_current_dir: + config.enableCwdShare() + if ParametersManager().privileged: + config.setPrivileged() + elif ParametersManager().capabilities is not None: + for cap in ParametersManager().capabilities: + config.addCapability(cap) + if ParametersManager().volumes is not None: + for volume in ParametersManager().volumes: + config.addRawVolume(volume) + if ParametersManager().devices is not None: + for device in ParametersManager().devices: + config.addUserDevice(device) + if ParametersManager().vpn is not None: + config.enableVPN() + if ParametersManager().envs is not None: + for env in ParametersManager().envs: + config.addRawEnv(env) + if UserConfig().desktop_default_enable ^ ParametersManager().desktop: + config.enableDesktop(ParametersManager().desktop_config) + if ParametersManager().comment: + config.addComment(ParametersManager().comment) + return config + except CancelOperation as e: + logger.critical(f"Unable to create a new container: {e}") + raise e @classmethod def __createContainer(cls, name: Optional[str]) -> ExegolContainer: diff --git a/exegol/model/ContainerConfig.py b/exegol/model/ContainerConfig.py index b903d49c..c34ba06b 100644 --- a/exegol/model/ContainerConfig.py +++ b/exegol/model/ContainerConfig.py @@ -1014,6 +1014,8 @@ def addVolume(self, if host_path.endswith("entrypoint.sh") or host_path.endswith("spawn.sh"): msg += " Your exegol installation cannot be stored under this directory." logger.critical(msg) + else: + msg += f" The volume {host_path} cannot be mounted to the container, please move it outside of this directory." raise CancelOperation(msg) if EnvInfo.isDockerDesktop(): match = False From f66bffb2c3c4a1001bbb6ebd606654f6c9f54808 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sat, 17 Aug 2024 12:48:52 +0200 Subject: [PATCH 29/31] Fix version action --- exegol/console/cli/actions/ExegolParameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exegol/console/cli/actions/ExegolParameters.py b/exegol/console/cli/actions/ExegolParameters.py index cbe0addb..2c8e10bb 100644 --- a/exegol/console/cli/actions/ExegolParameters.py +++ b/exegol/console/cli/actions/ExegolParameters.py @@ -289,4 +289,4 @@ class Version(Command): """Print current Exegol version""" def __call__(self, *args, **kwargs): - return ExegolManager.print_version + return lambda: None From 4538f5f3df119148b0de98afa7b70440778a2351 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sat, 24 Aug 2024 16:32:14 +0200 Subject: [PATCH 30/31] Handle git errors as raw message --- exegol/manager/ExegolController.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/exegol/manager/ExegolController.py b/exegol/manager/ExegolController.py index ad36116a..3c90ca4f 100644 --- a/exegol/manager/ExegolController.py +++ b/exegol/manager/ExegolController.py @@ -1,3 +1,5 @@ +import logging + try: import docker import requests @@ -69,8 +71,16 @@ def main(): logger.info("Exiting") except git.exc.GitCommandError as git_error: print_exception_banner() + # Printing git stderr as raw to avoid any Rich parsing error + logger.debug("Full git output:") + logger.raw(git_error, level=logging.DEBUG) + logger.empty_line() error = git_error.stderr.strip().split(": ")[-1].strip("'") - logger.critical(f"A critical error occurred while running this git command: {' '.join(git_error.command)} => {error}") + logger.error("Git error received:") + # Printing git error as raw to avoid any Rich parsing error + logger.raw(error, level=logging.ERROR) + logger.empty_line() + logger.critical(f"A critical error occurred while running this git command: {' '.join(git_error.command)}") except Exception: print_exception_banner() console.print_exception(show_locals=True, suppress=[docker, requests, git]) From 5cdf5731fc4ad48eb654b0ce3a6866f60843a692 Mon Sep 17 00:00:00 2001 From: Dramelac Date: Sat, 24 Aug 2024 16:48:02 +0200 Subject: [PATCH 31/31] New stable version --- exegol-resources | 2 +- exegol/config/ConstantConfig.py | 2 +- requirements.txt | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/exegol-resources b/exegol-resources index 833f0359..314c14d8 160000 --- a/exegol-resources +++ b/exegol-resources @@ -1 +1 @@ -Subproject commit 833f035933eec193fc8a32cc31c44eee80564a6b +Subproject commit 314c14d8e275f6d9111b7f434b3f846444fdbf60 diff --git a/exegol/config/ConstantConfig.py b/exegol/config/ConstantConfig.py index 65ede8d4..c4519dde 100644 --- a/exegol/config/ConstantConfig.py +++ b/exegol/config/ConstantConfig.py @@ -5,7 +5,7 @@ class ConstantConfig: """Constant parameters information""" # Exegol Version - version: str = "4.3.5b1" + version: str = "4.3.5" # Exegol documentation link documentation: str = "https://exegol.rtfd.io/" diff --git a/requirements.txt b/requirements.txt index b1542428..af0af7db 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,6 +2,6 @@ docker~=7.1.0 requests~=2.32.3 rich~=13.7.1 GitPython~=3.1.43 -PyYAML>=6.0.1 -argcomplete~=3.3.0 +PyYAML>=6.0.2 +argcomplete~=3.5.0 tzlocal~=5.2