Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kathará v3.7.3 #274

Merged
merged 46 commits into from
Mar 18, 2024
Merged
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
c09a3d5
Add support for `gnome-terminal` as terminal emulator
tcaiazzi Jan 17, 2024
d1ae331
Replace `progressbar2` with `rich` and add bar in Docker img pull (#267)
Skazza94 Jan 29, 2024
7e91184
Merge branch 'main' into develop
Skazza94 Jan 29, 2024
db978ec
Merge branch '267-progress-bars' into develop
Skazza94 Jan 29, 2024
2aa6cc3
Handle edge cases in Docker image pull bars (#267)
Skazza94 Jan 29, 2024
51eac2f
Add Rosetta compatibility on macOS (#268)
Skazza94 Jan 29, 2024
b30c293
Merge branch '268-amd64-macos-rosetta' into develop
Skazza94 Jan 29, 2024
1c08ff5
Use `rich` logging instead of `coloredlogs` (#269)
Skazza94 Jan 30, 2024
0d7d67f
Change logging.info with rich Panels in start/clean (#269)
Skazza94 Jan 30, 2024
72f1d6f
WIP on linfo logging refactoring (#269)
Skazza94 Jan 30, 2024
3018aa3
Merge remote-tracking branch 'origin/264-gnome-terminal' into develop
Skazza94 Jan 30, 2024
0a93c98
Use rich for confirmation prompts (#269)
Skazza94 Jan 30, 2024
8df8342
Check command with rich support (#269)
Skazza94 Jan 30, 2024
c5aa13c
Replaced all logging with rich in commands (#269)
Skazza94 Jan 31, 2024
5b8f42b
list/linfo with Rich + Remove terminaltables and Curses (#269)
Skazza94 Jan 31, 2024
373403a
Meta highlighter + Add loaders in linfo/list + Main color changes (#269)
Skazza94 Jan 31, 2024
17bb3a2
FIx logging on Linux
tcaiazzi Feb 1, 2024
184cadd
Fix requirements.txt
tcaiazzi Feb 1, 2024
6be9916
Fix kwargs typing (#271)
tcaiazzi Feb 1, 2024
9b86358
Fix `copy_files` signature (#271)
tcaiazzi Feb 1, 2024
4fe56b7
Move `lab.check_integrity()`inside `deploy_lab` of managers (#271)
tcaiazzi Feb 5, 2024
afa4144
Add device integrity check in `deploy_machine` of managers (#271)
tcaiazzi Feb 5, 2024
7c33cec
Add `connect_tty_obj` to managers (#271)
tcaiazzi Feb 5, 2024
2d80773
Fix `connect_tty_obj` (#271)
tcaiazzi Feb 5, 2024
4bdf567
Add `exec_obj`to managers (#271)
tcaiazzi Feb 5, 2024
7f5ca89
Add method `get_machine_stats_obj` to managers (#271)
tcaiazzi Feb 6, 2024
4ea2711
Add method `get_link_stats_obj' to managers (#271)
tcaiazzi Feb 6, 2024
2eb43d3
Add `stream`parameter to `exec` and `exec_obj` of managers (#271)
tcaiazzi Feb 7, 2024
c3d3238
Fix pydoc (#271)
tcaiazzi Feb 7, 2024
664e965
Fix unit tests (#271)
tcaiazzi Feb 7, 2024
578b153
Add regex support to FilesystemMixin methods (#271)
tcaiazzi Feb 22, 2024
cf68b7e
Fix pydoc
tcaiazzi Feb 22, 2024
44e25d1
Add unit tests (#271)
tcaiazzi Feb 28, 2024
c212305
Add unit tests (#271)
tcaiazzi Feb 28, 2024
5a7fd84
Separate lab general options from global machine metadata (#271)
tcaiazzi Feb 28, 2024
3d35256
Add privileged checks inside managers (#271)
tcaiazzi Feb 29, 2024
48710b5
Fix pydoc
tcaiazzi Feb 29, 2024
ff6e62d
Fix `get_machine_stats` privileges in DockerManager (#271)
tcaiazzi Feb 29, 2024
fcc064f
Fix tests and minors (#271)
Skazza94 Mar 4, 2024
d560531
Fix Docker exec stream=False return (#271)
Skazza94 Mar 4, 2024
037e216
Fix K8s exec w/ and w/o stream (#271)
Skazza94 Mar 4, 2024
03d7abf
Merge branch '271-api-improvements' into develop
Skazza94 Mar 4, 2024
ddf2dfb
Bump version number and changelog
Skazza94 Mar 4, 2024
ee1d657
Fix comment
Skazza94 Mar 6, 2024
ded4e91
Fix `test_deploy_machines_privilege_error`
tcaiazzi Mar 7, 2024
7ac01a4
Fix pydoc
tcaiazzi Mar 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix K8s exec w/ and w/o stream (#271)
Skazza94 committed Mar 4, 2024
commit 037e216b03b7d4e042e3bb4c145f0617bea8c167
18 changes: 8 additions & 10 deletions src/Kathara/manager/docker/DockerMachine.py
Original file line number Diff line number Diff line change
@@ -133,7 +133,6 @@ def deploy_machines(self, lab: Lab, selected_machines: Set[str] = None) -> None:

shared_mount = lab.general_options['shared_mount'] if 'shared_mount' in lab.general_options \
else Setting.get_instance().shared_mount

if shared_mount:
if Setting.get_instance().remote_url is not None:
logging.warning("Shared folder cannot be mounted with a remote Docker connection.")
@@ -258,18 +257,17 @@ def create(self, machine: Machine) -> None:
volumes = {}

lab_options = machine.lab.general_options

if lab_options['shared_mount'] and machine.lab.shared_path:
shared_mount = ['shared_mount'] if 'shared_mount' in lab_options else Setting.get_instance().shared_mount
if shared_mount and machine.lab.shared_path:
volumes[machine.lab.shared_path] = {'bind': '/shared', 'mode': 'rw'}

# Mount the host home only if specified in settings.

if lab_options['hosthome_mount'] and Setting.get_instance().remote_url is None:
hosthome_mount = lab_options['hosthome_mount'] if 'hosthome_mount' in lab_options else \
Setting.get_instance().hosthome_mount
if hosthome_mount and Setting.get_instance().remote_url is None:
volumes[utils.get_current_user_home()] = {'bind': '/hosthome', 'mode': 'rw'}

privileged = lab_options['privileged_machines'] if 'privileged_machines' in lab_options \
else False

privileged = lab_options['privileged_machines']
if Setting.get_instance().remote_url is not None and privileged:
privileged = False
logging.warning("Privileged flag is ignored with a remote Docker connection.")
@@ -678,7 +676,7 @@ def exec(self, lab_hash: str, machine_name: str, command: Union[str, List], user
if should_wait:
self._wait_startup_execution(container, n_retries=n_retries, retry_interval=retry_interval)

command = shlex.split(command) if type(command) == str else command
command = shlex.split(command) if type(command) is str else command
exec_result = self._exec_run(container,
cmd=command,
stdout=True,
@@ -756,7 +754,7 @@ def _exec_run(self, container: docker.models.containers.Container,
(stdout_out, _) = exec_output if demux else (exec_output, None)
exec_stdout = ""
if stdout_out:
if type(stdout_out) == bytes:
if type(stdout_out) is bytes:
char_encoding = chardet.detect(stdout_out)
exec_stdout = stdout_out.decode(char_encoding['encoding'])
else:
79 changes: 61 additions & 18 deletions src/Kathara/manager/kubernetes/KubernetesMachine.py
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
import threading
import uuid
from multiprocessing.dummy import Pool
from typing import Optional, Set, List, Union, Generator, Tuple, Dict
from typing import Optional, Set, List, Union, Generator, Tuple, Dict, Any

import chardet
from kubernetes import client
@@ -624,7 +624,7 @@ def connect(self, lab_hash: str, machine_name: str, shell: Union[str, List[str]]
shell_env_value = self.get_env_var_value_from_pod(deployment, "_MEGALOS_SHELL")
shell = shlex.split(shell_env_value if shell_env_value else Setting.get_instance().device_shell)
else:
shell = shlex.split(shell) if type(shell) == str else shell
shell = shlex.split(shell) if type(shell) is str else shell

logging.debug("Connect to device `%s` with shell: %s" % (machine_name, shell))

@@ -698,7 +698,7 @@ def exec(self, lab_hash: str, machine_name: str, command: Union[str, List], tty:
Args:
lab_hash (str): The hash of the network scenario containing the device.
machine_name (str): The name of the device.
command (str): The command to execute.
command (Union[str, List]): The command to execute.
tty (bool): If True, open a new tty.
stdin (bool): If True, open the stdin channel.
stdin_buffer (List[Union[str, bytes]]): List of command to pass to the stdin.
@@ -714,7 +714,7 @@ def exec(self, lab_hash: str, machine_name: str, command: Union[str, List], tty:
MachineNotRunningError: If the specified device is not running.
MachineBinaryError: If the command specified is not found on the device.
"""
command = shlex.split(command) if command is str else command
command = shlex.split(command) if type(command) is str else command

logging.debug("Executing command `%s` to device with name: %s" % (command, machine_name))

@@ -735,44 +735,87 @@ def exec(self, lab_hash: str, machine_name: str, command: Union[str, List], tty:
tty=tty,
_preload_content=False
)
except ApiException:
return None
except ApiException as e:
raise e

if stdin_buffer is None:
stdin_buffer = []

result = {
'stdout': '',
'stderr': ''
}
if is_stream:
return self._exec_stream(response, stdin, stdin_buffer, stderr)
else:
return self._exec_all(response, machine_name, command, stdin, stdin_buffer, stderr)

@staticmethod
def _exec_stream(response: Any, stdin: bool = False, stdin_buffer: List[Union[str, bytes]] = None,
has_stderr: bool = False) -> Generator[Tuple[bytes, bytes], None, None]:
"""Execute the command on the Kubernetes Pod, returning a generator.

Args:
response (Any): The stream response from Kubernetes API.
stdin (bool): If True, open the stdin channel.
stdin_buffer (List[Union[str, bytes]]): List of command to pass to the stdin.
has_stderr (bool): If True, return the stderr.

Returns:
Tuple[bytes, bytes, int]: A tuple containing the stdout, the stderr and the return code of the command.
"""
while response.is_open():
stdout = None
stderr = None
if response.peek_stdout():
stdout = response.read_stdout()
if not is_stream:
result['stdout'] += stdout
if stderr and response.peek_stderr():
if has_stderr and response.peek_stderr():
stderr = response.read_stderr()
if not is_stream:
result['stderr'] += stderr
if stdin and stdin_buffer:
param = stdin_buffer.pop(0)
response.write_stdin(param)
if len(stdin_buffer) <= 0:
break

if is_stream and (stdout or stderr):
yield stdout.encode('utf-8') if stdout else None, stderr.encode('utf-8') if stderr else None
yield stdout.encode('utf-8') if stdout else None, stderr.encode('utf-8') if stderr else None

response.close()

@staticmethod
def _exec_all(response: Any, machine_name: str, command: List, stdin: bool = False,
stdin_buffer: List[Union[str, bytes]] = None, has_stderr: bool = False) -> Tuple[bytes, bytes, int]:
"""Execute the command on the Kubernetes Pod, returning the full output.

Args:
response (Any): The stream response from Kubernetes API.
machine_name (str): The name of the device.
command (List): The command to execute.
stdin (bool): If True, open the stdin channel.
stdin_buffer (List[Union[str, bytes]]): List of command to pass to the stdin.
has_stderr (bool): If True, return the stderr.

Returns:
Generator[Tuple[bytes, bytes]]: A generator of tuples containing the stdout and stderr in bytes.

Raises:
MachineBinaryError: If the command specified is not found on the device.
"""
result = {'stdout': '', 'stderr': ''}

while response.is_open():
if response.peek_stdout():
result['stdout'] += response.read_stdout()
if has_stderr and response.peek_stderr():
result['stderr'] += response.read_stderr()
if stdin and stdin_buffer:
param = stdin_buffer.pop(0)
response.write_stdin(param)
if len(stdin_buffer) <= 0:
break

response.close()

try:
error_code = response.returncode
except ValueError as e:
if OCI_RUNTIME_RE.search(str(e)):
raise MachineBinaryError(command, machine_name)
raise MachineBinaryError(shlex.join(command), machine_name)
error_code = 1

return result['stdout'].encode('utf-8'), result['stderr'].encode('utf-8'), error_code
3 changes: 0 additions & 3 deletions src/Kathara/model/Lab.py
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@
from .. import utils
from ..exceptions import LinkNotFoundError, MachineNotFoundError, MachineAlreadyExistsError, LinkAlreadyExistsError
from ..foundation.model.FilesystemMixin import FilesystemMixin
from ..setting.Setting import Setting

LAB_METADATA: List[str] = ["LAB_NAME", "LAB_DESCRIPTION", "LAB_VERSION", "LAB_AUTHOR", "LAB_EMAIL", "LAB_WEB"]

@@ -67,8 +66,6 @@ def __init__(self, name: Optional[str], path: Optional[str] = None) -> None:

self.general_options: Dict[str, Any] = {
'privileged_machines': False,
'shared_mount': Setting.get_instance().shared_mount,
'hosthome_mount': Setting.get_instance().hosthome_mount
}

self.global_machine_metadata: Dict[str, Any] = {}
2 changes: 1 addition & 1 deletion src/Kathara/model/Machine.py
Original file line number Diff line number Diff line change
@@ -485,7 +485,7 @@ def is_ipv6_enabled(self) -> bool:
elif "ipv6" in self.meta:
is_v6_enabled = self.meta["ipv6"]

return is_v6_enabled if type(is_v6_enabled) == bool else strtobool(is_v6_enabled)
return is_v6_enabled if type(is_v6_enabled) is bool else strtobool(is_v6_enabled)
except ValueError:
raise MachineOptionError("IPv6 value not valid on `%s`." % self.name)

7 changes: 2 additions & 5 deletions tests/model/lab_test.py
Original file line number Diff line number Diff line change
@@ -59,8 +59,7 @@ def test_default_scenario_creation(default_scenario: Lab):
assert default_scenario.web is None
assert default_scenario.machines == {}
assert default_scenario.links == {}
assert default_scenario.general_options == {'privileged_machines': False, 'shared_mount': True,
'hosthome_mount': False}
assert default_scenario.general_options == {'privileged_machines': False}
assert not default_scenario.has_dependencies
assert default_scenario.fs_type() == "memory"
assert default_scenario.shared_path is None
@@ -81,9 +80,7 @@ def test_directory_scenario_creation_with_shared_files(directory_scenario: Lab,
assert directory_scenario.web is None
assert directory_scenario.machines == {}
assert directory_scenario.links == {}
assert directory_scenario.general_options == {'privileged_machines': False,
'shared_mount': True,
'hosthome_mount': False}
assert directory_scenario.general_options == {'privileged_machines': False}
assert not directory_scenario.has_dependencies
assert os.path.normpath(directory_scenario.fs_path()) == os.path.normpath(temporary_path)
assert directory_scenario.shared_path is None