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

Commitdf5255b

Browse files
feat(agent/agentcontainers): allow auto starting discovered dev containers
1 parent070178c commitdf5255b

File tree

3 files changed

+268
-4
lines changed

3 files changed

+268
-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
returnshell,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+
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+
gofunc() {
557+
_=api.CreateDevcontainer(dc.WorkspaceFolder,dc.ConfigPath)
558+
}()
559+
}
560+
544561
}
545562
api.mu.Unlock()
546563
}

‎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 disbaled.
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