@@ -68,7 +68,7 @@ type fakeDevcontainerCLI struct {
6868execErrC chan func (cmd string ,args ... string )error // If set, send fn to return err, nil or close to return execErr.
6969readConfig agentcontainers.DevcontainerConfig
7070readConfigErr error
71- readConfigErrC chan error
71+ readConfigErrC chan func ( envs [] string ) error
7272}
7373
7474func (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,
9999return f .execErr
100100}
101101
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 ) {
103103if f .readConfigErrC != nil {
104104select {
105105case <- ctx .Done ():
106106return agentcontainers.DevcontainerConfig {},ctx .Err ()
107- case err ,ok := <- f .readConfigErrC :
107+ case fn ,ok := <- f .readConfigErrC :
108108if ok {
109- return f .readConfig ,err
109+ return f .readConfig ,fn ( envs )
110110}
111111}
112112}
@@ -252,6 +252,15 @@ func (m *fakeSubAgentClient) Create(ctx context.Context, agent agentcontainers.S
252252}
253253}
254254}
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+ }
255264agent .ID = uuid .New ()
256265agent .AuthToken = uuid .New ()
257266if m .agents == nil {
@@ -1253,7 +1262,8 @@ func TestAPI(t *testing.T) {
12531262deleteErrC :make (chan error ,1 ),
12541263}
12551264fakeDCCLI = & 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 ),
12571267}
12581268
12591269testContainer = codersdk.WorkspaceAgentContainer {
@@ -1293,13 +1303,15 @@ func TestAPI(t *testing.T) {
12931303agentcontainers .WithSubAgentClient (fakeSAC ),
12941304agentcontainers .WithSubAgentURL ("test-subagent-url" ),
12951305agentcontainers .WithDevcontainerCLI (fakeDCCLI ),
1306+ agentcontainers .WithManifestInfo ("test-user" ,"test-workspace" ),
12961307)
12971308apiClose := func () {
12981309closeOnce .Do (func () {
12991310// Close before api.Close() defer to avoid deadlock after test.
13001311close (fakeSAC .createErrC )
13011312close (fakeSAC .deleteErrC )
13021313close (fakeDCCLI .execErrC )
1314+ close (fakeDCCLI .readConfigErrC )
13031315
13041316_ = api .Close ()
13051317})
@@ -1313,6 +1325,13 @@ func TestAPI(t *testing.T) {
13131325assert .Empty (t ,args )
13141326return nil
13151327})// 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+ })
13161335
13171336// Make sure the ticker function has been registered
13181337// before advancing the clock.
@@ -1453,6 +1472,13 @@ func TestAPI(t *testing.T) {
14531472assert .Empty (t ,args )
14541473return nil
14551474})// 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+ })
14561482
14571483err = api .RefreshContainers (ctx )
14581484require .NoError (t ,err ,"refresh containers should not fail" )
@@ -1603,6 +1629,116 @@ func TestAPI(t *testing.T) {
16031629assert .Contains (t ,subAgent .DisplayApps ,codersdk .DisplayAppPortForward )
16041630},
16051631},
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+ },
16061742}
16071743
16081744for _ ,tt := range tests {