@@ -25,6 +25,7 @@ import (
25
25
"github.com/coder/coder/v2/agent/agentcontainers"
26
26
"github.com/coder/coder/v2/agent/agentcontainers/acmock"
27
27
"github.com/coder/coder/v2/agent/agentcontainers/watcher"
28
+ "github.com/coder/coder/v2/coderd/util/ptr"
28
29
"github.com/coder/coder/v2/codersdk"
29
30
"github.com/coder/coder/v2/testutil"
30
31
"github.com/coder/quartz"
@@ -67,7 +68,7 @@ type fakeDevcontainerCLI struct {
67
68
execErrC chan func (cmd string ,args ... string )error // If set, send fn to return err, nil or close to return execErr.
68
69
readConfig agentcontainers.DevcontainerConfig
69
70
readConfigErr error
70
- readConfigErrC chan error
71
+ readConfigErrC chan func ( envs [] string ) (agentcontainers. DevcontainerConfig , error )
71
72
}
72
73
73
74
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,
98
99
return f .execErr
99
100
}
100
101
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 ) {
102
103
if f .readConfigErrC != nil {
103
104
select {
104
105
case <- ctx .Done ():
105
106
return agentcontainers.DevcontainerConfig {},ctx .Err ()
106
- case err ,ok := <- f .readConfigErrC :
107
+ case fn ,ok := <- f .readConfigErrC :
107
108
if ok {
108
- return f . readConfig , err
109
+ return fn ( envs )
109
110
}
110
111
}
111
112
}
@@ -1268,7 +1269,8 @@ func TestAPI(t *testing.T) {
1268
1269
deleteErrC :make (chan error ,1 ),
1269
1270
}
1270
1271
fakeDCCLI = & fakeDevcontainerCLI {
1271
- execErrC :make (chan func (cmd string ,args ... string )error ,1 ),
1272
+ execErrC :make (chan func (cmd string ,args ... string )error ,1 ),
1273
+ readConfigErrC :make (chan func (envs []string ) (agentcontainers.DevcontainerConfig ,error ),1 ),
1272
1274
}
1273
1275
1274
1276
testContainer = codersdk.WorkspaceAgentContainer {
@@ -1307,13 +1309,16 @@ func TestAPI(t *testing.T) {
1307
1309
agentcontainers .WithSubAgentClient (fakeSAC ),
1308
1310
agentcontainers .WithSubAgentURL ("test-subagent-url" ),
1309
1311
agentcontainers .WithDevcontainerCLI (fakeDCCLI ),
1312
+ agentcontainers .WithUserName ("test-user" ),
1313
+ agentcontainers .WithWorkspaceName ("test-workspace" ),
1310
1314
)
1311
1315
defer api .Close ()
1312
1316
1313
1317
// Close before api.Close() defer to avoid deadlock after test.
1314
1318
defer close (fakeSAC .createErrC )
1315
1319
defer close (fakeSAC .deleteErrC )
1316
1320
defer close (fakeDCCLI .execErrC )
1321
+ defer close (fakeDCCLI .readConfigErrC )
1317
1322
1318
1323
// Allow initial agent creation and injection to succeed.
1319
1324
testutil .RequireSend (ctx ,t ,fakeSAC .createErrC ,nil )
@@ -1322,6 +1327,13 @@ func TestAPI(t *testing.T) {
1322
1327
assert .Empty (t ,args )
1323
1328
return nil
1324
1329
})// 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
+ })
1325
1337
1326
1338
// Make sure the ticker function has been registered
1327
1339
// before advancing the clock.
@@ -1374,6 +1386,13 @@ func TestAPI(t *testing.T) {
1374
1386
assert .Empty (t ,args )
1375
1387
return nil
1376
1388
})// 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
+ })
1377
1396
1378
1397
// Wait until the agent recreation is started.
1379
1398
for len (fakeSAC .createErrC )> 0 {
@@ -1473,6 +1492,72 @@ func TestAPI(t *testing.T) {
1473
1492
assert .Equal (t ,codersdk .DisplayAppVSCodeInsiders ,subAgent .DisplayApps [2 ])
1474
1493
},
1475
1494
},
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
+ },
1476
1561
}
1477
1562
1478
1563
for _ ,tt := range tests {