@@ -68,7 +68,7 @@ type fakeDevcontainerCLI struct {
68
68
execErrC chan func (cmd string ,args ... string )error // If set, send fn to return err, nil or close to return execErr.
69
69
readConfig agentcontainers.DevcontainerConfig
70
70
readConfigErr error
71
- readConfigErrC chan error
71
+ readConfigErrC chan func ( envs [] string ) error
72
72
}
73
73
74
74
func (f * fakeDevcontainerCLI )Up (ctx context.Context ,_ ,_ string ,_ ... agentcontainers.DevcontainerCLIUpOptions ) (string ,error ) {
@@ -99,14 +99,14 @@ func (f *fakeDevcontainerCLI) Exec(ctx context.Context, _, _ string, cmd string,
99
99
return f .execErr
100
100
}
101
101
102
- 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 ) {
103
103
if f .readConfigErrC != nil {
104
104
select {
105
105
case <- ctx .Done ():
106
106
return agentcontainers.DevcontainerConfig {},ctx .Err ()
107
- case err ,ok := <- f .readConfigErrC :
107
+ case fn ,ok := <- f .readConfigErrC :
108
108
if ok {
109
- return f .readConfig ,err
109
+ return f .readConfig ,fn ( envs )
110
110
}
111
111
}
112
112
}
@@ -252,6 +252,15 @@ func (m *fakeSubAgentClient) Create(ctx context.Context, agent agentcontainers.S
252
252
}
253
253
}
254
254
}
255
+ if agent .Name == "" {
256
+ return agentcontainers.SubAgent {},xerrors .New ("name must be set" )
257
+ }
258
+ if agent .Architecture == "" {
259
+ return agentcontainers.SubAgent {},xerrors .New ("architecture must be set" )
260
+ }
261
+ if agent .OperatingSystem == "" {
262
+ return agentcontainers.SubAgent {},xerrors .New ("operating system must be set" )
263
+ }
255
264
agent .ID = uuid .New ()
256
265
agent .AuthToken = uuid .New ()
257
266
if m .agents == nil {
@@ -1253,7 +1262,8 @@ func TestAPI(t *testing.T) {
1253
1262
deleteErrC :make (chan error ,1 ),
1254
1263
}
1255
1264
fakeDCCLI = & fakeDevcontainerCLI {
1256
- execErrC :make (chan func (cmd string ,args ... string )error ,1 ),
1265
+ execErrC :make (chan func (cmd string ,args ... string )error ,1 ),
1266
+ readConfigErrC :make (chan func (envs []string )error ,1 ),
1257
1267
}
1258
1268
1259
1269
testContainer = codersdk.WorkspaceAgentContainer {
@@ -1293,13 +1303,15 @@ func TestAPI(t *testing.T) {
1293
1303
agentcontainers .WithSubAgentClient (fakeSAC ),
1294
1304
agentcontainers .WithSubAgentURL ("test-subagent-url" ),
1295
1305
agentcontainers .WithDevcontainerCLI (fakeDCCLI ),
1306
+ agentcontainers .WithManifestInfo ("test-user" ,"test-workspace" ),
1296
1307
)
1297
1308
apiClose := func () {
1298
1309
closeOnce .Do (func () {
1299
1310
// Close before api.Close() defer to avoid deadlock after test.
1300
1311
close (fakeSAC .createErrC )
1301
1312
close (fakeSAC .deleteErrC )
1302
1313
close (fakeDCCLI .execErrC )
1314
+ close (fakeDCCLI .readConfigErrC )
1303
1315
1304
1316
_ = api .Close ()
1305
1317
})
@@ -1313,6 +1325,13 @@ func TestAPI(t *testing.T) {
1313
1325
assert .Empty (t ,args )
1314
1326
return nil
1315
1327
})// Exec pwd.
1328
+ testutil .RequireSend (ctx ,t ,fakeDCCLI .readConfigErrC ,func (envs []string )error {
1329
+ assert .Contains (t ,envs ,"CODER_WORKSPACE_AGENT_NAME=test-container" )
1330
+ assert .Contains (t ,envs ,"CODER_WORKSPACE_NAME=test-workspace" )
1331
+ assert .Contains (t ,envs ,"CODER_WORKSPACE_OWNER_NAME=test-user" )
1332
+ assert .Contains (t ,envs ,"CODER_URL=test-subagent-url" )
1333
+ return nil
1334
+ })
1316
1335
1317
1336
// Make sure the ticker function has been registered
1318
1337
// before advancing the clock.
@@ -1453,6 +1472,13 @@ func TestAPI(t *testing.T) {
1453
1472
assert .Empty (t ,args )
1454
1473
return nil
1455
1474
})// Exec pwd.
1475
+ testutil .RequireSend (ctx ,t ,fakeDCCLI .readConfigErrC ,func (envs []string )error {
1476
+ assert .Contains (t ,envs ,"CODER_WORKSPACE_AGENT_NAME=test-container" )
1477
+ assert .Contains (t ,envs ,"CODER_WORKSPACE_NAME=test-workspace" )
1478
+ assert .Contains (t ,envs ,"CODER_WORKSPACE_OWNER_NAME=test-user" )
1479
+ assert .Contains (t ,envs ,"CODER_URL=test-subagent-url" )
1480
+ return nil
1481
+ })
1456
1482
1457
1483
err = api .RefreshContainers (ctx )
1458
1484
require .NoError (t ,err ,"refresh containers should not fail" )
@@ -1603,6 +1629,116 @@ func TestAPI(t *testing.T) {
1603
1629
assert .Contains (t ,subAgent .DisplayApps ,codersdk .DisplayAppPortForward )
1604
1630
},
1605
1631
},
1632
+ {
1633
+ name :"WithApps" ,
1634
+ customization : []agentcontainers.CoderCustomization {
1635
+ {
1636
+ Apps : []agentcontainers.SubAgentApp {
1637
+ {
1638
+ Slug :"web-app" ,
1639
+ DisplayName :"Web Application" ,
1640
+ URL :"http://localhost:8080" ,
1641
+ OpenIn :codersdk .WorkspaceAppOpenInTab ,
1642
+ Share :codersdk .WorkspaceAppSharingLevelOwner ,
1643
+ Icon :"/icons/web.svg" ,
1644
+ Order :int32 (1 ),
1645
+ },
1646
+ {
1647
+ Slug :"api-server" ,
1648
+ DisplayName :"API Server" ,
1649
+ URL :"http://localhost:3000" ,
1650
+ OpenIn :codersdk .WorkspaceAppOpenInSlimWindow ,
1651
+ Share :codersdk .WorkspaceAppSharingLevelAuthenticated ,
1652
+ Icon :"/icons/api.svg" ,
1653
+ Order :int32 (2 ),
1654
+ Hidden :true ,
1655
+ },
1656
+ {
1657
+ Slug :"docs" ,
1658
+ DisplayName :"Documentation" ,
1659
+ URL :"http://localhost:4000" ,
1660
+ OpenIn :codersdk .WorkspaceAppOpenInTab ,
1661
+ Share :codersdk .WorkspaceAppSharingLevelPublic ,
1662
+ Icon :"/icons/book.svg" ,
1663
+ Order :int32 (3 ),
1664
+ },
1665
+ },
1666
+ },
1667
+ },
1668
+ afterCreate :func (t * testing.T ,subAgent agentcontainers.SubAgent ) {
1669
+ require .Len (t ,subAgent .Apps ,3 )
1670
+
1671
+ // Verify first app
1672
+ assert .Equal (t ,"web-app" ,subAgent .Apps [0 ].Slug )
1673
+ assert .Equal (t ,"Web Application" ,subAgent .Apps [0 ].DisplayName )
1674
+ assert .Equal (t ,"http://localhost:8080" ,subAgent .Apps [0 ].URL )
1675
+ assert .Equal (t ,codersdk .WorkspaceAppOpenInTab ,subAgent .Apps [0 ].OpenIn )
1676
+ assert .Equal (t ,codersdk .WorkspaceAppSharingLevelOwner ,subAgent .Apps [0 ].Share )
1677
+ assert .Equal (t ,"/icons/web.svg" ,subAgent .Apps [0 ].Icon )
1678
+ assert .Equal (t ,int32 (1 ),subAgent .Apps [0 ].Order )
1679
+
1680
+ // Verify second app
1681
+ assert .Equal (t ,"api-server" ,subAgent .Apps [1 ].Slug )
1682
+ assert .Equal (t ,"API Server" ,subAgent .Apps [1 ].DisplayName )
1683
+ assert .Equal (t ,"http://localhost:3000" ,subAgent .Apps [1 ].URL )
1684
+ assert .Equal (t ,codersdk .WorkspaceAppOpenInSlimWindow ,subAgent .Apps [1 ].OpenIn )
1685
+ assert .Equal (t ,codersdk .WorkspaceAppSharingLevelAuthenticated ,subAgent .Apps [1 ].Share )
1686
+ assert .Equal (t ,"/icons/api.svg" ,subAgent .Apps [1 ].Icon )
1687
+ assert .Equal (t ,int32 (2 ),subAgent .Apps [1 ].Order )
1688
+ assert .Equal (t ,true ,subAgent .Apps [1 ].Hidden )
1689
+
1690
+ // Verify third app
1691
+ assert .Equal (t ,"docs" ,subAgent .Apps [2 ].Slug )
1692
+ assert .Equal (t ,"Documentation" ,subAgent .Apps [2 ].DisplayName )
1693
+ assert .Equal (t ,"http://localhost:4000" ,subAgent .Apps [2 ].URL )
1694
+ assert .Equal (t ,codersdk .WorkspaceAppOpenInTab ,subAgent .Apps [2 ].OpenIn )
1695
+ assert .Equal (t ,codersdk .WorkspaceAppSharingLevelPublic ,subAgent .Apps [2 ].Share )
1696
+ assert .Equal (t ,"/icons/book.svg" ,subAgent .Apps [2 ].Icon )
1697
+ assert .Equal (t ,int32 (3 ),subAgent .Apps [2 ].Order )
1698
+ },
1699
+ },
1700
+ {
1701
+ name :"AppDeduplication" ,
1702
+ customization : []agentcontainers.CoderCustomization {
1703
+ {
1704
+ Apps : []agentcontainers.SubAgentApp {
1705
+ {
1706
+ Slug :"foo-app" ,
1707
+ Hidden :true ,
1708
+ Order :1 ,
1709
+ },
1710
+ {
1711
+ Slug :"bar-app" ,
1712
+ },
1713
+ },
1714
+ },
1715
+ {
1716
+ Apps : []agentcontainers.SubAgentApp {
1717
+ {
1718
+ Slug :"foo-app" ,
1719
+ Order :2 ,
1720
+ },
1721
+ {
1722
+ Slug :"baz-app" ,
1723
+ },
1724
+ },
1725
+ },
1726
+ },
1727
+ afterCreate :func (t * testing.T ,subAgent agentcontainers.SubAgent ) {
1728
+ require .Len (t ,subAgent .Apps ,3 )
1729
+
1730
+ // As the original "foo-app" gets overridden by the later "foo-app",
1731
+ // we expect "bar-app" to be first in the order.
1732
+ assert .Equal (t ,"bar-app" ,subAgent .Apps [0 ].Slug )
1733
+ assert .Equal (t ,"foo-app" ,subAgent .Apps [1 ].Slug )
1734
+ assert .Equal (t ,"baz-app" ,subAgent .Apps [2 ].Slug )
1735
+
1736
+ // We do not expect the properties from the original "foo-app" to be
1737
+ // carried over.
1738
+ assert .Equal (t ,false ,subAgent .Apps [1 ].Hidden )
1739
+ assert .Equal (t ,int32 (2 ),subAgent .Apps [1 ].Order )
1740
+ },
1741
+ },
1606
1742
}
1607
1743
1608
1744
for _ ,tt := range tests {