-
Notifications
You must be signed in to change notification settings - Fork 80
Remote Dev Plugin Part 4 - bug fix and feature improvement #2569
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
base: 08-17-enable_remote_ssh_connection
Are you sure you want to change the base?
Remote Dev Plugin Part 4 - bug fix and feature improvement #2569
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
f2d260d to
e7f252e
Compare
|
|
||
| folder_uri = f"vscode-remote://ssh-remote+{service_name}{remote_path}" | ||
| try: | ||
| subprocess.run([binary_name, "--folder-uri", folder_uri], check=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The subprocess.run() call uses check=False, which means IDE launch failures won't raise exceptions. This could create a misleading user experience where the CLI appears to succeed even when the IDE fails to start. Consider either:
- Using
check=Trueto automatically raise exceptions on non-zero exit codes, or - Capturing the return value and checking it manually:
result = subprocess.run([binary_name, "--folder-uri", folder_uri], check=False) if result.returncode != 0: raise CliError(f"Failed to launch {binary_name}, exit code: {result.returncode}")
This would provide clearer feedback to users when their IDE fails to launch.
| subprocess.run([binary_name, "--folder-uri", folder_uri], check=False) | |
| result = subprocess.run([binary_name, "--folder-uri", folder_uri], check=False) | |
| if result.returncode != 0: | |
| raise CliError(f"Failed to launch {binary_name}, exit code: {result.returncode}") | |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
e7f252e to
cf658b9
Compare
cf658b9 to
db13c9c
Compare
| help="Internal Snowflake stage to mount for persistent storage. " | ||
| "The stage will be mounted at two locations: " | ||
| "1) 'stage/user-default' -> '/root/user-default' (workspace files), " | ||
| "2) 'stage/.vscode-server/data' -> '/root/.vscode-server/data' (VS Code settings). " | ||
| "Example: --stage @my_stage or --stage @my_stage/project", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just curious what are the considerations for using block storage instead? Is there a reason we're not going with that currently?
| "--ssh", | ||
| help="Set up SSH configuration for connecting to the remote environment. This is a blocking command that keeps SSH connections alive.", | ||
| ), | ||
| code: bool = typer.Option( | ||
| False, | ||
| "--code", | ||
| help="Open VS Code connected to the remote service over SSH (mutually exclusive with --ssh and --cursor)", | ||
| ), | ||
| cursor: bool = typer.Option( | ||
| False, | ||
| "--cursor", | ||
| help="Open Cursor connected to the remote service over SSH (mutually exclusive with --ssh and --code)", | ||
| ), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WDYT of making these full subcommands rather than options? i.e. snow remote start cursor instead of snow remote start --cursor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or alternatively flatten it a bit: snow remote cursor. Basically the commands would be:
snow remote start - start the service in the background, display the URL needed to connect by web interface
snow remote ssh - blocking command*, opens SSH tunnel
snow remote code - blocking command, open SSH tunnel + VSCode. As a bonus would be neat if this automatically exits if the user closes VSCode
snow remote cursor - same idea
*could consider making this non-blocking and spin up a bg process in the future, tbd
| launching_ide = code or cursor | ||
| if launching_ide: | ||
| manager.validate_ide_requirements(name, eai_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: doesn't it make sense to just ssh = ssh or launching_ide since IDEs presumably will require ssh?
| cc.warning( | ||
| f"Service '{service_name}' is already running. " | ||
| "Configuration changes provided to 'snow remote start' will be ignored for existing services. " | ||
| f"To apply changes, delete the service first with 'snow remote delete {service_name}' and then start it again with the new configuration." | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
consider making this an error instead
|
|
||
| # Check if user provided configuration options that would modify the service | ||
| self._warn_about_config_changes(service_name, **config_options) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider refactoring so we're not calling this in three places
|
|
||
| folder_uri = f"vscode-remote://ssh-remote+{service_name}{remote_path}" | ||
| try: | ||
| subprocess.run([binary_name, "--folder-uri", folder_uri], check=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
db13c9c to
c9a1a5d
Compare
2610027 to
6d2eb90
Compare
|
|
||
| if not token: | ||
| cc.step("❌ Unable to get token. Retrying in 30 seconds...") | ||
| self._wait_with_shutdown_check(SSH_RETRY_INTERVAL) | ||
| self._interruptible_sleep(SSH_RETRY_INTERVAL) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's an inconsistency between error handling approaches. The _get_fresh_token() method now raises exceptions rather than returning None, but this code still checks for None values. Consider updating this exception handling to align with the new behavior:
try:
token = self._get_fresh_token()
except Exception as e:
cc.step(f"❌ Unable to get token: {e}. Retrying in 30 seconds...")
self._interruptible_sleep(SSH_RETRY_INTERVAL)
continueThis would properly catch and handle exceptions from the updated _get_fresh_token() implementation.
| if not token: | |
| cc.step("❌ Unable to get token. Retrying in 30 seconds...") | |
| self._wait_with_shutdown_check(SSH_RETRY_INTERVAL) | |
| self._interruptible_sleep(SSH_RETRY_INTERVAL) | |
| try: | |
| token = self._get_fresh_token() | |
| except Exception as e: | |
| cc.step(f"❌ Unable to get token: {e}. Retrying in 30 seconds...") | |
| self._interruptible_sleep(SSH_RETRY_INTERVAL) | |
| continue |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
c9a1a5d to
784cd7a
Compare
| response = requests.get( | ||
| f"https://{endpoint_url}", | ||
| headers=headers, | ||
| timeout=ENDPOINT_REQUEST_TIMEOUT_SECONDS, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Security Concern: URL Construction
The current URL construction in validate_endpoint_ready() may be vulnerable to protocol downgrade attacks. When prepending https:// to endpoint_url without validating its content, there's a risk of creating malformed URLs if endpoint_url already contains a protocol scheme.
For example, if endpoint_url contains http://example.com, the resulting URL would be https://http://example.com, which could lead to unexpected behavior or security issues.
Consider either:
- Validating that
endpoint_urldoesn't already contain a protocol scheme - Using a URL parsing library to properly construct the URL with HTTPS
# Example fix using validation
if "://" in endpoint_url:
raise CliError(f"Endpoint URL should not contain protocol: {endpoint_url}")
response = requests.get(f"https://{endpoint_url}", ...)
# Or using URL parsing
from urllib.parse import urlparse, urlunparse
parsed = urlparse(endpoint_url)
if parsed.scheme:
# Strip existing scheme and ensure HTTPS
parts = list(parsed)
parts[0] = "https"
url = urlunparse(parts)
else:
url = f"https://{endpoint_url}"
response = requests.get(url, ...)| response = requests.get( | |
| f"https://{endpoint_url}", | |
| headers=headers, | |
| timeout=ENDPOINT_REQUEST_TIMEOUT_SECONDS, | |
| ) | |
| if "://" in endpoint_url: | |
| raise CliError(f"Endpoint URL should not contain protocol: {endpoint_url}") | |
| response = requests.get( | |
| f"https://{endpoint_url}", | |
| headers=headers, | |
| timeout=ENDPOINT_REQUEST_TIMEOUT_SECONDS, | |
| ) |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
| # If result is None, service needs recreation - continue to creation | ||
| else: | ||
| log.debug("Service %s does not exist, creating...", service_name) | ||
| cc.step(f"Service {service_name} does not exist, creating...") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent logging level for service creation message
There's an inconsistency in how service creation is logged. When a service doesn't exist:
cc.step(f"Service {service_name} does not exist, creating...")This uses cc.step() which displays to users, while the similar check above uses:
log.debug("Service %s is pending, waiting for it to be ready...", service_name)For consistency, either:
- Use
log.debug()for both messages to keep implementation details hidden from users - Use
cc.step()for both to provide consistent user-facing progress updates
This would make the logging behavior more predictable and maintain a consistent level of verbosity in the user interface.
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
6d2eb90 to
77accb6
Compare
582d471 to
b9a97f7
Compare
77accb6 to
85af513
Compare
b9a97f7 to
01d14d8
Compare
85af513 to
583ba5c
Compare

Pre-review checklist
Changes description
This PR enhances the
snow remotecommand with several new features and improvements:IDE Integration:
--codeflag to launch VS Code connected to the remote service--cursorflag to launch Cursor connected to the remote serviceImproved Stage Mounting:
Error Handling:
ValueErrorwith more specificCliErrorfor better error messagesUser Experience:
The changes maintain backward compatibility while adding new functionality for IDE integration and persistent storage.