-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add SSH command for executions (#314)
- Loading branch information
Showing
10 changed files
with
338 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import shutil | ||
import subprocess | ||
import time | ||
|
||
from tests.commands.execution.utils import get_execution_data_mock, no_sleep | ||
from tests.fixture_data import EXECUTION_DATA, STATUS_EVENT_RESPONSE_DATA | ||
from valohai_cli.commands.execution.ssh import ssh | ||
|
||
|
||
def test_ssh_in_completed_execution(runner, logged_in_and_linked, monkeypatch): | ||
with get_execution_data_mock(): | ||
counter = EXECUTION_DATA["counter"] | ||
result = runner.invoke(ssh, [str(counter)], catch_exceptions=False) | ||
assert f"Error: Execution #{counter} is complete. Cannot SSH into it.\n" in result.output | ||
assert result.exit_code == 1 | ||
|
||
|
||
def test_ssh_in_queued_execution(runner, logged_in_and_linked, monkeypatch): | ||
counter = EXECUTION_DATA["counter"] | ||
monkeypatch.setitem(EXECUTION_DATA, "status", "queued") | ||
monkeypatch.setattr(time, "sleep", no_sleep) | ||
with get_execution_data_mock(): | ||
result = runner.invoke(ssh, [str(counter)], catch_exceptions=False) | ||
assert f"Execution #{counter} is queued. Waiting for it to start...\n" in result.output | ||
assert result.exit_code == 1 | ||
|
||
|
||
def test_ssh_with_no_ssh_details_present(runner, logged_in_and_linked, monkeypatch): | ||
counter = EXECUTION_DATA["counter"] | ||
monkeypatch.setitem(EXECUTION_DATA, "status", "started") | ||
monkeypatch.setattr(time, "sleep", lambda x: None) | ||
with get_execution_data_mock() as m: | ||
m.get( | ||
f"https://app.valohai.com/api/v0/executions/{EXECUTION_DATA['id']}/status-events/", | ||
json={"status_events": []}, | ||
) | ||
result = runner.invoke(ssh, [str(counter)], catch_exceptions=False) | ||
output = result.output | ||
assert "1/5 Retrying: No SSH details found...\n" in output | ||
assert "2/5 Retrying: No SSH details found...\n" in output | ||
assert "3/5 Retrying: No SSH details found...\n" in output | ||
assert "4/5 Retrying: No SSH details found...\n" in output | ||
assert "5/5 Retrying: No SSH details found...\n" in output | ||
|
||
assert result.exit_code == 1 | ||
|
||
|
||
def test_ssh(runner, logged_in_and_linked, monkeypatch, tmp_path): | ||
counter = EXECUTION_DATA["counter"] | ||
monkeypatch.setitem(EXECUTION_DATA, "status", "started") | ||
|
||
def mock_prompt(): | ||
return tmp_path | ||
|
||
monkeypatch.setattr( | ||
"valohai_cli.commands.execution.ssh.select_private_key_from_possible_directories", | ||
mock_prompt, | ||
) | ||
|
||
with get_execution_data_mock() as m: | ||
m.get( | ||
f"https://app.valohai.com/api/v0/executions/{EXECUTION_DATA['id']}/status-events/", | ||
json=STATUS_EVENT_RESPONSE_DATA, | ||
) | ||
result = runner.invoke(ssh, [str(counter)], catch_exceptions=False) | ||
output = result.output | ||
assert "SSH address is 127.0.0.1:2222" in output | ||
|
||
def mock_subprocess_run(*args, **kwargs): | ||
print(args[0]) | ||
return subprocess.CompletedProcess(args=args, returncode=0) | ||
|
||
monkeypatch.setattr(subprocess, "run", mock_subprocess_run) | ||
|
||
result = result.runner.invoke(ssh, [str(counter)], input="1", catch_exceptions=False) | ||
assert ( | ||
f"['{shutil.which('ssh')}', '-i', PosixPath('{tmp_path}'), '[email protected]', '-p', '2222', '-t', '/bin/bash']" | ||
in result.output | ||
) | ||
assert result.exit_code == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import click | ||
|
||
from valohai_cli.exceptions import CLIException | ||
from valohai_cli.utils.cli_utils import counter_argument | ||
from valohai_cli.utils.ssh import ( | ||
get_ssh_details_with_retry, | ||
make_ssh_connection, | ||
select_private_key_from_possible_directories, | ||
) | ||
|
||
|
||
@click.command() | ||
@counter_argument | ||
@click.option( | ||
"--private-key-file", | ||
default=None, | ||
type=click.Path(file_okay=True, exists=True), | ||
help="Private SSH key to use for the connection.", | ||
) | ||
@click.option( | ||
"--address", | ||
default=None, | ||
help='Address of the container in "ip:port" format. If not provided, ' | ||
"the address from the execution will be used.", | ||
) | ||
def ssh(counter: int, private_key_file: str, address: str) -> None: | ||
""" | ||
Make SSH Connection to the execution container. | ||
""" | ||
if address: | ||
try: | ||
ip_address, _, port_str = address.partition(":") | ||
if not ip_address or not port_str: | ||
raise CLIException("Address must be in 'ip:port' format.") | ||
port = int(port_str) | ||
if port <= 1023: | ||
raise CLIException("Port must be above 1023") | ||
except ValueError as e: | ||
raise CLIException(f"Invalid address format: {e}") | ||
else: | ||
ip_address, port = get_ssh_details_with_retry(counter) | ||
|
||
click.echo(f"SSH address is {ip_address}:{port}") | ||
if not private_key_file: | ||
private_key_file = select_private_key_from_possible_directories() | ||
make_ssh_connection(ip_address, port, private_key_file) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.