Skip to content

Commit

Permalink
Merge pull request #175 from ThePorgs/dev
Browse files Browse the repository at this point in the history
Exegol 4.2.5
  • Loading branch information
Dramelac authored Aug 10, 2023
2 parents 7b3d3e0 + 76aba48 commit 5428a7a
Show file tree
Hide file tree
Showing 15 changed files with 69 additions and 18 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
<a target="_blank" rel="noopener noreferrer" href="https://www.blackhat.com/asia-23/arsenal/schedule/#exegol-professional-hacking-setup-30815" title="Schedule">
<img alt="Black Hat Asia 2023" src="https://img.shields.io/badge/Black%20Hat%20Arsenal-Asia%202023-blueviolet">
</a>
<a target="_blank" rel="noopener noreferrer" href="https://www.blackhat.com/us-23/arsenal/schedule/#exegol-professional-hacking-setup-31711" title="Schedule">
<img alt="Black Hat USA 2023" src="https://img.shields.io/badge/Black%20Hat%20Arsenal-USA%202023-blueviolet">
</a>
<br><br>
<a target="_blank" rel="noopener noreferrer" href="https://discord.gg/cXThyp7D6P" title="Join us on Discord"><img src="https://raw.githubusercontent.com/ThePorgs/Exegol-docs/main/.assets/discord_join_us.png" width="150" alt="Join us on Discord"></a>
<br><br>
Expand Down
2 changes: 1 addition & 1 deletion exegol-docker-build
2 changes: 1 addition & 1 deletion exegol/config/ConstantConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class ConstantConfig:
"""Constant parameters information"""
# Exegol Version
version: str = "4.2.4"
version: str = "4.2.5"

# Exegol documentation link
documentation: str = "https://exegol.rtfd.io/"
Expand Down
2 changes: 1 addition & 1 deletion exegol/config/DataCache.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
class DataCache(DataFileUtils, metaclass=MetaSingleton):
"""This class allows loading cached information defined configurations
Exemple of data:
Example of data:
{
wrapper: {
update: {
Expand Down
2 changes: 1 addition & 1 deletion exegol/console/TUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,7 @@ def printContainerRecap(cls, container: ExegolContainerTemplate):
container_info_header += f" - v.{container.image.getImageVersion()}"
if "Unknown" not in container.image.getStatus():
container_info_header += f" ({container.image.getStatus(include_version=False)})"
if container.image.getArch() != EnvInfo.arch or logger.isEnabledFor(ExeLog.VERBOSE):
if container.image.getArch().split('/')[0] != EnvInfo.arch or logger.isEnabledFor(ExeLog.VERBOSE):
color = ConsoleFormat.getArchColor(container.image.getArch())
container_info_header += f" [{color}]({container.image.getArch()})[/{color}]"
recap.add_column(container_info_header)
Expand Down
7 changes: 5 additions & 2 deletions exegol/manager/UpdateManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def __updateGit(gitUtils: GitUtils) -> bool:
if current_branch is None:
logger.warning("HEAD is detached. Please checkout to an existing branch.")
current_branch = "unknown"
if logger.isEnabledFor(ExeLog.VERBOSE) or current_branch not in ["master", "main"]:
if logger.isEnabledFor(ExeLog.VERBOSE):
available_branches = gitUtils.listBranch()
# Ask to checkout only if there is more than one branch available
if len(available_branches) > 1:
Expand Down Expand Up @@ -289,7 +289,10 @@ def isUpdateTag(cls) -> bool:

@classmethod
def display_latest_version(cls) -> str:
return f"[blue]v{DataCache().get_wrapper_data().last_version}[/blue]"
last_version = DataCache().get_wrapper_data().last_version
if len(last_version) == 8 and '.' not in last_version:
return f"[bright_black]\[{last_version}][/bright_black]"
return f"[blue]v{last_version}[/blue]"

@classmethod
def __untagUpdateAvailable(cls, current_version: Optional[str] = None):
Expand Down
13 changes: 11 additions & 2 deletions exegol/model/ContainerConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -588,10 +588,19 @@ def prepareShare(self, share_name: str):
# Skip default volume workspace if disabled
return
else:
# Add shared-data-volumes private workspace bind volume
# Add dedicated private workspace bind volume
volume_path = str(UserConfig().private_volume_path.joinpath(share_name))
self.addVolume(volume_path, '/workspace', enable_sticky_group=True)

def rollback_preparation(self, share_name: str):
"""Undo preparation in case of container creation failure"""
if self.__workspace_custom_path is None and not self.__disable_workspace:
# Remove dedicated workspace volume
logger.info("Rollback: removing dedicated workspace directory")
directory_path = UserConfig().private_volume_path.joinpath(share_name)
if directory_path.is_dir():
directory_path.rmdir()

def setNetworkMode(self, host_mode: Optional[bool]):
"""Set container's network mode, true for host, false for bridge"""
if host_mode is None:
Expand Down Expand Up @@ -852,7 +861,7 @@ def removeVolume(self, host_path: Optional[str] = None, container_path: Optional
"""Remove a volume from the container configuration (Only before container creation)"""
if host_path is None and container_path is None:
# This is a dev problem
raise ReferenceError('At least one parameter must be set')
raise ValueError('At least one parameter must be set')
for i in range(len(self.__mounts)):
# For each Mount object compare the host_path if supplied or the container_path si supplied
if host_path is not None and self.__mounts[i].get("Source") == host_path:
Expand Down
3 changes: 3 additions & 0 deletions exegol/model/ExegolContainer.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ def __removeVolume(self):
list_files = []
else:
return
except FileNotFoundError:
logger.debug("This workspace has already been removed.")
return
try:
if len(list_files) > 0:
# Directory is not empty
Expand Down
8 changes: 8 additions & 0 deletions exegol/model/ExegolContainerTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from rich.prompt import Prompt

from exegol.config.EnvInfo import EnvInfo
from exegol.model.ContainerConfig import ContainerConfig
from exegol.model.ExegolImage import ExegolImage

Expand All @@ -14,6 +15,9 @@ def __init__(self, name: Optional[str], config: ContainerConfig, image: ExegolIm
if name is None:
name = Prompt.ask("[bold blue][?][/bold blue] Enter the name of your new exegol container", default="default")
assert name is not None
if (EnvInfo.isWindowsHost() or EnvInfo.isMacHost()) and not name.startswith("exegol-"):
# Force container as lowercase because the filesystem of windows / mac are case-insensitive => https://github.com/ThePorgs/Exegol/issues/167
name = name.lower()
self.container_name: str = name if name.startswith("exegol-") else f'exegol-{name}'
self.name: str = name.replace('exegol-', '')
if hostname:
Expand All @@ -31,6 +35,10 @@ def prepare(self):
"""Prepare the model before creating the docker container"""
self.config.prepareShare(self.name)

def rollback(self):
"""Rollback change in case of container creation fail."""
self.config.rollback_preparation(self.name)

def getDisplayName(self) -> str:
"""Getter of the container's name for TUI purpose"""
if self.container_name != self.hostname:
Expand Down
2 changes: 1 addition & 1 deletion exegol/model/ExegolImage.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ def getName(self) -> str:
def getDisplayName(self) -> str:
"""Image's display name getter"""
result = self.__alt_name if self.__alt_name else self.__name
if self.getArch() != ParametersManager().arch or logger.isEnabledFor(ExeLog.VERBOSE):
if self.getArch().split('/')[0] != ParametersManager().arch or logger.isEnabledFor(ExeLog.VERBOSE):
color = ConsoleFormat.getArchColor(self.getArch())
result += f" [{color}]({self.getArch()})[/{color}]"
return result
Expand Down
10 changes: 6 additions & 4 deletions exegol/utils/DataFileUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,18 @@ def _build_file_content(self) -> str:
This fonction build the default file content. Called when the file doesn't exist yet or have been upgrade and need to be updated.
:return:
"""
raise NotImplementedError(
f"The '_build_default_file' method hasn't been implemented in the '{self.__class__}' class.")
raise NotImplementedError(f"The '_build_default_file' method hasn't been implemented in the '{self.__class__}' class.")

def _create_config_file(self):
"""
Create or overwrite the file content to the default / current value depending on the '_build_default_file' that must be redefined in child class.
:return:
"""
with open(self._file_path, 'w') as file:
file.write(self._build_file_content())
try:
with open(self._file_path, 'w') as file:
file.write(self._build_file_content())
except PermissionError as e:
logger.critical(f"Unable to open the file '{self._file_path}' ({e}). Please fix your file permissions or run exegol with the correct rights.")

def _parse_config(self):
data: Dict = {}
Expand Down
21 changes: 20 additions & 1 deletion exegol/utils/DockerUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,20 @@ def createContainer(cls, model: ExegolContainerTemplate, temporary: bool = False
auto_remove=temporary,
working_dir=model.config.getWorkingDir())
except APIError as err:
logger.error(err.explanation.decode('utf-8') if type(err.explanation) is bytes else err.explanation)
message = err.explanation.decode('utf-8').replace('[', '\\[') if type(err.explanation) is bytes else err.explanation
message = message.replace('[', '\\[')
logger.error(message)
logger.debug(err)
model.rollback()
try:
container = cls.__client.containers.list(all=True, filters={"name": model.container_name})
if container is not None and len(container) > 0:
for c in container:
if c.name == model.container_name: # Search for exact match
container[0].remove()
logger.debug("Container removed")
except Exception:
pass
logger.critical("Error while creating exegol container. Exiting.")
# Not reachable, critical logging will exit
return # type: ignore
Expand All @@ -151,6 +163,13 @@ def getContainer(cls, tag: str) -> ExegolContainer:
return # type: ignore
# Check if there is at least 1 result. If no container was found, raise ObjectNotFound.
if container is None or len(container) == 0:
# Handle case-insensitive OS
if EnvInfo.isWindowsHost() or EnvInfo.isMacHost():
# First try to fetch the container as-is (for retroactive support with old container with uppercase characters)
# If the user's input didn't match any container, try to force the name in lowercase if not already tried
lowered_tag = tag.lower()
if lowered_tag != tag:
return cls.getContainer(lowered_tag)
raise ObjectNotFound
# Filter results with exact name matching
for c in container:
Expand Down
4 changes: 4 additions & 0 deletions exegol/utils/GuiUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import shutil
import subprocess
import sys
import time
from pathlib import Path
from typing import Optional
Expand Down Expand Up @@ -92,6 +93,9 @@ def __macGuiChecks(cls) -> bool:
# Notify user to change configuration
logger.error("XQuartz does not allow network connections. "
"You need to manually change the configuration to 'Allow connections from network clients'")
# Add sys.platform check to exclude windows env (fix for mypy static code analysis)
if sys.platform != "win32" and os.getuid() == 0:
logger.warning("You are running exegol as [red]root[/red]! The root user cannot check in the user context whether XQuartz is properly configured or not.")
return False

# Check if XQuartz is started, check is dir exist and if there is at least one socket
Expand Down
6 changes: 3 additions & 3 deletions exegol/utils/WebUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def getRemoteVersion(cls, tag: str) -> Optional[str]:
response = cls.__runRequest(url, service_name="Docker Registry", headers=manifest_headers, method="GET")
version: Optional[str] = None
if response is not None and response.status_code == 200:
data = json.loads(response.text)
data = json.loads(response.content.decode("utf-8"))
# Parse metadata of the current image from v1 schema
metadata = json.loads(data.get("history", [])[0]['v1Compatibility'])
# Find version label and extract data
Expand All @@ -106,7 +106,7 @@ def runJsonRequest(cls, url: str, service_name: str, headers: Optional[Dict] = N
return None
data = cls.__runRequest(url, service_name, headers, method, data, retry_count)
if data is not None and data.status_code == 200:
data = json.loads(data.text)
data = json.loads(data.content.decode("utf-8"))
elif data is not None:
logger.error(f"Error during web request to {service_name} ({data.status_code}) on {url}")
if data.status_code == 404 and service_name == "Dockerhub":
Expand All @@ -127,7 +127,7 @@ def __runRequest(cls, url: str, service_name: str, headers: Optional[Dict] = Non
response = requests.request(method=method, url=url, timeout=(5, 10), verify=ParametersManager().verify, headers=headers, data=data)
return response
except requests.exceptions.HTTPError as e:
logger.error(f"Response error: {e.response.text}")
logger.error(f"Response error: {e.response.content.decode('utf-8')}")
except requests.exceptions.ConnectionError as err:
logger.debug(f"Error: {err}")
error_re = re.search(r"\[Errno [-\d]+]\s?([^']*)('\))+\)*", str(err))
Expand Down
2 changes: 1 addition & 1 deletion tests/test_exegol.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@


def test_version():
assert __version__ == '4.2.4'
assert __version__ == '4.2.5'

0 comments on commit 5428a7a

Please sign in to comment.