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

feat(core): add support for ssh connections into sessions #3318

Merged
merged 22 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4af6981
feat(core): add ssh_supported flag to template and migrations_check (…
Panaetius Feb 14, 2023
daeb51f
feat(cli): add SSH support for `renku session` (#3313)
Panaetius Feb 14, 2023
6ccc8bd
feat(cli): Show ssh support in project show and check SSH support on …
Panaetius Feb 16, 2023
687fae6
Merge branch 'develop' into feature/ssh-into-sessions
Panaetius Feb 16, 2023
f5e04a0
Merge branch 'develop' into feature/ssh-into-sessions
Panaetius Feb 16, 2023
e2e0988
fix login if backup already exists but user switches remote
Panaetius Feb 17, 2023
ecb3908
fix ssh_supported and remote URL in cli
Panaetius Feb 17, 2023
1ad69ef
change directory on SSH connection and keep connection open
Feb 20, 2023
5731608
make session open also work when called with SSH connection name
Feb 20, 2023
86c4507
change session launch message for SSH sessions
Feb 20, 2023
32edf53
add ssh-setup feedback
Feb 20, 2023
9516242
fix tests
Feb 20, 2023
e6bc1f2
change wording for image build
Feb 20, 2023
c586ae5
support for sessions not started from the CLI
Panaetius Feb 21, 2023
32e6078
use session name as host name
Panaetius Feb 21, 2023
18d33e8
Merge branch 'develop' into feature/ssh-into-sessions
Panaetius Feb 21, 2023
4f55723
fix cleanup on session open
Panaetius Feb 22, 2023
6d7b64a
Merge branch 'feature/ssh-into-sessions' of github.com:SwissDataScien…
Panaetius Feb 22, 2023
6883d74
add additional tests
Panaetius Feb 22, 2023
be1722c
address comments and fix tests
Feb 24, 2023
20bb664
change key to ed25519
Feb 24, 2023
ef90cf7
fix docstrings
Feb 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions renku/command/format/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def tabular(sessions, *, columns=None):
if not columns:
columns = "id,status,url"

if any(s.ssh_enabled for s in sessions):
columns += ",ssh"

return tabulate(collection=sessions, columns=columns, columns_mapping=SESSION_COLUMNS)


Expand All @@ -35,4 +38,5 @@ def tabular(sessions, *, columns=None):
"id": ("id", "id"),
"status": ("status", "status"),
"url": ("url", "url"),
"ssh": ("ssh_enabled", "SSH enabled"),
}
6 changes: 6 additions & 0 deletions renku/command/migrate.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,18 +88,23 @@ def _template_migration_check():
Returns:
Dictionary of template migration status.
"""
from renku.core.config import get_value
from renku.core.template.usecase import check_for_template_update

try:
project = project_context.project
template_source = project.template_metadata.template_source
template_ref = project.template_metadata.template_ref
template_id = project.template_metadata.template_id
ssh_supported = project.template_metadata.ssh_supported
except (ValueError, AttributeError):
project = None
template_source = None
template_ref = None
template_id = None
ssh_supported = False

ssh_supported = get_value("renku", "ssh_supported") == "true" or ssh_supported

update_available, update_allowed, current_version, new_version = check_for_template_update(project)

Expand All @@ -111,6 +116,7 @@ def _template_migration_check():
"template_source": template_source,
"template_ref": template_ref,
"template_id": template_id,
"ssh_supported": ssh_supported,
}


Expand Down
9 changes: 7 additions & 2 deletions renku/command/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@


from renku.command.command_builder.command import Command
from renku.core.session.session import session_list, session_open, session_start, session_stop
from renku.core.session.session import session_list, session_open, session_start, session_stop, ssh_setup


def session_list_command():
Expand All @@ -29,7 +29,7 @@ def session_list_command():

def session_start_command():
"""Start an interactive session."""
return Command().command(session_start)
return Command().command(session_start).with_database()


def session_stop_command():
Expand All @@ -40,3 +40,8 @@ def session_stop_command():
def session_open_command():
"""Open a running interactive session."""
return Command().command(session_open)


def ssh_setup_command():
"""Setup SSH keys for SSH connections to sessions."""
return Command().command(ssh_setup)
4 changes: 4 additions & 0 deletions renku/command/view_model/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from datetime import datetime
from typing import List, Optional

from renku.core.config import get_value
from renku.domain_model.project import Project
from renku.domain_model.provenance.agent import Person

Expand All @@ -39,6 +40,7 @@ def __init__(
annotations: Optional[str],
template_info: str,
keywords: Optional[List[str]],
ssh_supported: bool = False,
):
self.id = id
self.name = name
Expand All @@ -52,6 +54,7 @@ def __init__(
self.template_info = template_info
self.keywords = keywords
self.keywords_str = ", ".join(keywords) if keywords else ""
self.ssh_supported = ssh_supported

@classmethod
def from_project(cls, project: Project):
Expand Down Expand Up @@ -88,4 +91,5 @@ def from_project(cls, project: Project):
else None,
template_info=template_info,
keywords=project.keywords,
ssh_supported=get_value("renku", "ssh_supported") == "true" or project.template_metadata.ssh_supported,
)
13 changes: 13 additions & 0 deletions renku/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ class AuthenticationError(RenkuException):
"""Raise when there is a problem with authentication."""


class KeyNotFoundError(RenkuException):
"""Raise when an SSH private or public key couldn't be found."""


class DirtyRepository(RenkuException):
"""Raise when trying to work with dirty repository."""

Expand Down Expand Up @@ -526,6 +530,15 @@ def __init__(self):
super(NodeNotFoundError, self).__init__(msg)


class SSHNotFoundError(RenkuException):
"""Raised when SSH client is not installed on the system."""

def __init__(self):
"""Build a custom message."""
msg = "SSH client (ssh) could not be found on this system"
super(SSHNotFoundError, self).__init__(msg)


class ObjectNotFoundError(RenkuException):
"""Raised when an object is not found in the storage."""

Expand Down
2 changes: 2 additions & 0 deletions renku/core/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ def create_from_template_local(
description: Optional[str] = None,
keywords: Optional[List[str]] = None,
data_dir: Optional[str] = None,
ssh_supported: bool = False,
):
"""Initialize a new project from a template.

Expand Down Expand Up @@ -381,6 +382,7 @@ def create_from_template_local(
description="",
parameters={},
icon="",
ssh_supported=ssh_supported,
immutable_files=immutable_template_files or [],
allow_update=automated_template_update,
source=metadata["__template_source__"],
Expand Down
7 changes: 4 additions & 3 deletions renku/core/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ def login(endpoint: Optional[str], git_login: bool, yes: bool):
remote_name, remote_url = remote.name, remote.url

if remote_name and remote_url:
if not yes and not get_value("renku", "show_login_warning"):
show_login_warning = get_value("renku", "show_login_warning")
if not yes and (show_login_warning is None or show_login_warning.lower() == "true"):
message = (
"Remote URL will be changed. Do you want to continue "
"(to disable this warning, pass '--yes' or run 'renku config set show_login_warning False')?"
Expand Down Expand Up @@ -140,7 +141,8 @@ def login(endpoint: Optional[str], git_login: bool, yes: bool):
)
if backup_exists:
communication.echo(f"Backup remote '{backup_remote_name}' already exists.")
elif not remote:

if not remote and not backup_exists:
communication.error(f"Cannot create backup remote '{backup_remote_name}' for '{remote_url}'")
else:
_set_renku_url_for_remote(
Expand Down Expand Up @@ -182,7 +184,6 @@ def _set_renku_url_for_remote(repository: "Repository", remote_name: str, remote
remote_name(str): Name of the remote.
remote_url(str): Url of the remote.
hostname(str): Hostname.

Raises:
errors.GitCommandError: If remote doesn't exist.
"""
Expand Down
35 changes: 32 additions & 3 deletions renku/core/session/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
# limitations under the License.
"""Docker based interactive session provider."""

import webbrowser
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional, Tuple, cast
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional, Tuple, cast
from uuid import uuid4

import docker
Expand All @@ -32,6 +33,9 @@
from renku.domain_model.project_context import project_context
from renku.domain_model.session import ISessionProvider, Session

if TYPE_CHECKING:
from renku.core.dataset.providers.models import ProviderParameter


class DockerSessionProvider(ISessionProvider):
"""A docker based interactive session provider."""
Expand Down Expand Up @@ -71,7 +75,8 @@ def _get_jupyter_urls(ports: Dict[str, Any], auth_token: str, jupyter_port: int
def _get_docker_containers(self, project_name: str) -> List[docker.models.containers.Container]:
return self.docker_client().containers.list(filters={"label": f"renku_project={project_name}"})

def get_name(self) -> str:
@property
def name(self) -> str:
"""Return session provider's name."""
return "docker"

Expand Down Expand Up @@ -109,10 +114,18 @@ def session_provider(self) -> ISessionProvider:
"""Supported session provider.

Returns:
a reference to ``self``.
A reference to ``self``.
"""
return self

def get_start_parameters(self) -> List["ProviderParameter"]:
"""Returns parameters that can be set for session start."""
return []

def get_open_parameters(self) -> List["ProviderParameter"]:
"""Returns parameters that can be set for session open."""
return []

def session_list(self, project_name: str, config: Optional[Dict[str, Any]]) -> List[Session]:
"""Lists all the sessions currently running by the given session provider.

Expand Down Expand Up @@ -140,6 +153,7 @@ def session_start(
mem_request: Optional[str] = None,
disk_request: Optional[str] = None,
gpu_request: Optional[str] = None,
**kwargs,
) -> Tuple[str, str]:
"""Creates an interactive session.

Expand Down Expand Up @@ -259,6 +273,21 @@ def session_stop(self, project_name: str, session_name: Optional[str], stop_all:
except docker.errors.APIError as error:
raise errors.DockerError(error.msg)

def session_open(self, project_name: str, session_name: str, **kwargs) -> bool:
"""Open a given interactive session.

Args:
project_name(str): Renku project name.
session_name(str): The unique id of the interactive session.
"""
url = self.session_url(session_name)

if not url:
return False

webbrowser.open(url)
return True

def session_url(self, session_name: str) -> Optional[str]:
"""Get the URL of the interactive session."""
for c in self.docker_client().containers.list():
Expand Down