@@ -212,6 +212,7 @@ func (w *fakeWatcher) sendEventWaitNextCalled(ctx context.Context, event fsnotif
212212
213213// fakeSubAgentClient implements SubAgentClient for testing purposes.
214214type fakeSubAgentClient struct {
215+ logger slog.Logger
215216agents map [uuid.UUID ]agentcontainers.SubAgent
216217
217218listErrC chan error // If set, send to return error, close to return nil.
@@ -240,6 +241,7 @@ func (m *fakeSubAgentClient) List(ctx context.Context) ([]agentcontainers.SubAge
240241}
241242
242243func (m * fakeSubAgentClient )Create (ctx context.Context ,agent agentcontainers.SubAgent ) (agentcontainers.SubAgent ,error ) {
244+ m .logger .Debug (ctx ,"creating sub agent" ,slog .F ("agent" ,agent ))
243245if m .createErrC != nil {
244246select {
245247case <- ctx .Done ():
@@ -261,6 +263,7 @@ func (m *fakeSubAgentClient) Create(ctx context.Context, agent agentcontainers.S
261263}
262264
263265func (m * fakeSubAgentClient )Delete (ctx context.Context ,id uuid.UUID )error {
266+ m .logger .Debug (ctx ,"deleting sub agent" ,slog .F ("id" ,id .String ()))
264267if m .deleteErrC != nil {
265268select {
266269case <- ctx .Done ():
@@ -1245,6 +1248,7 @@ func TestAPI(t *testing.T) {
12451248mClock = quartz .NewMock (t )
12461249mCCLI = acmock .NewMockContainerCLI (gomock .NewController (t ))
12471250fakeSAC = & fakeSubAgentClient {
1251+ logger :logger .Named ("fakeSubAgentClient" ),
12481252createErrC :make (chan error ,1 ),
12491253deleteErrC :make (chan error ,1 ),
12501254}
@@ -1270,7 +1274,7 @@ func TestAPI(t *testing.T) {
12701274
12711275mCCLI .EXPECT ().List (gomock .Any ()).Return (codersdk.WorkspaceAgentListContainersResponse {
12721276Containers : []codersdk.WorkspaceAgentContainer {testContainer },
1273- },nil ).Times (1 + 3 )// 1 initial call +3 updates.
1277+ },nil ).Times (3 )// 1 initial call +2 updates.
12741278gomock .InOrder (
12751279mCCLI .EXPECT ().DetectArchitecture (gomock .Any (),"test-container-id" ).Return (runtime .GOARCH ,nil ),
12761280mCCLI .EXPECT ().ExecAs (gomock .Any (),"test-container-id" ,"root" ,"mkdir" ,"-p" ,"/.coder-agent" ).Return (nil ,nil ),
@@ -1315,19 +1319,20 @@ func TestAPI(t *testing.T) {
13151319tickerTrap .MustWait (ctx ).MustRelease (ctx )
13161320tickerTrap .Close ()
13171321
1318- //Ensure we only inject the agentonce .
1319- for i := range 3 {
1320- _ , aw := mClock . AdvanceNext ( )
1321- aw . MustWait ( ctx )
1322+ //Refresh twice to ensure idempotency of agentcreation .
1323+ err = api . RefreshContainers ( ctx )
1324+ require . NoError ( t , err , "refresh containers should not fail" )
1325+ t . Logf ( "Agents created: %d, deleted: %d" , len ( fakeSAC . created ), len ( fakeSAC . deleted ) )
13221326
1323- t .Logf ("Iteration %d: agents created: %d" ,i + 1 ,len (fakeSAC .created ))
1327+ err = api .RefreshContainers (ctx )
1328+ require .NoError (t ,err ,"refresh containers should not fail" )
1329+ t .Logf ("Agents created: %d, deleted: %d" ,len (fakeSAC .created ),len (fakeSAC .deleted ))
13241330
1325- // Verify agent was created.
1326- require .Len (t ,fakeSAC .created ,1 )
1327- assert .Equal (t ,"test-container" ,fakeSAC .created [0 ].Name )
1328- assert .Equal (t ,"/workspaces" ,fakeSAC .created [0 ].Directory )
1329- assert .Len (t ,fakeSAC .deleted ,0 )
1330- }
1331+ // Verify agent was created.
1332+ require .Len (t ,fakeSAC .created ,1 )
1333+ assert .Equal (t ,"test-container" ,fakeSAC .created [0 ].Name )
1334+ assert .Equal (t ,"/workspaces" ,fakeSAC .created [0 ].Directory )
1335+ assert .Len (t ,fakeSAC .deleted ,0 )
13311336
13321337t .Log ("Agent injected successfully, now testing reinjection into the same container..." )
13331338
@@ -1349,32 +1354,23 @@ func TestAPI(t *testing.T) {
13491354// Expect the agent to be reinjected.
13501355mCCLI .EXPECT ().List (gomock .Any ()).Return (codersdk.WorkspaceAgentListContainersResponse {
13511356Containers : []codersdk.WorkspaceAgentContainer {testContainer },
1352- },nil ).Times (3 )//3 updates .
1357+ },nil ).Times (1 )//1 update .
13531358gomock .InOrder (
13541359mCCLI .EXPECT ().DetectArchitecture (gomock .Any (),"test-container-id" ).Return (runtime .GOARCH ,nil ),
13551360mCCLI .EXPECT ().ExecAs (gomock .Any (),"test-container-id" ,"root" ,"mkdir" ,"-p" ,"/.coder-agent" ).Return (nil ,nil ),
13561361mCCLI .EXPECT ().Copy (gomock .Any (),"test-container-id" ,coderBin ,"/.coder-agent/coder" ).Return (nil ),
13571362mCCLI .EXPECT ().ExecAs (gomock .Any (),"test-container-id" ,"root" ,"chmod" ,"0755" ,"/.coder-agent" ,"/.coder-agent/coder" ).Return (nil ,nil ),
13581363)
13591364
1360- // Allow agent reinjection to succeed.
1361- testutil .RequireSend (ctx ,t ,fakeDCCLI .execErrC ,func (cmd string ,args ... string )error {
1362- assert .Equal (t ,"pwd" ,cmd )
1363- assert .Empty (t ,args )
1364- return nil
1365- })// Exec pwd.
1366-
1367- // Ensure we only inject the agent once.
1368- for i := range 3 {
1369- _ ,aw := mClock .AdvanceNext ()
1370- aw .MustWait (ctx )
1365+ // Agent reinjection will succeed and we will not re-create the
1366+ // agent, nor re-probe pwd.
1367+ err = api .RefreshContainers (ctx )
1368+ require .NoError (t ,err ,"refresh containers should not fail" )
1369+ t .Logf ("Agents created: %d, deleted: %d" ,len (fakeSAC .created ),len (fakeSAC .deleted ))
13711370
1372- t .Logf ("Iteration %d: agents created: %d" ,i + 1 ,len (fakeSAC .created ))
1373-
1374- // Verify that the agent was reused.
1375- require .Len (t ,fakeSAC .created ,1 )
1376- assert .Len (t ,fakeSAC .deleted ,0 )
1377- }
1371+ // Verify that the agent was reused.
1372+ require .Len (t ,fakeSAC .created ,1 )
1373+ assert .Len (t ,fakeSAC .deleted ,0 )
13781374
13791375t .Log ("Agent reinjected successfully, now testing agent deletion and recreation..." )
13801376
@@ -1383,7 +1379,7 @@ func TestAPI(t *testing.T) {
13831379// Expect the agent to be injected.
13841380mCCLI .EXPECT ().List (gomock .Any ()).Return (codersdk.WorkspaceAgentListContainersResponse {
13851381Containers : []codersdk.WorkspaceAgentContainer {testContainer },
1386- },nil ).Times (3 )//3 updates .
1382+ },nil ).Times (1 )//1 update .
13871383gomock .InOrder (
13881384mCCLI .EXPECT ().DetectArchitecture (gomock .Any (),"new-test-container-id" ).Return (runtime .GOARCH ,nil ),
13891385mCCLI .EXPECT ().ExecAs (gomock .Any (),"new-test-container-id" ,"root" ,"mkdir" ,"-p" ,"/.coder-agent" ).Return (nil ,nil ),
@@ -1404,7 +1400,20 @@ func TestAPI(t *testing.T) {
14041400})
14051401<- terminated
14061402
1407- // Simulate the agent deletion.
1403+ fakeDCCLI .readConfig .MergedConfiguration .Customizations .Coder = []agentcontainers.CoderCustomization {
1404+ {
1405+ DisplayApps :map [codersdk.DisplayApp ]bool {
1406+ codersdk .DisplayAppSSH :true ,
1407+ codersdk .DisplayAppWebTerminal :true ,
1408+ codersdk .DisplayAppVSCodeDesktop :true ,
1409+ codersdk .DisplayAppVSCodeInsiders :true ,
1410+ codersdk .DisplayAppPortForward :true ,
1411+ },
1412+ },
1413+ }
1414+
1415+ // Simulate the agent deletion (this happens because the
1416+ // devcontainer configuration changed).
14081417testutil .RequireSend (ctx ,t ,fakeSAC .deleteErrC ,nil )
14091418// Expect the agent to be recreated.
14101419testutil .RequireSend (ctx ,t ,fakeSAC .createErrC ,nil )
@@ -1414,13 +1423,9 @@ func TestAPI(t *testing.T) {
14141423return nil
14151424})// Exec pwd.
14161425
1417- // Advance the clock to run updaterLoop.
1418- for i := range 3 {
1419- _ ,aw := mClock .AdvanceNext ()
1420- aw .MustWait (ctx )
1421-
1422- t .Logf ("Iteration %d: agents created: %d, deleted: %d" ,i + 1 ,len (fakeSAC .created ),len (fakeSAC .deleted ))
1423- }
1426+ err = api .RefreshContainers (ctx )
1427+ require .NoError (t ,err ,"refresh containers should not fail" )
1428+ t .Logf ("Agents created: %d, deleted: %d" ,len (fakeSAC .created ),len (fakeSAC .deleted ))
14241429
14251430// Verify the agent was deleted and recreated.
14261431require .Len (t ,fakeSAC .deleted ,1 ,"there should be one deleted agent after recreation" )
@@ -1453,6 +1458,7 @@ func TestAPI(t *testing.T) {
14531458mClock = quartz .NewMock (t )
14541459mCCLI = acmock .NewMockContainerCLI (gomock .NewController (t ))
14551460fakeSAC = & fakeSubAgentClient {
1461+ logger :logger .Named ("fakeSubAgentClient" ),
14561462agents :map [uuid.UUID ]agentcontainers.SubAgent {
14571463existingAgentID :existingAgent ,
14581464},
@@ -1577,7 +1583,10 @@ func TestAPI(t *testing.T) {
15771583logger = testutil .Logger (t )
15781584mClock = quartz .NewMock (t )
15791585mCCLI = acmock .NewMockContainerCLI (gomock .NewController (t ))
1580- fSAC = & fakeSubAgentClient {createErrC :make (chan error ,1 )}
1586+ fSAC = & fakeSubAgentClient {
1587+ logger :logger .Named ("fakeSubAgentClient" ),
1588+ createErrC :make (chan error ,1 ),
1589+ }
15811590fDCCLI = & fakeDevcontainerCLI {
15821591readConfig : agentcontainers.DevcontainerConfig {
15831592MergedConfiguration : agentcontainers.DevcontainerConfiguration {