mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(agent): enable devcontainers by default (#18533)
This commit is contained in:
+12
-13
@@ -89,9 +89,8 @@ type Options struct {
|
||||
ServiceBannerRefreshInterval time.Duration
|
||||
BlockFileTransfer bool
|
||||
Execer agentexec.Execer
|
||||
|
||||
ExperimentalDevcontainersEnabled bool
|
||||
ContainerAPIOptions []agentcontainers.Option // Enable ExperimentalDevcontainersEnabled for these to be effective.
|
||||
Devcontainers bool
|
||||
DevcontainerAPIOptions []agentcontainers.Option // Enable Devcontainers for these to be effective.
|
||||
}
|
||||
|
||||
type Client interface {
|
||||
@@ -190,8 +189,8 @@ func New(options Options) Agent {
|
||||
metrics: newAgentMetrics(prometheusRegistry),
|
||||
execer: options.Execer,
|
||||
|
||||
experimentalDevcontainersEnabled: options.ExperimentalDevcontainersEnabled,
|
||||
containerAPIOptions: options.ContainerAPIOptions,
|
||||
devcontainers: options.Devcontainers,
|
||||
containerAPIOptions: options.DevcontainerAPIOptions,
|
||||
}
|
||||
// Initially, we have a closed channel, reflecting the fact that we are not initially connected.
|
||||
// Each time we connect we replace the channel (while holding the closeMutex) with a new one
|
||||
@@ -272,9 +271,9 @@ type agent struct {
|
||||
metrics *agentMetrics
|
||||
execer agentexec.Execer
|
||||
|
||||
experimentalDevcontainersEnabled bool
|
||||
containerAPIOptions []agentcontainers.Option
|
||||
containerAPI atomic.Pointer[agentcontainers.API] // Set by apiHandler.
|
||||
devcontainers bool
|
||||
containerAPIOptions []agentcontainers.Option
|
||||
containerAPI atomic.Pointer[agentcontainers.API] // Set by apiHandler.
|
||||
}
|
||||
|
||||
func (a *agent) TailnetConn() *tailnet.Conn {
|
||||
@@ -311,7 +310,7 @@ func (a *agent) init() {
|
||||
return a.reportConnection(id, connectionType, ip)
|
||||
},
|
||||
|
||||
ExperimentalDevContainersEnabled: a.experimentalDevcontainersEnabled,
|
||||
ExperimentalContainers: a.devcontainers,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -340,7 +339,7 @@ func (a *agent) init() {
|
||||
a.metrics.connectionsTotal, a.metrics.reconnectingPTYErrors,
|
||||
a.reconnectingPTYTimeout,
|
||||
func(s *reconnectingpty.Server) {
|
||||
s.ExperimentalDevcontainersEnabled = a.experimentalDevcontainersEnabled
|
||||
s.ExperimentalContainers = a.devcontainers
|
||||
},
|
||||
)
|
||||
go a.runLoop()
|
||||
@@ -1087,9 +1086,9 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
|
||||
slog.F("parent_id", manifest.ParentID),
|
||||
slog.F("agent_id", manifest.AgentID),
|
||||
)
|
||||
if a.experimentalDevcontainersEnabled {
|
||||
if a.devcontainers {
|
||||
a.logger.Info(ctx, "devcontainers are not supported on sub agents, disabling feature")
|
||||
a.experimentalDevcontainersEnabled = false
|
||||
a.devcontainers = false
|
||||
}
|
||||
}
|
||||
a.client.RewriteDERPMap(manifest.DERPMap)
|
||||
@@ -1145,7 +1144,7 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
|
||||
scripts = manifest.Scripts
|
||||
scriptRunnerOpts []agentscripts.InitOption
|
||||
)
|
||||
if a.experimentalDevcontainersEnabled {
|
||||
if a.devcontainers {
|
||||
var dcScripts []codersdk.WorkspaceAgentScript
|
||||
scripts, dcScripts = agentcontainers.ExtractAndInitializeDevcontainerScripts(manifest.Devcontainers, scripts)
|
||||
// See ExtractAndInitializeDevcontainerScripts for motivation
|
||||
|
||||
+8
-9
@@ -1954,8 +1954,8 @@ func TestAgent_ReconnectingPTYContainer(t *testing.T) {
|
||||
|
||||
// nolint: dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, agentsdk.Manifest{}, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
})
|
||||
@@ -2161,9 +2161,9 @@ func TestAgent_DevcontainerAutostart(t *testing.T) {
|
||||
|
||||
//nolint:dogsled
|
||||
_, agentClient, _, _, _ := setupAgent(t, manifest, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(
|
||||
o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(
|
||||
o.DevcontainerAPIOptions,
|
||||
// Only match this specific dev container.
|
||||
agentcontainers.WithClock(mClock),
|
||||
agentcontainers.WithContainerLabelIncludeFilter("devcontainer.local_folder", tempWorkspaceFolder),
|
||||
@@ -2312,8 +2312,8 @@ func TestAgent_DevcontainerRecreate(t *testing.T) {
|
||||
|
||||
//nolint:dogsled
|
||||
conn, client, _, _, _ := setupAgent(t, manifest, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerLabelIncludeFilter("devcontainer.local_folder", workspaceFolder),
|
||||
)
|
||||
})
|
||||
@@ -2438,8 +2438,7 @@ func TestAgent_DevcontainersDisabledForSubAgent(t *testing.T) {
|
||||
|
||||
// Setup the agent with devcontainers enabled initially.
|
||||
//nolint:dogsled
|
||||
conn, _, _, _, _ := setupAgent(t, manifest, 0, func(_ *agenttest.Client, o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
conn, _, _, _, _ := setupAgent(t, manifest, 0, func(*agenttest.Client, *agent.Options) {
|
||||
})
|
||||
|
||||
// Query the containers API endpoint. This should fail because
|
||||
|
||||
@@ -113,9 +113,10 @@ type Config struct {
|
||||
BlockFileTransfer bool
|
||||
// ReportConnection.
|
||||
ReportConnection reportConnectionFunc
|
||||
// Experimental: allow connecting to running containers if
|
||||
// CODER_AGENT_DEVCONTAINERS_ENABLE=true.
|
||||
ExperimentalDevContainersEnabled bool
|
||||
// Experimental: allow connecting to running containers via Docker exec.
|
||||
// Note that this is different from the devcontainers feature, which uses
|
||||
// subagents.
|
||||
ExperimentalContainers bool
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
@@ -435,7 +436,7 @@ func (s *Server) sessionHandler(session ssh.Session) {
|
||||
switch ss := session.Subsystem(); ss {
|
||||
case "":
|
||||
case "sftp":
|
||||
if s.config.ExperimentalDevContainersEnabled && container != "" {
|
||||
if s.config.ExperimentalContainers && container != "" {
|
||||
closeCause("sftp not yet supported with containers")
|
||||
_ = session.Exit(1)
|
||||
return
|
||||
@@ -549,7 +550,7 @@ func (s *Server) sessionStart(logger slog.Logger, session ssh.Session, env []str
|
||||
|
||||
var ei usershell.EnvInfoer
|
||||
var err error
|
||||
if s.config.ExperimentalDevContainersEnabled && container != "" {
|
||||
if s.config.ExperimentalContainers && container != "" {
|
||||
ei, err = agentcontainers.EnvInfo(ctx, s.Execer, container, containerUser)
|
||||
if err != nil {
|
||||
s.metrics.sessionErrors.WithLabelValues(magicTypeLabel, ptyLabel, "container_env_info").Add(1)
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ func (a *agent) apiHandler(aAPI proto.DRPCAgentClient26) (http.Handler, func() e
|
||||
cacheDuration: cacheDuration,
|
||||
}
|
||||
|
||||
if a.experimentalDevcontainersEnabled {
|
||||
if a.devcontainers {
|
||||
containerAPIOpts := []agentcontainers.Option{
|
||||
agentcontainers.WithExecer(a.execer),
|
||||
agentcontainers.WithCommandEnv(a.sshServer.CommandEnv),
|
||||
|
||||
@@ -31,8 +31,10 @@ type Server struct {
|
||||
connCount atomic.Int64
|
||||
reconnectingPTYs sync.Map
|
||||
timeout time.Duration
|
||||
|
||||
ExperimentalDevcontainersEnabled bool
|
||||
// Experimental: allow connecting to running containers via Docker exec.
|
||||
// Note that this is different from the devcontainers feature, which uses
|
||||
// subagents.
|
||||
ExperimentalContainers bool
|
||||
}
|
||||
|
||||
// NewServer returns a new ReconnectingPTY server
|
||||
@@ -187,7 +189,7 @@ func (s *Server) handleConn(ctx context.Context, logger slog.Logger, conn net.Co
|
||||
}()
|
||||
|
||||
var ei usershell.EnvInfoer
|
||||
if s.ExperimentalDevcontainersEnabled && msg.Container != "" {
|
||||
if s.ExperimentalContainers && msg.Container != "" {
|
||||
dei, err := agentcontainers.EnvInfo(ctx, s.commandCreator.Execer, msg.Container, msg.ContainerUser)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("get container env info: %w", err)
|
||||
|
||||
+9
-10
@@ -55,8 +55,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||
blockFileTransfer bool
|
||||
agentHeaderCommand string
|
||||
agentHeader []string
|
||||
|
||||
experimentalDevcontainersEnabled bool
|
||||
devcontainers bool
|
||||
)
|
||||
cmd := &serpent.Command{
|
||||
Use: "agent",
|
||||
@@ -321,7 +320,7 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||
return xerrors.Errorf("create agent execer: %w", err)
|
||||
}
|
||||
|
||||
if experimentalDevcontainersEnabled {
|
||||
if devcontainers {
|
||||
logger.Info(ctx, "agent devcontainer detection enabled")
|
||||
} else {
|
||||
logger.Info(ctx, "agent devcontainer detection not enabled")
|
||||
@@ -359,11 +358,11 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||
SSHMaxTimeout: sshMaxTimeout,
|
||||
Subsystems: subsystems,
|
||||
|
||||
PrometheusRegistry: prometheusRegistry,
|
||||
BlockFileTransfer: blockFileTransfer,
|
||||
Execer: execer,
|
||||
ExperimentalDevcontainersEnabled: experimentalDevcontainersEnabled,
|
||||
ContainerAPIOptions: []agentcontainers.Option{
|
||||
PrometheusRegistry: prometheusRegistry,
|
||||
BlockFileTransfer: blockFileTransfer,
|
||||
Execer: execer,
|
||||
Devcontainers: devcontainers,
|
||||
DevcontainerAPIOptions: []agentcontainers.Option{
|
||||
agentcontainers.WithSubAgentURL(r.agentURL.String()),
|
||||
},
|
||||
})
|
||||
@@ -506,10 +505,10 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
|
||||
},
|
||||
{
|
||||
Flag: "devcontainers-enable",
|
||||
Default: "false",
|
||||
Default: "true",
|
||||
Env: "CODER_AGENT_DEVCONTAINERS_ENABLE",
|
||||
Description: "Allow the agent to automatically detect running devcontainers.",
|
||||
Value: serpent.BoolOf(&experimentalDevcontainersEnabled),
|
||||
Value: serpent.BoolOf(&devcontainers),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -116,8 +116,8 @@ func TestExpRpty(t *testing.T) {
|
||||
})
|
||||
|
||||
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerLabelIncludeFilter(wantLabel, "true"),
|
||||
)
|
||||
})
|
||||
|
||||
+4
-4
@@ -334,8 +334,8 @@ func TestOpenVSCodeDevContainer(t *testing.T) {
|
||||
})
|
||||
|
||||
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerCLI(mccli),
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
@@ -509,8 +509,8 @@ func TestOpenVSCodeDevContainer_NoAgentDirectory(t *testing.T) {
|
||||
})
|
||||
|
||||
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerCLI(mccli),
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
|
||||
+4
-4
@@ -2029,8 +2029,8 @@ func TestSSH_Container(t *testing.T) {
|
||||
})
|
||||
|
||||
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
})
|
||||
@@ -2069,8 +2069,8 @@ func TestSSH_Container(t *testing.T) {
|
||||
Warnings: nil,
|
||||
}, nil).AnyTimes()
|
||||
_ = agenttest.New(t, client.URL, agentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerCLI(mLister),
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ OPTIONS:
|
||||
--debug-address string, $CODER_AGENT_DEBUG_ADDRESS (default: 127.0.0.1:2113)
|
||||
The bind address to serve a debug HTTP server.
|
||||
|
||||
--devcontainers-enable bool, $CODER_AGENT_DEVCONTAINERS_ENABLE (default: false)
|
||||
--devcontainers-enable bool, $CODER_AGENT_DEVCONTAINERS_ENABLE (default: true)
|
||||
Allow the agent to automatically detect running devcontainers.
|
||||
|
||||
--log-dir string, $CODER_AGENT_LOG_DIR (default: /tmp)
|
||||
|
||||
@@ -1250,8 +1250,8 @@ func TestWorkspaceAgentContainers(t *testing.T) {
|
||||
return agents
|
||||
}).Do()
|
||||
_ = agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
})
|
||||
@@ -1358,8 +1358,8 @@ func TestWorkspaceAgentContainers(t *testing.T) {
|
||||
}).Do()
|
||||
_ = agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
|
||||
o.Logger = logger.Named("agent")
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerCLI(mcl),
|
||||
agentcontainers.WithContainerLabelIncludeFilter("this.label.does.not.exist.ignore.devcontainers", "true"),
|
||||
)
|
||||
@@ -1473,9 +1473,9 @@ func TestWorkspaceAgentRecreateDevcontainer(t *testing.T) {
|
||||
}).Do()
|
||||
_ = agenttest.New(t, client.URL, r.AgentToken, func(o *agent.Options) {
|
||||
o.Logger = logger.Named("agent")
|
||||
o.ExperimentalDevcontainersEnabled = true
|
||||
o.ContainerAPIOptions = append(
|
||||
o.ContainerAPIOptions,
|
||||
o.Devcontainers = true
|
||||
o.DevcontainerAPIOptions = append(
|
||||
o.DevcontainerAPIOptions,
|
||||
agentcontainers.WithContainerCLI(mccli),
|
||||
agentcontainers.WithDevcontainerCLI(mdccli),
|
||||
agentcontainers.WithWatcher(watcher.NewNoop()),
|
||||
|
||||
Reference in New Issue
Block a user