feat(agent): enable devcontainers by default (#18533)

This commit is contained in:
Mathias Fredriksson
2025-06-24 21:17:04 +03:00
committed by GitHub
parent fcf93719c9
commit 99d124e276
11 changed files with 59 additions and 59 deletions
+12 -13
View File
@@ -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
View File
@@ -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
+6 -5
View File
@@ -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
View File
@@ -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),
+5 -3
View File
@@ -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
View File
@@ -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),
},
}
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+7 -7
View File
@@ -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()),