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

Commitdd15026

Browse files
feat(agent/agentcontainers): support displayApps from devcontainer config (#18342)
Updates the agent injection routine to read the dev container'sconfiguration so we can add display apps to the sub agent.
1 parentbc74166 commitdd15026

File tree

11 files changed

+558
-22
lines changed

11 files changed

+558
-22
lines changed

‎agent/agentcontainers/acmock/acmock.go

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

‎agent/agentcontainers/api.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,13 +1099,25 @@ func (api *API) injectSubAgentIntoContainerLocked(ctx context.Context, dc coders
10991099
directory=DevcontainerDefaultContainerWorkspaceFolder
11001100
}
11011101

1102+
vardisplayApps []codersdk.DisplayApp
1103+
1104+
ifconfig,err:=api.dccli.ReadConfig(ctx,dc.WorkspaceFolder,dc.ConfigPath);err!=nil {
1105+
api.logger.Error(ctx,"unable to read devcontainer config",slog.Error(err))
1106+
}else {
1107+
coderCustomization:=config.MergedConfiguration.Customizations.Coder
1108+
ifcoderCustomization!=nil {
1109+
displayApps=coderCustomization.DisplayApps
1110+
}
1111+
}
1112+
11021113
// The preparation of the subagent is done, now we can create the
11031114
// subagent record in the database to receive the auth token.
11041115
createdAgent,err:=api.subAgentClient.Create(ctx,SubAgent{
11051116
Name:dc.Name,
11061117
Directory:directory,
11071118
OperatingSystem:"linux",// Assuming Linux for dev containers.
11081119
Architecture:arch,
1120+
DisplayApps:displayApps,
11091121
})
11101122
iferr!=nil {
11111123
returnxerrors.Errorf("create agent: %w",err)

‎agent/agentcontainers/api_test.go

Lines changed: 148 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,14 @@ func (f *fakeContainerCLI) ExecAs(ctx context.Context, name, user string, args .
6060
// fakeDevcontainerCLI implements the agentcontainers.DevcontainerCLI
6161
// interface for testing.
6262
typefakeDevcontainerCLIstruct {
63-
upIDstring
64-
upErrerror
65-
upErrCchanerror// If set, send to return err, close to return upErr.
66-
execErrerror
67-
execErrCchanfunc(cmdstring,args...string)error// If set, send fn to return err, nil or close to return execErr.
63+
upIDstring
64+
upErrerror
65+
upErrCchanerror// If set, send to return err, close to return upErr.
66+
execErrerror
67+
execErrCchanfunc(cmdstring,args...string)error// If set, send fn to return err, nil or close to return execErr.
68+
readConfig agentcontainers.DevcontainerConfig
69+
readConfigErrerror
70+
readConfigErrCchanerror
6871
}
6972

7073
func (f*fakeDevcontainerCLI)Up(ctx context.Context,_,_string,_...agentcontainers.DevcontainerCLIUpOptions) (string,error) {
@@ -95,6 +98,20 @@ func (f *fakeDevcontainerCLI) Exec(ctx context.Context, _, _ string, cmd string,
9598
returnf.execErr
9699
}
97100

101+
func (f*fakeDevcontainerCLI)ReadConfig(ctx context.Context,_,_string,_...agentcontainers.DevcontainerCLIReadConfigOptions) (agentcontainers.DevcontainerConfig,error) {
102+
iff.readConfigErrC!=nil {
103+
select {
104+
case<-ctx.Done():
105+
return agentcontainers.DevcontainerConfig{},ctx.Err()
106+
caseerr,ok:=<-f.readConfigErrC:
107+
ifok {
108+
returnf.readConfig,err
109+
}
110+
}
111+
}
112+
returnf.readConfig,f.readConfigErr
113+
}
114+
98115
// fakeWatcher implements the watcher.Watcher interface for testing.
99116
// It allows controlling what events are sent and when.
100117
typefakeWatcherstruct {
@@ -1132,10 +1149,12 @@ func TestAPI(t *testing.T) {
11321149
Containers: []codersdk.WorkspaceAgentContainer{container},
11331150
},
11341151
}
1152+
fDCCLI:=&fakeDevcontainerCLI{}
11351153

11361154
logger:=slogtest.Make(t,nil).Leveled(slog.LevelDebug)
11371155
api:=agentcontainers.NewAPI(
11381156
logger,
1157+
agentcontainers.WithDevcontainerCLI(fDCCLI),
11391158
agentcontainers.WithContainerCLI(fLister),
11401159
agentcontainers.WithWatcher(fWatcher),
11411160
agentcontainers.WithClock(mClock),
@@ -1421,6 +1440,130 @@ func TestAPI(t *testing.T) {
14211440
assert.Contains(t,fakeSAC.deleted,existingAgentID)
14221441
assert.Empty(t,fakeSAC.agents)
14231442
})
1443+
1444+
t.Run("Create",func(t*testing.T) {
1445+
t.Parallel()
1446+
1447+
ifruntime.GOOS=="windows" {
1448+
t.Skip("Dev Container tests are not supported on Windows (this test uses mocks but fails due to Windows paths)")
1449+
}
1450+
1451+
tests:= []struct {
1452+
namestring
1453+
customization*agentcontainers.CoderCustomization
1454+
afterCreatefunc(t*testing.T,subAgent agentcontainers.SubAgent)
1455+
}{
1456+
{
1457+
name:"WithoutCustomization",
1458+
customization:nil,
1459+
},
1460+
{
1461+
name:"WithDisplayApps",
1462+
customization:&agentcontainers.CoderCustomization{
1463+
DisplayApps: []codersdk.DisplayApp{
1464+
codersdk.DisplayAppSSH,
1465+
codersdk.DisplayAppWebTerminal,
1466+
codersdk.DisplayAppVSCodeInsiders,
1467+
},
1468+
},
1469+
afterCreate:func(t*testing.T,subAgent agentcontainers.SubAgent) {
1470+
require.Len(t,subAgent.DisplayApps,3)
1471+
assert.Equal(t,codersdk.DisplayAppSSH,subAgent.DisplayApps[0])
1472+
assert.Equal(t,codersdk.DisplayAppWebTerminal,subAgent.DisplayApps[1])
1473+
assert.Equal(t,codersdk.DisplayAppVSCodeInsiders,subAgent.DisplayApps[2])
1474+
},
1475+
},
1476+
}
1477+
1478+
for_,tt:=rangetests {
1479+
t.Run(tt.name,func(t*testing.T) {
1480+
t.Parallel()
1481+
1482+
var (
1483+
ctx=testutil.Context(t,testutil.WaitMedium)
1484+
logger=testutil.Logger(t)
1485+
mClock=quartz.NewMock(t)
1486+
mCCLI=acmock.NewMockContainerCLI(gomock.NewController(t))
1487+
fSAC=&fakeSubAgentClient{createErrC:make(chanerror,1)}
1488+
fDCCLI=&fakeDevcontainerCLI{
1489+
readConfig: agentcontainers.DevcontainerConfig{
1490+
MergedConfiguration: agentcontainers.DevcontainerConfiguration{
1491+
Customizations: agentcontainers.DevcontainerCustomizations{
1492+
Coder:tt.customization,
1493+
},
1494+
},
1495+
},
1496+
execErrC:make(chanfunc(cmdstring,args...string)error,1),
1497+
}
1498+
1499+
testContainer= codersdk.WorkspaceAgentContainer{
1500+
ID:"test-container-id",
1501+
FriendlyName:"test-container",
1502+
Image:"test-image",
1503+
Running:true,
1504+
CreatedAt:time.Now(),
1505+
Labels:map[string]string{
1506+
agentcontainers.DevcontainerLocalFolderLabel:"/workspaces",
1507+
agentcontainers.DevcontainerConfigFileLabel:"/workspace/.devcontainer/devcontainer.json",
1508+
},
1509+
}
1510+
)
1511+
1512+
coderBin,err:=os.Executable()
1513+
require.NoError(t,err)
1514+
1515+
// Mock the `List` function to always return out test container.
1516+
mCCLI.EXPECT().List(gomock.Any()).Return(codersdk.WorkspaceAgentListContainersResponse{
1517+
Containers: []codersdk.WorkspaceAgentContainer{testContainer},
1518+
},nil).AnyTimes()
1519+
1520+
// Mock the steps used for injecting the coder agent.
1521+
gomock.InOrder(
1522+
mCCLI.EXPECT().DetectArchitecture(gomock.Any(),testContainer.ID).Return(runtime.GOARCH,nil),
1523+
mCCLI.EXPECT().ExecAs(gomock.Any(),testContainer.ID,"root","mkdir","-p","/.coder-agent").Return(nil,nil),
1524+
mCCLI.EXPECT().Copy(gomock.Any(),testContainer.ID,coderBin,"/.coder-agent/coder").Return(nil),
1525+
mCCLI.EXPECT().ExecAs(gomock.Any(),testContainer.ID,"root","chmod","0755","/.coder-agent","/.coder-agent/coder").Return(nil,nil),
1526+
)
1527+
1528+
mClock.Set(time.Now()).MustWait(ctx)
1529+
tickerTrap:=mClock.Trap().TickerFunc("updaterLoop")
1530+
1531+
api:=agentcontainers.NewAPI(logger,
1532+
agentcontainers.WithClock(mClock),
1533+
agentcontainers.WithContainerCLI(mCCLI),
1534+
agentcontainers.WithDevcontainerCLI(fDCCLI),
1535+
agentcontainers.WithSubAgentClient(fSAC),
1536+
agentcontainers.WithSubAgentURL("test-subagent-url"),
1537+
agentcontainers.WithWatcher(watcher.NewNoop()),
1538+
)
1539+
deferapi.Close()
1540+
1541+
// Close before api.Close() defer to avoid deadlock after test.
1542+
deferclose(fSAC.createErrC)
1543+
deferclose(fDCCLI.execErrC)
1544+
1545+
// Given: We allow agent creation and injection to succeed.
1546+
testutil.RequireSend(ctx,t,fSAC.createErrC,nil)
1547+
testutil.RequireSend(ctx,t,fDCCLI.execErrC,func(cmdstring,args...string)error {
1548+
assert.Equal(t,"pwd",cmd)
1549+
assert.Empty(t,args)
1550+
returnnil
1551+
})
1552+
1553+
// Wait until the ticker has been registered.
1554+
tickerTrap.MustWait(ctx).MustRelease(ctx)
1555+
tickerTrap.Close()
1556+
1557+
// Then: We expected it to succeed
1558+
require.Len(t,fSAC.created,1)
1559+
assert.Equal(t,testContainer.FriendlyName,fSAC.created[0].Name)
1560+
1561+
iftt.afterCreate!=nil {
1562+
tt.afterCreate(t,fSAC.created[0])
1563+
}
1564+
})
1565+
}
1566+
})
14241567
}
14251568

14261569
// mustFindDevcontainerByPath returns the devcontainer with the given workspace

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp