-
Notifications
You must be signed in to change notification settings - Fork 943
feat(cli): allow SSH command to connect to running container #16726
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
Changes from 1 commit
3dc994a
56538a9
7437e49
a1d0d00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…tainer
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ import ( | |
|
||
"cdr.dev/slog" | ||
|
||
"github.com/coder/coder/v2/agent/agentcontainers" | ||
"github.com/coder/coder/v2/agent/agentexec" | ||
"github.com/coder/coder/v2/agent/agentrsa" | ||
"github.com/coder/coder/v2/agent/usershell" | ||
|
@@ -104,6 +105,9 @@ type Config struct { | |
BlockFileTransfer bool | ||
// ReportConnection. | ||
ReportConnection reportConnectionFunc | ||
// Experimental: allow connecting to running containers if | ||
// CODER_AGENT_DEVCONTAINERS_ENABLE=true. | ||
ExperimentalContainersEnabled bool | ||
} | ||
|
||
type Server struct { | ||
|
@@ -324,6 +328,22 @@ func (s *sessionCloseTracker) Close() error { | |
return s.Session.Close() | ||
} | ||
|
||
func extractContainerInfo(env []string) (container, containerUser string, filteredEnv []string) { | ||
for _, kv := range env { | ||
if strings.HasPrefix(kv, "CODER_CONTAINER=") { | ||
container = strings.TrimPrefix(kv, "CODER_CONTAINER=") | ||
} | ||
|
||
if strings.HasPrefix(kv, "CODER_CONTAINER_USER=") { | ||
containerUser = strings.TrimPrefix(kv, "CODER_CONTAINER_USER=") | ||
} | ||
} | ||
|
||
return container, containerUser, slices.DeleteFunc(env, func(kv string) bool { | ||
return strings.HasPrefix(kv, "CODER_CONTAINER=") || strings.HasPrefix(kv, "CODER_CONTAINER_USER=") | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. review: inspired by the magic session stuff, this seems to be the only real way to smuggle info betweeen the client and the server. |
||
} | ||
|
||
func (s *Server) sessionHandler(session ssh.Session) { | ||
ctx := session.Context() | ||
id := uuid.New() | ||
|
@@ -353,6 +373,7 @@ func (s *Server) sessionHandler(session ssh.Session) { | |
defer s.trackSession(session, false) | ||
|
||
reportSession := true | ||
|
||
switch magicType { | ||
case MagicSessionTypeVSCode: | ||
s.connCountVSCode.Add(1) | ||
|
@@ -395,9 +416,19 @@ func (s *Server) sessionHandler(session ssh.Session) { | |
return | ||
} | ||
|
||
container, containerUser, env := extractContainerInfo(env) | ||
s.logger.Debug(ctx, "container info", | ||
johnstcn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
slog.F("container", container), | ||
slog.F("container_user", containerUser), | ||
) | ||
|
||
switch ss := session.Subsystem(); ss { | ||
case "": | ||
case "sftp": | ||
if s.config.ExperimentalContainersEnabled && container != "" { | ||
closeCause("sftp not yet supported with containers") | ||
return | ||
johnstcn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
err := s.sftpHandler(logger, session) | ||
if err != nil { | ||
closeCause(err.Error()) | ||
|
@@ -422,7 +453,7 @@ func (s *Server) sessionHandler(session ssh.Session) { | |
env = append(env, fmt.Sprintf("DISPLAY=localhost:%d.%d", display, x11.ScreenNumber)) | ||
} | ||
|
||
err := s.sessionStart(logger, session, env, magicType) | ||
err := s.sessionStart(logger, session, env, magicType, container, containerUser) | ||
var exitError *exec.ExitError | ||
if xerrors.As(err, &exitError) { | ||
code := exitError.ExitCode() | ||
|
@@ -495,30 +526,35 @@ func (s *Server) fileTransferBlocked(session ssh.Session) bool { | |
return false | ||
} | ||
|
||
func (s *Server) sessionStart(logger slog.Logger, session ssh.Session, env []string, magicType MagicSessionType) (retErr error) { | ||
func (s *Server) sessionStart(logger slog.Logger, session ssh.Session, env []string, magicType MagicSessionType, container, containerUser string) (retErr error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. review: think it might be time to consider an opts struct? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could be done, but no need to do that yet IMO. If this grows more then probably. I'd actually want us to refactor the whole session handling and manage our own |
||
ctx := session.Context() | ||
|
||
magicTypeLabel := magicTypeMetricLabel(magicType) | ||
sshPty, windowSize, isPty := session.Pty() | ||
ptyLabel := "no" | ||
if isPty { | ||
ptyLabel = "yes" | ||
} | ||
|
||
cmd, err := s.CreateCommand(ctx, session.RawCommand(), env, nil) | ||
if err != nil { | ||
ptyLabel := "no" | ||
if isPty { | ||
ptyLabel = "yes" | ||
// plumb in envinfoer here to modify command for container exec? | ||
johnstcn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var ei usershell.EnvInfoer | ||
var err error | ||
if s.config.ExperimentalContainersEnabled && container != "" { | ||
ei, err = agentcontainers.EnvInfo(ctx, s.Execer, container, containerUser) | ||
if err != nil { | ||
s.metrics.sessionErrors.WithLabelValues(magicTypeLabel, ptyLabel, "container_env_info").Add(1) | ||
return err | ||
johnstcn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
cmd, err := s.CreateCommand(ctx, session.RawCommand(), env, ei) | ||
if err != nil { | ||
s.metrics.sessionErrors.WithLabelValues(magicTypeLabel, ptyLabel, "create_command").Add(1) | ||
return err | ||
} | ||
|
||
if ssh.AgentRequested(session) { | ||
l, err := ssh.NewAgentListener() | ||
if err != nil { | ||
ptyLabel := "no" | ||
if isPty { | ||
ptyLabel = "yes" | ||
} | ||
|
||
s.metrics.sessionErrors.WithLabelValues(magicTypeLabel, ptyLabel, "listener").Add(1) | ||
return xerrors.Errorf("new agent listener: %w", err) | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.