Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit66cf90c

Browse files
feat(agent/agentcontainers): allow auto start for discovered containers (#19040)
Closescoder/internal#711When a `devcontainer.json` has been found and it has `.customizations.coder.autoStart = true`, we will now auto start this dev container.
1 parent398e80f commit66cf90c

File tree

3 files changed

+270
-4
lines changed

3 files changed

+270
-4
lines changed

‎agent/agentcontainers/api.go‎

Lines changed: 23 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
returnshell,dir,env,nil
149150
}
@@ -524,23 +525,41 @@ 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+
iferr!=nil {
547+
logger.Error(api.ctx,"read project configuration",slog.Error(err))
548+
}elseifconfig.Configuration.Customizations.Coder.AutoStart {
549+
dc.Status=codersdk.WorkspaceAgentDevcontainerStatusStarting
550+
}
551+
543552
api.knownDevcontainers[workspaceFolder]=dc
553+
api.broadcastUpdatesLocked()
554+
555+
ifdc.Status==codersdk.WorkspaceAgentDevcontainerStatusStarting {
556+
api.asyncWg.Add(1)
557+
gofunc() {
558+
deferapi.asyncWg.Done()
559+
560+
_=api.CreateDevcontainer(dc.WorkspaceFolder,dc.ConfigPath)
561+
}()
562+
}
544563
}
545564
api.mu.Unlock()
546565
}

‎agent/agentcontainers/api_test.go‎

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3568,4 +3568,250 @@ 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+
namestring
3577+
agentDirstring
3578+
fsmap[string]string
3579+
expectDevcontainerCountint
3580+
setupMocksfunc(mDCCLI*acmock.MockDevcontainerCLI)
3581+
}{
3582+
{
3583+
name:"SingleEnabled",
3584+
agentDir:"/home/coder",
3585+
expectDevcontainerCount:1,
3586+
fs:map[string]string{
3587+
"/home/coder/.git/HEAD":"",
3588+
"/home/coder/.devcontainer/devcontainer.json":"",
3589+
},
3590+
setupMocks:func(mDCCLI*acmock.MockDevcontainerCLI) {
3591+
gomock.InOrder(
3592+
// Given: This dev container has auto start enabled.
3593+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3594+
"/home/coder",
3595+
"/home/coder/.devcontainer/devcontainer.json",
3596+
[]string{},
3597+
).Return(agentcontainers.DevcontainerConfig{
3598+
Configuration: agentcontainers.DevcontainerConfiguration{
3599+
Customizations: agentcontainers.DevcontainerCustomizations{
3600+
Coder: agentcontainers.CoderCustomization{
3601+
AutoStart:true,
3602+
},
3603+
},
3604+
},
3605+
},nil),
3606+
3607+
// Then: We expect it to be started.
3608+
mDCCLI.EXPECT().Up(gomock.Any(),
3609+
"/home/coder",
3610+
"/home/coder/.devcontainer/devcontainer.json",
3611+
gomock.Any(),
3612+
).Return("",nil),
3613+
)
3614+
},
3615+
},
3616+
{
3617+
name:"SingleDisabled",
3618+
agentDir:"/home/coder",
3619+
expectDevcontainerCount:1,
3620+
fs:map[string]string{
3621+
"/home/coder/.git/HEAD":"",
3622+
"/home/coder/.devcontainer/devcontainer.json":"",
3623+
},
3624+
setupMocks:func(mDCCLI*acmock.MockDevcontainerCLI) {
3625+
gomock.InOrder(
3626+
// Given: This dev container has auto start disabled.
3627+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3628+
"/home/coder",
3629+
"/home/coder/.devcontainer/devcontainer.json",
3630+
[]string{},
3631+
).Return(agentcontainers.DevcontainerConfig{
3632+
Configuration: agentcontainers.DevcontainerConfiguration{
3633+
Customizations: agentcontainers.DevcontainerCustomizations{
3634+
Coder: agentcontainers.CoderCustomization{
3635+
AutoStart:false,
3636+
},
3637+
},
3638+
},
3639+
},nil),
3640+
3641+
// Then: We expect it to _not_ be started.
3642+
mDCCLI.EXPECT().Up(gomock.Any(),
3643+
"/home/coder",
3644+
"/home/coder/.devcontainer/devcontainer.json",
3645+
gomock.Any(),
3646+
).Return("",nil).Times(0),
3647+
)
3648+
},
3649+
},
3650+
{
3651+
name:"OneEnabledOneDisabled",
3652+
agentDir:"/home/coder",
3653+
expectDevcontainerCount:2,
3654+
fs:map[string]string{
3655+
"/home/coder/.git/HEAD":"",
3656+
"/home/coder/.devcontainer/devcontainer.json":"",
3657+
"/home/coder/project/.devcontainer.json":"",
3658+
},
3659+
setupMocks:func(mDCCLI*acmock.MockDevcontainerCLI) {
3660+
gomock.InOrder(
3661+
// Given: This dev container has auto start enabled.
3662+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3663+
"/home/coder",
3664+
"/home/coder/.devcontainer/devcontainer.json",
3665+
[]string{},
3666+
).Return(agentcontainers.DevcontainerConfig{
3667+
Configuration: agentcontainers.DevcontainerConfiguration{
3668+
Customizations: agentcontainers.DevcontainerCustomizations{
3669+
Coder: agentcontainers.CoderCustomization{
3670+
AutoStart:true,
3671+
},
3672+
},
3673+
},
3674+
},nil),
3675+
3676+
// Then: We expect it to be started.
3677+
mDCCLI.EXPECT().Up(gomock.Any(),
3678+
"/home/coder",
3679+
"/home/coder/.devcontainer/devcontainer.json",
3680+
gomock.Any(),
3681+
).Return("",nil),
3682+
)
3683+
3684+
gomock.InOrder(
3685+
// Given: This dev container has auto start disabled.
3686+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3687+
"/home/coder/project",
3688+
"/home/coder/project/.devcontainer.json",
3689+
[]string{},
3690+
).Return(agentcontainers.DevcontainerConfig{
3691+
Configuration: agentcontainers.DevcontainerConfiguration{
3692+
Customizations: agentcontainers.DevcontainerCustomizations{
3693+
Coder: agentcontainers.CoderCustomization{
3694+
AutoStart:false,
3695+
},
3696+
},
3697+
},
3698+
},nil),
3699+
3700+
// Then: We expect it to _not_ be started.
3701+
mDCCLI.EXPECT().Up(gomock.Any(),
3702+
"/home/coder/project",
3703+
"/home/coder/project/.devcontainer.json",
3704+
gomock.Any(),
3705+
).Return("",nil).Times(0),
3706+
)
3707+
},
3708+
},
3709+
{
3710+
name:"MultipleEnabled",
3711+
agentDir:"/home/coder",
3712+
expectDevcontainerCount:2,
3713+
fs:map[string]string{
3714+
"/home/coder/.git/HEAD":"",
3715+
"/home/coder/.devcontainer/devcontainer.json":"",
3716+
"/home/coder/project/.devcontainer.json":"",
3717+
},
3718+
setupMocks:func(mDCCLI*acmock.MockDevcontainerCLI) {
3719+
gomock.InOrder(
3720+
// Given: This dev container has auto start enabled.
3721+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3722+
"/home/coder",
3723+
"/home/coder/.devcontainer/devcontainer.json",
3724+
[]string{},
3725+
).Return(agentcontainers.DevcontainerConfig{
3726+
Configuration: agentcontainers.DevcontainerConfiguration{
3727+
Customizations: agentcontainers.DevcontainerCustomizations{
3728+
Coder: agentcontainers.CoderCustomization{
3729+
AutoStart:true,
3730+
},
3731+
},
3732+
},
3733+
},nil),
3734+
3735+
// Then: We expect it to be started.
3736+
mDCCLI.EXPECT().Up(gomock.Any(),
3737+
"/home/coder",
3738+
"/home/coder/.devcontainer/devcontainer.json",
3739+
gomock.Any(),
3740+
).Return("",nil),
3741+
)
3742+
3743+
gomock.InOrder(
3744+
// Given: This dev container has auto start enabled.
3745+
mDCCLI.EXPECT().ReadConfig(gomock.Any(),
3746+
"/home/coder/project",
3747+
"/home/coder/project/.devcontainer.json",
3748+
[]string{},
3749+
).Return(agentcontainers.DevcontainerConfig{
3750+
Configuration: agentcontainers.DevcontainerConfiguration{
3751+
Customizations: agentcontainers.DevcontainerCustomizations{
3752+
Coder: agentcontainers.CoderCustomization{
3753+
AutoStart:true,
3754+
},
3755+
},
3756+
},
3757+
},nil),
3758+
3759+
// Then: We expect it to be started.
3760+
mDCCLI.EXPECT().Up(gomock.Any(),
3761+
"/home/coder/project",
3762+
"/home/coder/project/.devcontainer.json",
3763+
gomock.Any(),
3764+
).Return("",nil),
3765+
)
3766+
},
3767+
},
3768+
}
3769+
3770+
for_,tt:=rangetests {
3771+
t.Run(tt.name,func(t*testing.T) {
3772+
t.Parallel()
3773+
3774+
var (
3775+
ctx=testutil.Context(t,testutil.WaitShort)
3776+
logger=testutil.Logger(t)
3777+
mClock=quartz.NewMock(t)
3778+
mDCCLI=acmock.NewMockDevcontainerCLI(gomock.NewController(t))
3779+
3780+
r=chi.NewRouter()
3781+
)
3782+
3783+
// Given: We setup our mocks. These mocks handle our expectations for these
3784+
// tests. If there are missing/unexpected mock calls, the test will fail.
3785+
tt.setupMocks(mDCCLI)
3786+
3787+
api:=agentcontainers.NewAPI(logger,
3788+
agentcontainers.WithClock(mClock),
3789+
agentcontainers.WithWatcher(watcher.NewNoop()),
3790+
agentcontainers.WithFileSystem(initFS(t,tt.fs)),
3791+
agentcontainers.WithManifestInfo("owner","workspace","parent-agent","/home/coder"),
3792+
agentcontainers.WithContainerCLI(&fakeContainerCLI{}),
3793+
agentcontainers.WithDevcontainerCLI(mDCCLI),
3794+
agentcontainers.WithProjectDiscovery(true),
3795+
)
3796+
api.Start()
3797+
deferapi.Close()
3798+
r.Mount("/",api.Routes())
3799+
3800+
// When: All expected dev containers have been found.
3801+
require.Eventuallyf(t,func()bool {
3802+
req:=httptest.NewRequest(http.MethodGet,"/",nil).WithContext(ctx)
3803+
rec:=httptest.NewRecorder()
3804+
r.ServeHTTP(rec,req)
3805+
3806+
got:= codersdk.WorkspaceAgentListContainersResponse{}
3807+
err:=json.NewDecoder(rec.Body).Decode(&got)
3808+
require.NoError(t,err)
3809+
3810+
returnlen(got.Devcontainers)>=tt.expectDevcontainerCount
3811+
},testutil.WaitShort,testutil.IntervalFast,"dev containers never found")
3812+
3813+
// Then: We expect the mock infra to not fail.
3814+
})
3815+
}
3816+
})
35713817
}

‎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
Namestring`json:"name,omitempty"`
9393
Ignorebool`json:"ignore,omitempty"`
94+
AutoStartbool`json:"autoStart,omitempty"`
9495
}
9596

9697
typeDevcontainerWorkspacestruct {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp