mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
feat(agent): wire up agentssh server to allow exec into container (#16638)
Builds on top of https://github.com/coder/coder/pull/16623/ and wires up the ReconnectingPTY server. This does nothing to wire up the web terminal yet but the added test demonstrates the functionality working. Other changes: * Refactors and moves the `SystemEnvInfo` interface to the `agent/usershell` package to address follow-up from https://github.com/coder/coder/pull/16623#discussion_r1967580249 * Marks `usershellinfo.Get` as deprecated. Consumers should use the `EnvInfoer` interface instead. --------- Co-authored-by: Mathias Fredriksson <mafredri@gmail.com> Co-authored-by: Danny Kopping <danny@coder.com>
This commit is contained in:
co-authored by
Mathias Fredriksson
Danny Kopping
parent
a3223397cb
commit
172e52317c
@@ -93,6 +93,24 @@ type AgentReconnectingPTYInit struct {
|
||||
Height uint16
|
||||
Width uint16
|
||||
Command string
|
||||
// Container, if set, will attempt to exec into a running container visible to the agent.
|
||||
// This should be a unique container ID (implementation-dependent).
|
||||
Container string
|
||||
// ContainerUser, if set, will set the target user when execing into a container.
|
||||
// This can be a username or UID, depending on the underlying implementation.
|
||||
// This is ignored if Container is not set.
|
||||
ContainerUser string
|
||||
}
|
||||
|
||||
// AgentReconnectingPTYInitOption is a functional option for AgentReconnectingPTYInit.
|
||||
type AgentReconnectingPTYInitOption func(*AgentReconnectingPTYInit)
|
||||
|
||||
// AgentReconnectingPTYInitWithContainer sets the container and container user for the reconnecting PTY session.
|
||||
func AgentReconnectingPTYInitWithContainer(container, containerUser string) AgentReconnectingPTYInitOption {
|
||||
return func(init *AgentReconnectingPTYInit) {
|
||||
init.Container = container
|
||||
init.ContainerUser = containerUser
|
||||
}
|
||||
}
|
||||
|
||||
// ReconnectingPTYRequest is sent from the client to the server
|
||||
@@ -107,7 +125,7 @@ type ReconnectingPTYRequest struct {
|
||||
// ReconnectingPTY spawns a new reconnecting terminal session.
|
||||
// `ReconnectingPTYRequest` should be JSON marshaled and written to the returned net.Conn.
|
||||
// Raw terminal output will be read from the returned net.Conn.
|
||||
func (c *AgentConn) ReconnectingPTY(ctx context.Context, id uuid.UUID, height, width uint16, command string) (net.Conn, error) {
|
||||
func (c *AgentConn) ReconnectingPTY(ctx context.Context, id uuid.UUID, height, width uint16, command string, initOpts ...AgentReconnectingPTYInitOption) (net.Conn, error) {
|
||||
ctx, span := tracing.StartSpan(ctx)
|
||||
defer span.End()
|
||||
|
||||
@@ -119,12 +137,16 @@ func (c *AgentConn) ReconnectingPTY(ctx context.Context, id uuid.UUID, height, w
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := json.Marshal(AgentReconnectingPTYInit{
|
||||
rptyInit := AgentReconnectingPTYInit{
|
||||
ID: id,
|
||||
Height: height,
|
||||
Width: width,
|
||||
Command: command,
|
||||
})
|
||||
}
|
||||
for _, o := range initOpts {
|
||||
o(&rptyInit)
|
||||
}
|
||||
data, err := json.Marshal(rptyInit)
|
||||
if err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, err
|
||||
|
||||
@@ -12,12 +12,14 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/wgengine/capture"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/tailnet"
|
||||
"github.com/coder/coder/v2/tailnet/proto"
|
||||
@@ -305,6 +307,16 @@ type WorkspaceAgentReconnectingPTYOpts struct {
|
||||
// issue-reconnecting-pty-signed-token endpoint. If set, the session token
|
||||
// on the client will not be sent.
|
||||
SignedToken string
|
||||
|
||||
// Experimental: Container, if set, will attempt to exec into a running container
|
||||
// visible to the agent. This should be a unique container ID
|
||||
// (implementation-dependent).
|
||||
// ContainerUser is the user as which to exec into the container.
|
||||
// NOTE: This feature is currently experimental and is currently "opt-in".
|
||||
// In order to use this feature, the agent must have the environment variable
|
||||
// CODER_AGENT_DEVCONTAINERS_ENABLE set to "true".
|
||||
Container string
|
||||
ContainerUser string
|
||||
}
|
||||
|
||||
// AgentReconnectingPTY spawns a PTY that reconnects using the token provided.
|
||||
@@ -320,6 +332,12 @@ func (c *Client) AgentReconnectingPTY(ctx context.Context, opts WorkspaceAgentRe
|
||||
q.Set("width", strconv.Itoa(int(opts.Width)))
|
||||
q.Set("height", strconv.Itoa(int(opts.Height)))
|
||||
q.Set("command", opts.Command)
|
||||
if opts.Container != "" {
|
||||
q.Set("container", opts.Container)
|
||||
}
|
||||
if opts.ContainerUser != "" {
|
||||
q.Set("container_user", opts.ContainerUser)
|
||||
}
|
||||
// If we're using a signed token, set the query parameter.
|
||||
if opts.SignedToken != "" {
|
||||
q.Set(codersdk.SignedAppTokenQueryParameter, opts.SignedToken)
|
||||
|
||||
Reference in New Issue
Block a user