flow
This commit is contained in:
127
commands/enter.py
Normal file
127
commands/enter.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""flow enter — connect to a development instance via SSH."""
|
||||
|
||||
import getpass
|
||||
import os
|
||||
import sys
|
||||
|
||||
from flow.core.config import FlowContext
|
||||
|
||||
# Default host templates per platform
|
||||
HOST_TEMPLATES = {
|
||||
"orb": "<namespace>.orb",
|
||||
"utm": "<namespace>.utm.local",
|
||||
"core": "<namespace>.core.lan",
|
||||
}
|
||||
|
||||
|
||||
def register(subparsers):
|
||||
p = subparsers.add_parser("enter", help="Connect to a development instance via SSH")
|
||||
p.add_argument("target", help="Target: [user@]namespace@platform")
|
||||
p.add_argument("-u", "--user", help="SSH user (overrides target)")
|
||||
p.add_argument("-n", "--namespace", help="Namespace (overrides target)")
|
||||
p.add_argument("-p", "--platform", help="Platform (overrides target)")
|
||||
p.add_argument("-s", "--session", default="default", help="Tmux session name (default: 'default')")
|
||||
p.add_argument("--no-tmux", action="store_true", help="Skip tmux attachment")
|
||||
p.add_argument("-d", "--dry-run", action="store_true", help="Show command without executing")
|
||||
p.set_defaults(handler=run)
|
||||
|
||||
|
||||
def _parse_target(target: str):
|
||||
"""Parse [user@]namespace@platform into (user, namespace, platform)."""
|
||||
user = None
|
||||
namespace = None
|
||||
platform = None
|
||||
|
||||
if "@" in target:
|
||||
platform = target.rsplit("@", 1)[1]
|
||||
rest = target.rsplit("@", 1)[0]
|
||||
else:
|
||||
rest = target
|
||||
|
||||
if "@" in rest:
|
||||
user = rest.rsplit("@", 1)[0]
|
||||
namespace = rest.rsplit("@", 1)[1]
|
||||
else:
|
||||
namespace = rest
|
||||
|
||||
return user, namespace, platform
|
||||
|
||||
|
||||
def _build_destination(user: str, host: str, preserve_host_user: bool = False) -> str:
|
||||
if "@" in host:
|
||||
host_user, host_name = host.rsplit("@", 1)
|
||||
effective_user = host_user if preserve_host_user else (user or host_user)
|
||||
return f"{effective_user}@{host_name}"
|
||||
if not user:
|
||||
return host
|
||||
return f"{user}@{host}"
|
||||
|
||||
|
||||
def run(ctx: FlowContext, args):
|
||||
# Warn if already inside an instance
|
||||
if os.environ.get("DF_NAMESPACE") and os.environ.get("DF_PLATFORM"):
|
||||
ns = os.environ["DF_NAMESPACE"]
|
||||
plat = os.environ["DF_PLATFORM"]
|
||||
ctx.console.error(
|
||||
f"Not recommended inside an instance. Currently in: {ns}@{plat}"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
user, namespace, platform = _parse_target(args.target)
|
||||
|
||||
# Apply overrides
|
||||
if args.user:
|
||||
user = args.user
|
||||
if args.namespace:
|
||||
namespace = args.namespace
|
||||
if args.platform:
|
||||
platform = args.platform
|
||||
|
||||
user_was_explicit = bool(user)
|
||||
|
||||
if not user:
|
||||
user = os.environ.get("USER") or getpass.getuser()
|
||||
if not namespace:
|
||||
ctx.console.error("Namespace is required in target")
|
||||
sys.exit(1)
|
||||
if not platform:
|
||||
ctx.console.error("Platform is required in target")
|
||||
sys.exit(1)
|
||||
|
||||
# Resolve SSH host from template or config
|
||||
host_template = HOST_TEMPLATES.get(platform)
|
||||
ssh_identity = None
|
||||
|
||||
# Check config targets for override
|
||||
for tc in ctx.config.targets:
|
||||
if tc.namespace == namespace and tc.platform == platform:
|
||||
host_template = tc.ssh_host
|
||||
ssh_identity = tc.ssh_identity
|
||||
break
|
||||
|
||||
if not host_template:
|
||||
ctx.console.error(f"Unknown platform: {platform}")
|
||||
sys.exit(1)
|
||||
|
||||
ssh_host = host_template.replace("<namespace>", namespace)
|
||||
destination = _build_destination(user, ssh_host, preserve_host_user=not user_was_explicit)
|
||||
|
||||
# Build SSH command
|
||||
ssh_cmd = ["ssh", "-tt"]
|
||||
if ssh_identity:
|
||||
ssh_cmd.extend(["-i", os.path.expanduser(ssh_identity)])
|
||||
ssh_cmd.append(destination)
|
||||
|
||||
if not args.no_tmux:
|
||||
ssh_cmd.extend([
|
||||
"tmux", "new-session", "-As", args.session,
|
||||
"-e", f"DF_NAMESPACE={namespace}",
|
||||
"-e", f"DF_PLATFORM={platform}",
|
||||
])
|
||||
|
||||
if args.dry_run:
|
||||
ctx.console.info("Dry run command:")
|
||||
print(" " + " ".join(ssh_cmd))
|
||||
return
|
||||
|
||||
os.execvp("ssh", ssh_cmd)
|
||||
Reference in New Issue
Block a user