Skip to content

Commit ee6b803

Browse files
feat(agent/agentcontainers): allow auto starting discovered dev containers
1 parent 070178c commit ee6b803

File tree

3 files changed

+131
-4
lines changed

3 files changed

+131
-4
lines changed

agent/agentcontainers/api.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ func WithCommandEnv(ce CommandEnv) Option {
143143
strings.HasPrefix(s, "CODER_WORKSPACE_AGENT_URL=") ||
144144
strings.HasPrefix(s, "CODER_AGENT_TOKEN=") ||
145145
strings.HasPrefix(s, "CODER_AGENT_AUTH=") ||
146-
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_ENABLE=")
146+
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_ENABLE=") ||
147+
strings.HasPrefix(s, "CODER_AGENT_DEVCONTAINERS_PROJECT_DISCOVERY_ENABLE=")
147148
})
148149
return shell, dir, env, nil
149150
}
@@ -524,23 +525,39 @@ func (api *API) discoverDevcontainersInProject(projectPath string) error {
524525

525526
workspaceFolder := strings.TrimSuffix(path, relativeConfigPath)
526527

527-
logger.Debug(api.ctx, "discovered dev container project", slog.F("workspace_folder", workspaceFolder))
528+
logger := logger.With(slog.F("workspace_folder", workspaceFolder))
529+
logger.Debug(api.ctx, "discovered dev container project")
528530

529531
api.mu.Lock()
530532
if _, found := api.knownDevcontainers[workspaceFolder]; !found {
531-
logger.Debug(api.ctx, "adding dev container project", slog.F("workspace_folder", workspaceFolder))
533+
logger.Debug(api.ctx, "adding dev container project")
532534

533535
dc := codersdk.WorkspaceAgentDevcontainer{
534536
ID: uuid.New(),
535537
Name: "", // Updated later based on container state.
536538
WorkspaceFolder: workspaceFolder,
537539
ConfigPath: path,
538-
Status: "", // Updated later based on container state.
540+
Status: codersdk.WorkspaceAgentDevcontainerStatusStopped,
539541
Dirty: false, // Updated later based on config file changes.
540542
Container: nil,
541543
}
542544

545+
config, err := api.dccli.ReadConfig(api.ctx, workspaceFolder, path, []string{})
546+
if err != nil {
547+
logger.Error(api.ctx, "read project configuration", slog.Error(err))
548+
} else if config.Configuration.Customizations.Coder.AutoStart {
549+
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
550+
}
551+
543552
api.knownDevcontainers[workspaceFolder] = dc
553+
api.broadcastUpdatesLocked()
554+
555+
if dc.Status == codersdk.WorkspaceAgentDevcontainerStatusStarting {
556+
go func() {
557+
_ = api.CreateDevcontainer(dc.WorkspaceFolder, dc.ConfigPath)
558+
}()
559+
}
560+
544561
}
545562
api.mu.Unlock()
546563
}

agent/agentcontainers/api_test.go

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3568,4 +3568,113 @@ func TestDevcontainerDiscovery(t *testing.T) {
35683568
// This is implicitly handled by `testutil.Logger` failing when it
35693569
// detects an error has been logged.
35703570
})
3571+
3572+
t.Run("AutoStart", func(t *testing.T) {
3573+
t.Parallel()
3574+
3575+
tests := []struct {
3576+
name string
3577+
agentDir string
3578+
fs map[string]string
3579+
setupMocks func(mDCCLI *acmock.MockDevcontainerCLI)
3580+
}{
3581+
{
3582+
name: "SingleEnabled",
3583+
agentDir: "/home/coder",
3584+
fs: map[string]string{
3585+
"/home/coder/.git/HEAD": "",
3586+
"/home/coder/.devcontainer/devcontainer.json": "",
3587+
},
3588+
setupMocks: func(mDCCLI *acmock.MockDevcontainerCLI) {
3589+
gomock.InOrder(
3590+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3591+
"/home/coder",
3592+
"/home/coder/.devcontainer/devcontainer.json",
3593+
[]string{},
3594+
).Return(agentcontainers.DevcontainerConfig{
3595+
Configuration: agentcontainers.DevcontainerConfiguration{
3596+
Customizations: agentcontainers.DevcontainerCustomizations{
3597+
Coder: agentcontainers.CoderCustomization{
3598+
AutoStart: true,
3599+
},
3600+
},
3601+
},
3602+
}, nil),
3603+
mDCCLI.EXPECT().Up(gomock.Any(),
3604+
"/home/coder",
3605+
"/home/coder/.devcontainer/devcontainer.json",
3606+
gomock.Any(),
3607+
).Return("", nil),
3608+
)
3609+
},
3610+
},
3611+
{
3612+
name: "SingleDisabled",
3613+
agentDir: "/home/coder",
3614+
fs: map[string]string{
3615+
"/home/coder/.git/HEAD": "",
3616+
"/home/coder/.devcontainer/devcontainer.json": "",
3617+
},
3618+
setupMocks: func(mDCCLI *acmock.MockDevcontainerCLI) {
3619+
gomock.InOrder(
3620+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3621+
"/home/coder",
3622+
"/home/coder/.devcontainer/devcontainer.json",
3623+
[]string{},
3624+
).Return(agentcontainers.DevcontainerConfig{
3625+
Configuration: agentcontainers.DevcontainerConfiguration{
3626+
Customizations: agentcontainers.DevcontainerCustomizations{
3627+
Coder: agentcontainers.CoderCustomization{
3628+
AutoStart: false,
3629+
},
3630+
},
3631+
},
3632+
}, nil),
3633+
)
3634+
},
3635+
},
3636+
}
3637+
3638+
for _, tt := range tests {
3639+
t.Run(tt.name, func(t *testing.T) {
3640+
t.Parallel()
3641+
3642+
var (
3643+
ctx = testutil.Context(t, testutil.WaitShort)
3644+
logger = testutil.Logger(t)
3645+
mClock = quartz.NewMock(t)
3646+
mDCCLI = acmock.NewMockDevcontainerCLI(gomock.NewController(t))
3647+
3648+
r = chi.NewRouter()
3649+
)
3650+
3651+
tt.setupMocks(mDCCLI)
3652+
3653+
api := agentcontainers.NewAPI(logger,
3654+
agentcontainers.WithClock(mClock),
3655+
agentcontainers.WithWatcher(watcher.NewNoop()),
3656+
agentcontainers.WithFileSystem(initFS(t, tt.fs)),
3657+
agentcontainers.WithManifestInfo("owner", "workspace", "parent-agent", "/home/coder"),
3658+
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
3659+
agentcontainers.WithDevcontainerCLI(mDCCLI),
3660+
agentcontainers.WithProjectDiscovery(true),
3661+
)
3662+
api.Start()
3663+
defer api.Close()
3664+
r.Mount("/", api.Routes())
3665+
3666+
require.Eventuallyf(t, func() bool {
3667+
req := httptest.NewRequest(http.MethodGet, "/", nil).WithContext(ctx)
3668+
rec := httptest.NewRecorder()
3669+
r.ServeHTTP(rec, req)
3670+
3671+
got := codersdk.WorkspaceAgentListContainersResponse{}
3672+
err := json.NewDecoder(rec.Body).Decode(&got)
3673+
require.NoError(t, err)
3674+
3675+
return len(got.Devcontainers) >= 1
3676+
}, testutil.WaitShort, testutil.IntervalFast, "dev containers never found")
3677+
})
3678+
}
3679+
})
35713680
}

agent/agentcontainers/devcontainercli.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ type CoderCustomization struct {
9191
Apps []SubAgentApp `json:"apps,omitempty"`
9292
Name string `json:"name,omitempty"`
9393
Ignore bool `json:"ignore,omitempty"`
94+
AutoStart bool `json:"autoStart,omitempty"`
9495
}
9596

9697
type DevcontainerWorkspace struct {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy