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

Commit6270098

Browse files
feat(agent/agentcontainers): support apps for dev container agents
1 parentdd15026 commit6270098

File tree

8 files changed

+461
-16
lines changed

8 files changed

+461
-16
lines changed

‎agent/agentcontainers/acmock/acmock.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎agent/agentcontainers/api.go

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ type API struct {
6363
subAgentURLstring
6464
subAgentEnv []string
6565

66+
userNamestring
67+
workspaceNamestring
68+
6669
mu sync.RWMutex
6770
closedbool
6871
containers codersdk.WorkspaceAgentListContainersResponse// Output from the last list operation.
@@ -151,6 +154,20 @@ func WithSubAgentEnv(env ...string) Option {
151154
}
152155
}
153156

157+
// WithWorkspaceName sets the workspace name for the sub-agent.
158+
funcWithWorkspaceName(namestring)Option {
159+
returnfunc(api*API) {
160+
api.workspaceName=name
161+
}
162+
}
163+
164+
// WithUserName sets the workspace name for the sub-agent.
165+
funcWithUserName(namestring)Option {
166+
returnfunc(api*API) {
167+
api.userName=name
168+
}
169+
}
170+
154171
// WithDevcontainers sets the known devcontainers for the API. This
155172
// allows the API to be aware of devcontainers defined in the workspace
156173
// agent manifest.
@@ -1100,13 +1117,24 @@ func (api *API) injectSubAgentIntoContainerLocked(ctx context.Context, dc coders
11001117
}
11011118

11021119
vardisplayApps []codersdk.DisplayApp
1103-
1104-
ifconfig,err:=api.dccli.ReadConfig(ctx,dc.WorkspaceFolder,dc.ConfigPath);err!=nil {
1120+
varapps []SubAgentApp
1121+
1122+
ifconfig,err:=api.dccli.ReadConfig(ctx,
1123+
dc.WorkspaceFolder,
1124+
dc.ConfigPath,
1125+
[]string{
1126+
fmt.Sprintf("CODER_AGENT_NAME=%s",dc.Name),
1127+
fmt.Sprintf("CODER_USER_NAME=%s",api.userName),
1128+
fmt.Sprintf("CODER_WORKSPACE_NAME=%s",api.workspaceName),
1129+
fmt.Sprintf("CODER_DEPLOYMENT_URL=%s",api.subAgentURL),
1130+
},
1131+
);err!=nil {
11051132
api.logger.Error(ctx,"unable to read devcontainer config",slog.Error(err))
11061133
}else {
11071134
coderCustomization:=config.MergedConfiguration.Customizations.Coder
11081135
ifcoderCustomization!=nil {
11091136
displayApps=coderCustomization.DisplayApps
1137+
apps=coderCustomization.Apps
11101138
}
11111139
}
11121140

@@ -1118,6 +1146,7 @@ func (api *API) injectSubAgentIntoContainerLocked(ctx context.Context, dc coders
11181146
OperatingSystem:"linux",// Assuming Linux for dev containers.
11191147
Architecture:arch,
11201148
DisplayApps:displayApps,
1149+
Apps:apps,
11211150
})
11221151
iferr!=nil {
11231152
returnxerrors.Errorf("create agent: %w",err)

‎agent/agentcontainers/api_test.go

Lines changed: 90 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/coder/coder/v2/agent/agentcontainers"
2626
"github.com/coder/coder/v2/agent/agentcontainers/acmock"
2727
"github.com/coder/coder/v2/agent/agentcontainers/watcher"
28+
"github.com/coder/coder/v2/coderd/util/ptr"
2829
"github.com/coder/coder/v2/codersdk"
2930
"github.com/coder/coder/v2/testutil"
3031
"github.com/coder/quartz"
@@ -67,7 +68,7 @@ type fakeDevcontainerCLI struct {
6768
execErrCchanfunc(cmdstring,args...string)error// If set, send fn to return err, nil or close to return execErr.
6869
readConfig agentcontainers.DevcontainerConfig
6970
readConfigErrerror
70-
readConfigErrCchanerror
71+
readConfigErrCchanfunc(envs []string) (agentcontainers.DevcontainerConfig,error)
7172
}
7273

7374
func (f*fakeDevcontainerCLI)Up(ctx context.Context,_,_string,_...agentcontainers.DevcontainerCLIUpOptions) (string,error) {
@@ -98,14 +99,14 @@ func (f *fakeDevcontainerCLI) Exec(ctx context.Context, _, _ string, cmd string,
9899
returnf.execErr
99100
}
100101

101-
func (f*fakeDevcontainerCLI)ReadConfig(ctx context.Context,_,_string,_...agentcontainers.DevcontainerCLIReadConfigOptions) (agentcontainers.DevcontainerConfig,error) {
102+
func (f*fakeDevcontainerCLI)ReadConfig(ctx context.Context,_,_string,envs []string,_...agentcontainers.DevcontainerCLIReadConfigOptions) (agentcontainers.DevcontainerConfig,error) {
102103
iff.readConfigErrC!=nil {
103104
select {
104105
case<-ctx.Done():
105106
return agentcontainers.DevcontainerConfig{},ctx.Err()
106-
caseerr,ok:=<-f.readConfigErrC:
107+
casefn,ok:=<-f.readConfigErrC:
107108
ifok {
108-
returnf.readConfig,err
109+
returnfn(envs)
109110
}
110111
}
111112
}
@@ -1268,7 +1269,8 @@ func TestAPI(t *testing.T) {
12681269
deleteErrC:make(chanerror,1),
12691270
}
12701271
fakeDCCLI=&fakeDevcontainerCLI{
1271-
execErrC:make(chanfunc(cmdstring,args...string)error,1),
1272+
execErrC:make(chanfunc(cmdstring,args...string)error,1),
1273+
readConfigErrC:make(chanfunc(envs []string) (agentcontainers.DevcontainerConfig,error),1),
12721274
}
12731275

12741276
testContainer= codersdk.WorkspaceAgentContainer{
@@ -1307,13 +1309,16 @@ func TestAPI(t *testing.T) {
13071309
agentcontainers.WithSubAgentClient(fakeSAC),
13081310
agentcontainers.WithSubAgentURL("test-subagent-url"),
13091311
agentcontainers.WithDevcontainerCLI(fakeDCCLI),
1312+
agentcontainers.WithUserName("test-user"),
1313+
agentcontainers.WithWorkspaceName("test-workspace"),
13101314
)
13111315
deferapi.Close()
13121316

13131317
// Close before api.Close() defer to avoid deadlock after test.
13141318
deferclose(fakeSAC.createErrC)
13151319
deferclose(fakeSAC.deleteErrC)
13161320
deferclose(fakeDCCLI.execErrC)
1321+
deferclose(fakeDCCLI.readConfigErrC)
13171322

13181323
// Allow initial agent creation and injection to succeed.
13191324
testutil.RequireSend(ctx,t,fakeSAC.createErrC,nil)
@@ -1322,6 +1327,13 @@ func TestAPI(t *testing.T) {
13221327
assert.Empty(t,args)
13231328
returnnil
13241329
})// Exec pwd.
1330+
testutil.RequireSend(ctx,t,fakeDCCLI.readConfigErrC,func(envs []string) (agentcontainers.DevcontainerConfig,error) {
1331+
assert.Contains(t,envs,"CODER_AGENT_NAME=test-container")
1332+
assert.Contains(t,envs,"CODER_WORKSPACE_NAME=test-workspace")
1333+
assert.Contains(t,envs,"CODER_USER_NAME=test-user")
1334+
assert.Contains(t,envs,"CODER_DEPLOYMENT_URL=test-subagent-url")
1335+
return agentcontainers.DevcontainerConfig{},nil
1336+
})
13251337

13261338
// Make sure the ticker function has been registered
13271339
// before advancing the clock.
@@ -1374,6 +1386,13 @@ func TestAPI(t *testing.T) {
13741386
assert.Empty(t,args)
13751387
returnnil
13761388
})// Exec pwd.
1389+
testutil.RequireSend(ctx,t,fakeDCCLI.readConfigErrC,func(envs []string) (agentcontainers.DevcontainerConfig,error) {
1390+
assert.Contains(t,envs,"CODER_AGENT_NAME=test-container")
1391+
assert.Contains(t,envs,"CODER_WORKSPACE_NAME=test-workspace")
1392+
assert.Contains(t,envs,"CODER_USER_NAME=test-user")
1393+
assert.Contains(t,envs,"CODER_DEPLOYMENT_URL=test-subagent-url")
1394+
return agentcontainers.DevcontainerConfig{},nil
1395+
})
13771396

13781397
// Wait until the agent recreation is started.
13791398
forlen(fakeSAC.createErrC)>0 {
@@ -1473,6 +1492,72 @@ func TestAPI(t *testing.T) {
14731492
assert.Equal(t,codersdk.DisplayAppVSCodeInsiders,subAgent.DisplayApps[2])
14741493
},
14751494
},
1495+
{
1496+
name:"WithApps",
1497+
customization:&agentcontainers.CoderCustomization{
1498+
Apps: []agentcontainers.SubAgentApp{
1499+
{
1500+
Slug:"web-app",
1501+
DisplayName:ptr.Ref("Web Application"),
1502+
URL:ptr.Ref("http://localhost:8080"),
1503+
OpenIn:codersdk.WorkspaceAppOpenInTab,
1504+
Share:codersdk.WorkspaceAppSharingLevelOwner,
1505+
Icon:ptr.Ref("/icons/web.svg"),
1506+
Order:ptr.Ref(int32(1)),
1507+
},
1508+
{
1509+
Slug:"api-server",
1510+
DisplayName:ptr.Ref("API Server"),
1511+
URL:ptr.Ref("http://localhost:3000"),
1512+
OpenIn:codersdk.WorkspaceAppOpenInSlimWindow,
1513+
Share:codersdk.WorkspaceAppSharingLevelAuthenticated,
1514+
Icon:ptr.Ref("/icons/api.svg"),
1515+
Order:ptr.Ref(int32(2)),
1516+
Hidden:ptr.Ref(true),
1517+
},
1518+
{
1519+
Slug:"docs",
1520+
DisplayName:ptr.Ref("Documentation"),
1521+
URL:ptr.Ref("http://localhost:4000"),
1522+
OpenIn:codersdk.WorkspaceAppOpenInTab,
1523+
Share:codersdk.WorkspaceAppSharingLevelPublic,
1524+
Icon:ptr.Ref("/icons/book.svg"),
1525+
Order:ptr.Ref(int32(3)),
1526+
},
1527+
},
1528+
},
1529+
afterCreate:func(t*testing.T,subAgent agentcontainers.SubAgent) {
1530+
require.Len(t,subAgent.Apps,3)
1531+
1532+
// Verify first app
1533+
assert.Equal(t,"web-app",subAgent.Apps[0].Slug)
1534+
assert.Equal(t,"Web Application",*subAgent.Apps[0].DisplayName)
1535+
assert.Equal(t,"http://localhost:8080",*subAgent.Apps[0].URL)
1536+
assert.Equal(t,codersdk.WorkspaceAppOpenInTab,subAgent.Apps[0].OpenIn)
1537+
assert.Equal(t,codersdk.WorkspaceAppSharingLevelOwner,subAgent.Apps[0].Share)
1538+
assert.Equal(t,"/icons/web.svg",*subAgent.Apps[0].Icon)
1539+
assert.Equal(t,int32(1),*subAgent.Apps[0].Order)
1540+
1541+
// Verify second app
1542+
assert.Equal(t,"api-server",subAgent.Apps[1].Slug)
1543+
assert.Equal(t,"API Server",*subAgent.Apps[1].DisplayName)
1544+
assert.Equal(t,"http://localhost:3000",*subAgent.Apps[1].URL)
1545+
assert.Equal(t,codersdk.WorkspaceAppOpenInSlimWindow,subAgent.Apps[1].OpenIn)
1546+
assert.Equal(t,codersdk.WorkspaceAppSharingLevelAuthenticated,subAgent.Apps[1].Share)
1547+
assert.Equal(t,"/icons/api.svg",*subAgent.Apps[1].Icon)
1548+
assert.Equal(t,int32(2),*subAgent.Apps[1].Order)
1549+
assert.Equal(t,true,*subAgent.Apps[1].Hidden)
1550+
1551+
// Verify third app
1552+
assert.Equal(t,"docs",subAgent.Apps[2].Slug)
1553+
assert.Equal(t,"Documentation",*subAgent.Apps[2].DisplayName)
1554+
assert.Equal(t,"http://localhost:4000",*subAgent.Apps[2].URL)
1555+
assert.Equal(t,codersdk.WorkspaceAppOpenInTab,subAgent.Apps[2].OpenIn)
1556+
assert.Equal(t,codersdk.WorkspaceAppSharingLevelPublic,subAgent.Apps[2].Share)
1557+
assert.Equal(t,"/icons/book.svg",*subAgent.Apps[2].Icon)
1558+
assert.Equal(t,int32(3),*subAgent.Apps[2].Order)
1559+
},
1560+
},
14761561
}
14771562

14781563
for_,tt:=rangetests {

‎agent/agentcontainers/devcontainercli.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ type DevcontainerCustomizations struct {
3232

3333
typeCoderCustomizationstruct {
3434
DisplayApps []codersdk.DisplayApp`json:"displayApps,omitempty"`
35+
Apps []SubAgentApp`json:"apps,omitempty"`
3536
}
3637

3738
// DevcontainerCLI is an interface for the devcontainer CLI.
3839
typeDevcontainerCLIinterface {
3940
Up(ctx context.Context,workspaceFolder,configPathstring,opts...DevcontainerCLIUpOptions) (idstring,errerror)
4041
Exec(ctx context.Context,workspaceFolder,configPathstring,cmdstring,cmdArgs []string,opts...DevcontainerCLIExecOptions)error
41-
ReadConfig(ctx context.Context,workspaceFolder,configPathstring,opts...DevcontainerCLIReadConfigOptions) (DevcontainerConfig,error)
42+
ReadConfig(ctx context.Context,workspaceFolder,configPathstring,env []string,opts...DevcontainerCLIReadConfigOptions) (DevcontainerConfig,error)
4243
}
4344

4445
// DevcontainerCLIUpOptions are options for the devcontainer CLI Up
@@ -113,8 +114,8 @@ type devcontainerCLIReadConfigConfig struct {
113114
stderr io.Writer
114115
}
115116

116-
//WithExecOutput sets additional stdout and stderr writers for logs
117-
// duringExec operations.
117+
//WithReadConfigOutput sets additional stdout and stderr writers for logs
118+
// duringReadConfig operations.
118119
funcWithReadConfigOutput(stdout,stderr io.Writer)DevcontainerCLIReadConfigOptions {
119120
returnfunc(o*devcontainerCLIReadConfigConfig) {
120121
o.stdout=stdout
@@ -250,7 +251,7 @@ func (d *devcontainerCLI) Exec(ctx context.Context, workspaceFolder, configPath
250251
returnnil
251252
}
252253

253-
func (d*devcontainerCLI)ReadConfig(ctx context.Context,workspaceFolder,configPathstring,opts...DevcontainerCLIReadConfigOptions) (DevcontainerConfig,error) {
254+
func (d*devcontainerCLI)ReadConfig(ctx context.Context,workspaceFolder,configPathstring,env []string,opts...DevcontainerCLIReadConfigOptions) (DevcontainerConfig,error) {
254255
conf:=applyDevcontainerCLIReadConfigOptions(opts)
255256
logger:=d.logger.With(slog.F("workspace_folder",workspaceFolder),slog.F("config_path",configPath))
256257

@@ -263,6 +264,7 @@ func (d *devcontainerCLI) ReadConfig(ctx context.Context, workspaceFolder, confi
263264
}
264265

265266
c:=d.execer.CommandContext(ctx,"devcontainer",args...)
267+
c.Env=append(c.Env,env...)
266268

267269
varstdoutBuf bytes.Buffer
268270
stdoutWriters:= []io.Writer{&stdoutBuf,&devcontainerCLILogWriter{ctx:ctx,logger:logger.With(slog.F("stdout",true))}}

‎agent/agentcontainers/devcontainercli_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ func TestDevcontainerCLI_ArgsAndParsing(t *testing.T) {
308308
}
309309

310310
dccli:=agentcontainers.NewDevcontainerCLI(logger,testExecer)
311-
config,err:=dccli.ReadConfig(ctx,tt.workspaceFolder,tt.configPath,tt.opts...)
311+
config,err:=dccli.ReadConfig(ctx,tt.workspaceFolder,tt.configPath,[]string{},tt.opts...)
312312
iftt.wantError {
313313
assert.Error(t,err,"want error")
314314
assert.Equal(t, agentcontainers.DevcontainerConfig{},config,"expected empty config on error")

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp