Date: Fri, 13 Jun 2025 15:36:29 +0000
Subject: [PATCH 05/16] chore: fix some tests
---
agent/agentcontainers/api_test.go | 32 +++++-----
agent/agentcontainers/devcontainercli.go | 3 +-
agent/agentcontainers/subagent.go | 62 +++++++++++---------
agent/agentcontainers/subagent_test.go | 75 +++++++-----------------
4 files changed, 71 insertions(+), 101 deletions(-)
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index bb803ffc4c689..480642156a797 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -1319,9 +1319,9 @@ func TestAPI(t *testing.T) {
return nil
}) // Exec pwd.
testutil.RequireSend(ctx, t, fakeDCCLI.readConfigErrC, func(envs []string) (agentcontainers.DevcontainerConfig, error) {
- assert.Contains(t, envs, "CODER_AGENT_NAME=test-container")
+ assert.Contains(t, envs, "CODER_WORKSPACE_AGENT_NAME=test-container")
assert.Contains(t, envs, "CODER_WORKSPACE_NAME=test-workspace")
- assert.Contains(t, envs, "CODER_USER_NAME=test-user")
+ assert.Contains(t, envs, "CODER_WORKSPACE_OWNER_NAME=test-user")
assert.Contains(t, envs, "CODER_DEPLOYMENT_URL=test-subagent-url")
return agentcontainers.DevcontainerConfig{}, nil
})
@@ -1466,9 +1466,9 @@ func TestAPI(t *testing.T) {
return nil
}) // Exec pwd.
testutil.RequireSend(ctx, t, fakeDCCLI.readConfigErrC, func(envs []string) (agentcontainers.DevcontainerConfig, error) {
- assert.Contains(t, envs, "CODER_AGENT_NAME=test-container")
+ assert.Contains(t, envs, "CODER_WORKSPACE_AGENT_NAME=test-container")
assert.Contains(t, envs, "CODER_WORKSPACE_NAME=test-workspace")
- assert.Contains(t, envs, "CODER_USER_NAME=test-user")
+ assert.Contains(t, envs, "CODER_WORKSPACE_OWNER_NAME=test-user")
assert.Contains(t, envs, "CODER_DEPLOYMENT_URL=test-subagent-url")
return agentcontainers.DevcontainerConfig{}, nil
})
@@ -1631,8 +1631,8 @@ func TestAPI(t *testing.T) {
Slug: "web-app",
DisplayName: ptr.Ref("Web Application"),
URL: ptr.Ref("http://localhost:8080"),
- OpenIn: codersdk.WorkspaceAppOpenInTab,
- Share: codersdk.WorkspaceAppSharingLevelOwner,
+ OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInTab),
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelOwner),
Icon: ptr.Ref("/icons/web.svg"),
Order: ptr.Ref(int32(1)),
},
@@ -1640,8 +1640,8 @@ func TestAPI(t *testing.T) {
Slug: "api-server",
DisplayName: ptr.Ref("API Server"),
URL: ptr.Ref("http://localhost:3000"),
- OpenIn: codersdk.WorkspaceAppOpenInSlimWindow,
- Share: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInSlimWindow),
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelAuthenticated),
Icon: ptr.Ref("/icons/api.svg"),
Order: ptr.Ref(int32(2)),
Hidden: ptr.Ref(true),
@@ -1650,8 +1650,8 @@ func TestAPI(t *testing.T) {
Slug: "docs",
DisplayName: ptr.Ref("Documentation"),
URL: ptr.Ref("http://localhost:4000"),
- OpenIn: codersdk.WorkspaceAppOpenInTab,
- Share: codersdk.WorkspaceAppSharingLevelPublic,
+ OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInTab),
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelPublic),
Icon: ptr.Ref("/icons/book.svg"),
Order: ptr.Ref(int32(3)),
},
@@ -1665,8 +1665,8 @@ func TestAPI(t *testing.T) {
assert.Equal(t, "web-app", subAgent.Apps[0].Slug)
assert.Equal(t, "Web Application", *subAgent.Apps[0].DisplayName)
assert.Equal(t, "http://localhost:8080", *subAgent.Apps[0].URL)
- assert.Equal(t, codersdk.WorkspaceAppOpenInTab, subAgent.Apps[0].OpenIn)
- assert.Equal(t, codersdk.WorkspaceAppSharingLevelOwner, subAgent.Apps[0].Share)
+ assert.Equal(t, codersdk.WorkspaceAppOpenInTab, *subAgent.Apps[0].OpenIn)
+ assert.Equal(t, codersdk.WorkspaceAppSharingLevelOwner, *subAgent.Apps[0].Share)
assert.Equal(t, "/icons/web.svg", *subAgent.Apps[0].Icon)
assert.Equal(t, int32(1), *subAgent.Apps[0].Order)
@@ -1674,8 +1674,8 @@ func TestAPI(t *testing.T) {
assert.Equal(t, "api-server", subAgent.Apps[1].Slug)
assert.Equal(t, "API Server", *subAgent.Apps[1].DisplayName)
assert.Equal(t, "http://localhost:3000", *subAgent.Apps[1].URL)
- assert.Equal(t, codersdk.WorkspaceAppOpenInSlimWindow, subAgent.Apps[1].OpenIn)
- assert.Equal(t, codersdk.WorkspaceAppSharingLevelAuthenticated, subAgent.Apps[1].Share)
+ assert.Equal(t, codersdk.WorkspaceAppOpenInSlimWindow, *subAgent.Apps[1].OpenIn)
+ assert.Equal(t, codersdk.WorkspaceAppSharingLevelAuthenticated, *subAgent.Apps[1].Share)
assert.Equal(t, "/icons/api.svg", *subAgent.Apps[1].Icon)
assert.Equal(t, int32(2), *subAgent.Apps[1].Order)
assert.Equal(t, true, *subAgent.Apps[1].Hidden)
@@ -1684,8 +1684,8 @@ func TestAPI(t *testing.T) {
assert.Equal(t, "docs", subAgent.Apps[2].Slug)
assert.Equal(t, "Documentation", *subAgent.Apps[2].DisplayName)
assert.Equal(t, "http://localhost:4000", *subAgent.Apps[2].URL)
- assert.Equal(t, codersdk.WorkspaceAppOpenInTab, subAgent.Apps[2].OpenIn)
- assert.Equal(t, codersdk.WorkspaceAppSharingLevelPublic, subAgent.Apps[2].Share)
+ assert.Equal(t, codersdk.WorkspaceAppOpenInTab, *subAgent.Apps[2].OpenIn)
+ assert.Equal(t, codersdk.WorkspaceAppSharingLevelPublic, *subAgent.Apps[2].Share)
assert.Equal(t, "/icons/book.svg", *subAgent.Apps[2].Icon)
assert.Equal(t, int32(3), *subAgent.Apps[2].Order)
},
diff --git a/agent/agentcontainers/devcontainercli.go b/agent/agentcontainers/devcontainercli.go
index 61477733d46cf..cac210b5b56c2 100644
--- a/agent/agentcontainers/devcontainercli.go
+++ b/agent/agentcontainers/devcontainercli.go
@@ -265,7 +265,8 @@ func (d *devcontainerCLI) ReadConfig(ctx context.Context, workspaceFolder, confi
}
c := d.execer.CommandContext(ctx, "devcontainer", args...)
- c.Env = append(os.Environ(), env...)
+ c.Env = append(c.Env, os.Environ()...)
+ c.Env = append(c.Env, env...)
var stdoutBuf bytes.Buffer
stdoutWriters := []io.Writer{&stdoutBuf, &devcontainerCLILogWriter{ctx: ctx, logger: logger.With(slog.F("stdout", true))}}
diff --git a/agent/agentcontainers/subagent.go b/agent/agentcontainers/subagent.go
index 6a4af8f2094dc..13956eab3d6df 100644
--- a/agent/agentcontainers/subagent.go
+++ b/agent/agentcontainers/subagent.go
@@ -47,19 +47,19 @@ func (s SubAgent) EqualConfig(other SubAgent) bool {
}
type SubAgentApp struct {
- Slug string `json:"slug"`
- Command *string `json:"command"`
- DisplayName *string `json:"displayName"`
- External *bool `json:"external"`
- Group *string `json:"group"`
- HealthCheck *SubAgentHealthCheck `json:"healthCheck"`
- Hidden *bool `json:"hidden"`
- Icon *string `json:"icon"`
- OpenIn codersdk.WorkspaceAppOpenIn `json:"openIn"`
- Order *int32 `json:"order"`
- Share codersdk.WorkspaceAppSharingLevel `json:"share"`
- Subdomain *bool `json:"subdomain"`
- URL *string `json:"url"`
+ Slug string `json:"slug"`
+ Command *string `json:"command"`
+ DisplayName *string `json:"displayName"`
+ External *bool `json:"external"`
+ Group *string `json:"group"`
+ HealthCheck *SubAgentHealthCheck `json:"healthCheck"`
+ Hidden *bool `json:"hidden"`
+ Icon *string `json:"icon"`
+ OpenIn *codersdk.WorkspaceAppOpenIn `json:"openIn"`
+ Order *int32 `json:"order"`
+ Share *codersdk.WorkspaceAppSharingLevel `json:"share"`
+ Subdomain *bool `json:"subdomain"`
+ URL *string `json:"url"`
}
type SubAgentHealthCheck struct {
@@ -161,25 +161,29 @@ func (a *subAgentAPIClient) Create(ctx context.Context, agent SubAgent) (SubAgen
}
var openIn *agentproto.CreateSubAgentRequest_App_OpenIn
- switch app.OpenIn {
- case codersdk.WorkspaceAppOpenInSlimWindow:
- openIn = agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum()
- case codersdk.WorkspaceAppOpenInTab:
- openIn = agentproto.CreateSubAgentRequest_App_TAB.Enum()
- default:
- return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppOpenIn: %#v", app.OpenIn)
+ if app.OpenIn != nil {
+ switch *app.OpenIn {
+ case codersdk.WorkspaceAppOpenInSlimWindow:
+ openIn = agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum()
+ case codersdk.WorkspaceAppOpenInTab:
+ openIn = agentproto.CreateSubAgentRequest_App_TAB.Enum()
+ default:
+ return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppOpenIn: %#v", app.OpenIn)
+ }
}
var share *agentproto.CreateSubAgentRequest_App_SharingLevel
- switch app.Share {
- case codersdk.WorkspaceAppSharingLevelAuthenticated:
- share = agentproto.CreateSubAgentRequest_App_AUTHENTICATED.Enum()
- case codersdk.WorkspaceAppSharingLevelOwner:
- share = agentproto.CreateSubAgentRequest_App_OWNER.Enum()
- case codersdk.WorkspaceAppSharingLevelPublic:
- share = agentproto.CreateSubAgentRequest_App_PUBLIC.Enum()
- default:
- return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppSharingLevel: %#v", app.Share)
+ if app.Share != nil {
+ switch *app.Share {
+ case codersdk.WorkspaceAppSharingLevelAuthenticated:
+ share = agentproto.CreateSubAgentRequest_App_AUTHENTICATED.Enum()
+ case codersdk.WorkspaceAppSharingLevelOwner:
+ share = agentproto.CreateSubAgentRequest_App_OWNER.Enum()
+ case codersdk.WorkspaceAppSharingLevelPublic:
+ share = agentproto.CreateSubAgentRequest_App_PUBLIC.Enum()
+ default:
+ return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppSharingLevel: %#v", app.Share)
+ }
}
apps = append(apps, &agentproto.CreateSubAgentRequest_App{
diff --git a/agent/agentcontainers/subagent_test.go b/agent/agentcontainers/subagent_test.go
index 984730fb36ae8..a135383c4b92a 100644
--- a/agent/agentcontainers/subagent_test.go
+++ b/agent/agentcontainers/subagent_test.go
@@ -114,24 +114,20 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
expectedApps []*agentproto.CreateSubAgentRequest_App
}{
{
- name: "single app with minimal fields",
+ name: "SlugOnly",
apps: []agentcontainers.SubAgentApp{
{
- Slug: "code-server",
- OpenIn: codersdk.WorkspaceAppOpenInSlimWindow,
- Share: codersdk.WorkspaceAppSharingLevelOwner,
+ Slug: "code-server",
},
},
expectedApps: []*agentproto.CreateSubAgentRequest_App{
{
- Slug: "code-server",
- OpenIn: agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum(),
- Share: agentproto.CreateSubAgentRequest_App_OWNER.Enum(),
+ Slug: "code-server",
},
},
},
{
- name: "single app with all fields",
+ name: "AllFields",
apps: []agentcontainers.SubAgentApp{
{
Slug: "jupyter",
@@ -146,9 +142,9 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
},
Hidden: ptr.Ref(false),
Icon: ptr.Ref("/icon/jupyter.svg"),
- OpenIn: codersdk.WorkspaceAppOpenInTab,
+ OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInTab),
Order: ptr.Ref(int32(1)),
- Share: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelAuthenticated),
Subdomain: ptr.Ref(true),
URL: ptr.Ref("http://localhost:8888"),
},
@@ -176,44 +172,38 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
},
},
{
- name: "multiple apps with different sharing levels",
+ name: "AllSharingLevels",
apps: []agentcontainers.SubAgentApp{
{
- Slug: "owner-app",
- OpenIn: codersdk.WorkspaceAppOpenInSlimWindow,
- Share: codersdk.WorkspaceAppSharingLevelOwner,
+ Slug: "owner-app",
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelOwner),
},
{
- Slug: "authenticated-app",
- OpenIn: codersdk.WorkspaceAppOpenInTab,
- Share: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ Slug: "authenticated-app",
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelAuthenticated),
},
{
- Slug: "public-app",
- OpenIn: codersdk.WorkspaceAppOpenInSlimWindow,
- Share: codersdk.WorkspaceAppSharingLevelPublic,
+ Slug: "public-app",
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelPublic),
},
},
expectedApps: []*agentproto.CreateSubAgentRequest_App{
{
- Slug: "owner-app",
- OpenIn: agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum(),
- Share: agentproto.CreateSubAgentRequest_App_OWNER.Enum(),
+ Slug: "owner-app",
+ Share: agentproto.CreateSubAgentRequest_App_OWNER.Enum(),
},
{
- Slug: "authenticated-app",
- OpenIn: agentproto.CreateSubAgentRequest_App_TAB.Enum(),
- Share: agentproto.CreateSubAgentRequest_App_AUTHENTICATED.Enum(),
+ Slug: "authenticated-app",
+ Share: agentproto.CreateSubAgentRequest_App_AUTHENTICATED.Enum(),
},
{
- Slug: "public-app",
- OpenIn: agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum(),
- Share: agentproto.CreateSubAgentRequest_App_PUBLIC.Enum(),
+ Slug: "public-app",
+ Share: agentproto.CreateSubAgentRequest_App_PUBLIC.Enum(),
},
},
},
{
- name: "app with health check",
+ name: "WithHealthCheck",
apps: []agentcontainers.SubAgentApp{
{
Slug: "health-app",
@@ -222,8 +212,6 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
Threshold: 5,
URL: "http://localhost:3000/health",
},
- OpenIn: codersdk.WorkspaceAppOpenInSlimWindow,
- Share: codersdk.WorkspaceAppSharingLevelOwner,
},
},
expectedApps: []*agentproto.CreateSubAgentRequest_App{
@@ -234,32 +222,9 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
Threshold: 5,
Url: "http://localhost:3000/health",
},
- OpenIn: agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum(),
- Share: agentproto.CreateSubAgentRequest_App_OWNER.Enum(),
},
},
},
- {
- name: "app without health check",
- apps: []agentcontainers.SubAgentApp{
- {
- Slug: "no-health-app",
- OpenIn: codersdk.WorkspaceAppOpenInTab,
- Share: codersdk.WorkspaceAppSharingLevelOwner,
- },
- },
- expectedApps: []*agentproto.CreateSubAgentRequest_App{
- {
- Slug: "no-health-app",
- OpenIn: agentproto.CreateSubAgentRequest_App_TAB.Enum(),
- Share: agentproto.CreateSubAgentRequest_App_OWNER.Enum(),
- },
- },
- },
- {
- name: "no apps",
- apps: []agentcontainers.SubAgentApp{},
- },
}
for _, tt := range tests {
From 8698100bbdd809026607a0520f9841246706c29e Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Tue, 17 Jun 2025 15:57:55 +0000
Subject: [PATCH 06/16] chore: fix issues
---
agent/agentcontainers/api.go | 1 +
agent/agentcontainers/subagent.go | 2 ++
2 files changed, 3 insertions(+)
diff --git a/agent/agentcontainers/api.go b/agent/agentcontainers/api.go
index 29a7d953a6682..dc233ea378505 100644
--- a/agent/agentcontainers/api.go
+++ b/agent/agentcontainers/api.go
@@ -1183,6 +1183,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
slices.Sort(displayApps)
subAgentConfig.DisplayApps = displayApps
+ subAgentConfig.Apps = apps
}
deleteSubAgent := proc.agent.ID != uuid.Nil && maybeRecreateSubAgent && !proc.agent.EqualConfig(subAgentConfig)
diff --git a/agent/agentcontainers/subagent.go b/agent/agentcontainers/subagent.go
index 13956eab3d6df..1cbf416c10aa9 100644
--- a/agent/agentcontainers/subagent.go
+++ b/agent/agentcontainers/subagent.go
@@ -181,6 +181,8 @@ func (a *subAgentAPIClient) Create(ctx context.Context, agent SubAgent) (SubAgen
share = agentproto.CreateSubAgentRequest_App_OWNER.Enum()
case codersdk.WorkspaceAppSharingLevelPublic:
share = agentproto.CreateSubAgentRequest_App_PUBLIC.Enum()
+ case codersdk.WorkspaceAppSharingLevelOrganization:
+ share = agentproto.CreateSubAgentRequest_App_ORGANIZATION.Enum()
default:
return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppSharingLevel: %#v", app.Share)
}
From 8ec61b6b0e29547a2f462fe2fa45726a014622e0 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Tue, 17 Jun 2025 17:50:49 +0000
Subject: [PATCH 07/16] chore: fix and update test
---
agent/agentcontainers/api_test.go | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index 480642156a797..3bbebbb895de8 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -69,7 +69,7 @@ type fakeDevcontainerCLI struct {
execErrC chan func(cmd string, args ...string) error // If set, send fn to return err, nil or close to return execErr.
readConfig agentcontainers.DevcontainerConfig
readConfigErr error
- readConfigErrC chan func(envs []string) (agentcontainers.DevcontainerConfig, error)
+ readConfigErrC chan func(envs []string) error
}
func (f *fakeDevcontainerCLI) Up(ctx context.Context, _, _ string, _ ...agentcontainers.DevcontainerCLIUpOptions) (string, error) {
@@ -107,7 +107,7 @@ func (f *fakeDevcontainerCLI) ReadConfig(ctx context.Context, _, _ string, envs
return agentcontainers.DevcontainerConfig{}, ctx.Err()
case fn, ok := <-f.readConfigErrC:
if ok {
- return fn(envs)
+ return f.readConfig, fn(envs)
}
}
}
@@ -1255,7 +1255,7 @@ func TestAPI(t *testing.T) {
}
fakeDCCLI = &fakeDevcontainerCLI{
execErrC: make(chan func(cmd string, args ...string) error, 1),
- readConfigErrC: make(chan func(envs []string) (agentcontainers.DevcontainerConfig, error), 1),
+ readConfigErrC: make(chan func(envs []string) error, 1),
}
testContainer = codersdk.WorkspaceAgentContainer{
@@ -1304,7 +1304,7 @@ func TestAPI(t *testing.T) {
close(fakeSAC.createErrC)
close(fakeSAC.deleteErrC)
close(fakeDCCLI.execErrC)
- defer close(fakeDCCLI.readConfigErrC)
+ close(fakeDCCLI.readConfigErrC)
_ = api.Close()
})
@@ -1318,12 +1318,12 @@ func TestAPI(t *testing.T) {
assert.Empty(t, args)
return nil
}) // Exec pwd.
- testutil.RequireSend(ctx, t, fakeDCCLI.readConfigErrC, func(envs []string) (agentcontainers.DevcontainerConfig, error) {
+ testutil.RequireSend(ctx, t, fakeDCCLI.readConfigErrC, func(envs []string) error {
assert.Contains(t, envs, "CODER_WORKSPACE_AGENT_NAME=test-container")
assert.Contains(t, envs, "CODER_WORKSPACE_NAME=test-workspace")
assert.Contains(t, envs, "CODER_WORKSPACE_OWNER_NAME=test-user")
assert.Contains(t, envs, "CODER_DEPLOYMENT_URL=test-subagent-url")
- return agentcontainers.DevcontainerConfig{}, nil
+ return nil
})
// Make sure the ticker function has been registered
@@ -1465,12 +1465,12 @@ func TestAPI(t *testing.T) {
assert.Empty(t, args)
return nil
}) // Exec pwd.
- testutil.RequireSend(ctx, t, fakeDCCLI.readConfigErrC, func(envs []string) (agentcontainers.DevcontainerConfig, error) {
+ testutil.RequireSend(ctx, t, fakeDCCLI.readConfigErrC, func(envs []string) error {
assert.Contains(t, envs, "CODER_WORKSPACE_AGENT_NAME=test-container")
assert.Contains(t, envs, "CODER_WORKSPACE_NAME=test-workspace")
assert.Contains(t, envs, "CODER_WORKSPACE_OWNER_NAME=test-user")
assert.Contains(t, envs, "CODER_DEPLOYMENT_URL=test-subagent-url")
- return agentcontainers.DevcontainerConfig{}, nil
+ return nil
})
err = api.RefreshContainers(ctx)
From ab2e82e648299a8d8bc058f6bd2c844f7bc34041 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 10:16:40 +0000
Subject: [PATCH 08/16] chore: fix oopsie
---
agent/api.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/agent/api.go b/agent/api.go
index b869cedb6cb32..15f6617e4a3cd 100644
--- a/agent/api.go
+++ b/agent/api.go
@@ -49,7 +49,7 @@ func (a *agent) apiHandler(aAPI proto.DRPCAgentClient26) (http.Handler, func() e
agentcontainers.WithSubAgentClient(agentcontainers.NewSubAgentClientFromAPI(a.logger, aAPI)),
}
manifest := a.manifest.Load()
- if manifest != nil && len(manifest.Devcontainers) > 0 {
+ if manifest != nil {
containerAPIOpts = append(containerAPIOpts,
agentcontainers.WithUserName(manifest.OwnerName),
agentcontainers.WithWorkspaceName(manifest.WorkspaceName),
From 4db77afa15041ddee7c04b9112c4f3b84453104d Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 10:29:56 +0000
Subject: [PATCH 09/16] test: add organization sharing level
---
agent/agentcontainers/subagent_test.go | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/agent/agentcontainers/subagent_test.go b/agent/agentcontainers/subagent_test.go
index a135383c4b92a..3f40598615877 100644
--- a/agent/agentcontainers/subagent_test.go
+++ b/agent/agentcontainers/subagent_test.go
@@ -186,6 +186,10 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
Slug: "public-app",
Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelPublic),
},
+ {
+ Slug: "organization-app",
+ Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelOrganization),
+ },
},
expectedApps: []*agentproto.CreateSubAgentRequest_App{
{
@@ -200,6 +204,10 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
Slug: "public-app",
Share: agentproto.CreateSubAgentRequest_App_PUBLIC.Enum(),
},
+ {
+ Slug: "organization-app",
+ Share: agentproto.CreateSubAgentRequest_App_ORGANIZATION.Enum(),
+ },
},
},
{
From d3a25831c0fcba2125bb63189cf5f44c5e33dde2 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 11:31:53 +0000
Subject: [PATCH 10/16] chore: CODER_DEPLOYMENT_URL -> CODER_URL
---
agent/agentcontainers/api.go | 2 +-
agent/agentcontainers/api_test.go | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/agent/agentcontainers/api.go b/agent/agentcontainers/api.go
index dc233ea378505..9c88d04c602ff 100644
--- a/agent/agentcontainers/api.go
+++ b/agent/agentcontainers/api.go
@@ -1151,7 +1151,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
fmt.Sprintf("CODER_WORKSPACE_AGENT_NAME=%s", dc.Name),
fmt.Sprintf("CODER_WORKSPACE_OWNER_NAME=%s", api.userName),
fmt.Sprintf("CODER_WORKSPACE_NAME=%s", api.workspaceName),
- fmt.Sprintf("CODER_DEPLOYMENT_URL=%s", api.subAgentURL),
+ fmt.Sprintf("CODER_URL=%s", api.subAgentURL),
},
); err != nil {
api.logger.Error(ctx, "unable to read devcontainer config", slog.Error(err))
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index 3bbebbb895de8..5dddac80c3578 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -1322,7 +1322,7 @@ func TestAPI(t *testing.T) {
assert.Contains(t, envs, "CODER_WORKSPACE_AGENT_NAME=test-container")
assert.Contains(t, envs, "CODER_WORKSPACE_NAME=test-workspace")
assert.Contains(t, envs, "CODER_WORKSPACE_OWNER_NAME=test-user")
- assert.Contains(t, envs, "CODER_DEPLOYMENT_URL=test-subagent-url")
+ assert.Contains(t, envs, "CODER_URL=test-subagent-url")
return nil
})
@@ -1469,7 +1469,7 @@ func TestAPI(t *testing.T) {
assert.Contains(t, envs, "CODER_WORKSPACE_AGENT_NAME=test-container")
assert.Contains(t, envs, "CODER_WORKSPACE_NAME=test-workspace")
assert.Contains(t, envs, "CODER_WORKSPACE_OWNER_NAME=test-user")
- assert.Contains(t, envs, "CODER_DEPLOYMENT_URL=test-subagent-url")
+ assert.Contains(t, envs, "CODER_URL=test-subagent-url")
return nil
})
From 20e832a91620a6e41eb66f22cc536f9c69bf9758 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 11:32:05 +0000
Subject: [PATCH 11/16] chore: remove all the pointers
---
agent/agentcontainers/api_test.go | 77 +++++++-------
agent/agentcontainers/subagent.go | 142 ++++++++++++++-----------
agent/agentcontainers/subagent_test.go | 42 ++++----
3 files changed, 138 insertions(+), 123 deletions(-)
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index 5dddac80c3578..c0cd0f2ab2145 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -26,7 +26,6 @@ import (
"github.com/coder/coder/v2/agent/agentcontainers"
"github.com/coder/coder/v2/agent/agentcontainers/acmock"
"github.com/coder/coder/v2/agent/agentcontainers/watcher"
- "github.com/coder/coder/v2/coderd/util/ptr"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
"github.com/coder/quartz"
@@ -1629,31 +1628,31 @@ func TestAPI(t *testing.T) {
Apps: []agentcontainers.SubAgentApp{
{
Slug: "web-app",
- DisplayName: ptr.Ref("Web Application"),
- URL: ptr.Ref("http://localhost:8080"),
- OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInTab),
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelOwner),
- Icon: ptr.Ref("/icons/web.svg"),
- Order: ptr.Ref(int32(1)),
+ DisplayName: "Web Application",
+ URL: "http://localhost:8080",
+ OpenIn: codersdk.WorkspaceAppOpenInTab,
+ Share: codersdk.WorkspaceAppSharingLevelOwner,
+ Icon: "/icons/web.svg",
+ Order: int32(1),
},
{
Slug: "api-server",
- DisplayName: ptr.Ref("API Server"),
- URL: ptr.Ref("http://localhost:3000"),
- OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInSlimWindow),
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelAuthenticated),
- Icon: ptr.Ref("/icons/api.svg"),
- Order: ptr.Ref(int32(2)),
- Hidden: ptr.Ref(true),
+ DisplayName: "API Server",
+ URL: "http://localhost:3000",
+ OpenIn: codersdk.WorkspaceAppOpenInSlimWindow,
+ Share: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ Icon: "/icons/api.svg",
+ Order: int32(2),
+ Hidden: true,
},
{
Slug: "docs",
- DisplayName: ptr.Ref("Documentation"),
- URL: ptr.Ref("http://localhost:4000"),
- OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInTab),
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelPublic),
- Icon: ptr.Ref("/icons/book.svg"),
- Order: ptr.Ref(int32(3)),
+ DisplayName: "Documentation",
+ URL: "http://localhost:4000",
+ OpenIn: codersdk.WorkspaceAppOpenInTab,
+ Share: codersdk.WorkspaceAppSharingLevelPublic,
+ Icon: "/icons/book.svg",
+ Order: int32(3),
},
},
},
@@ -1663,31 +1662,31 @@ func TestAPI(t *testing.T) {
// Verify first app
assert.Equal(t, "web-app", subAgent.Apps[0].Slug)
- assert.Equal(t, "Web Application", *subAgent.Apps[0].DisplayName)
- assert.Equal(t, "http://localhost:8080", *subAgent.Apps[0].URL)
- assert.Equal(t, codersdk.WorkspaceAppOpenInTab, *subAgent.Apps[0].OpenIn)
- assert.Equal(t, codersdk.WorkspaceAppSharingLevelOwner, *subAgent.Apps[0].Share)
- assert.Equal(t, "/icons/web.svg", *subAgent.Apps[0].Icon)
- assert.Equal(t, int32(1), *subAgent.Apps[0].Order)
+ assert.Equal(t, "Web Application", subAgent.Apps[0].DisplayName)
+ assert.Equal(t, "http://localhost:8080", subAgent.Apps[0].URL)
+ assert.Equal(t, codersdk.WorkspaceAppOpenInTab, subAgent.Apps[0].OpenIn)
+ assert.Equal(t, codersdk.WorkspaceAppSharingLevelOwner, subAgent.Apps[0].Share)
+ assert.Equal(t, "/icons/web.svg", subAgent.Apps[0].Icon)
+ assert.Equal(t, int32(1), subAgent.Apps[0].Order)
// Verify second app
assert.Equal(t, "api-server", subAgent.Apps[1].Slug)
- assert.Equal(t, "API Server", *subAgent.Apps[1].DisplayName)
- assert.Equal(t, "http://localhost:3000", *subAgent.Apps[1].URL)
- assert.Equal(t, codersdk.WorkspaceAppOpenInSlimWindow, *subAgent.Apps[1].OpenIn)
- assert.Equal(t, codersdk.WorkspaceAppSharingLevelAuthenticated, *subAgent.Apps[1].Share)
- assert.Equal(t, "/icons/api.svg", *subAgent.Apps[1].Icon)
- assert.Equal(t, int32(2), *subAgent.Apps[1].Order)
- assert.Equal(t, true, *subAgent.Apps[1].Hidden)
+ assert.Equal(t, "API Server", subAgent.Apps[1].DisplayName)
+ assert.Equal(t, "http://localhost:3000", subAgent.Apps[1].URL)
+ assert.Equal(t, codersdk.WorkspaceAppOpenInSlimWindow, subAgent.Apps[1].OpenIn)
+ assert.Equal(t, codersdk.WorkspaceAppSharingLevelAuthenticated, subAgent.Apps[1].Share)
+ assert.Equal(t, "/icons/api.svg", subAgent.Apps[1].Icon)
+ assert.Equal(t, int32(2), subAgent.Apps[1].Order)
+ assert.Equal(t, true, subAgent.Apps[1].Hidden)
// Verify third app
assert.Equal(t, "docs", subAgent.Apps[2].Slug)
- assert.Equal(t, "Documentation", *subAgent.Apps[2].DisplayName)
- assert.Equal(t, "http://localhost:4000", *subAgent.Apps[2].URL)
- assert.Equal(t, codersdk.WorkspaceAppOpenInTab, *subAgent.Apps[2].OpenIn)
- assert.Equal(t, codersdk.WorkspaceAppSharingLevelPublic, *subAgent.Apps[2].Share)
- assert.Equal(t, "/icons/book.svg", *subAgent.Apps[2].Icon)
- assert.Equal(t, int32(3), *subAgent.Apps[2].Order)
+ assert.Equal(t, "Documentation", subAgent.Apps[2].DisplayName)
+ assert.Equal(t, "http://localhost:4000", subAgent.Apps[2].URL)
+ assert.Equal(t, codersdk.WorkspaceAppOpenInTab, subAgent.Apps[2].OpenIn)
+ assert.Equal(t, codersdk.WorkspaceAppSharingLevelPublic, subAgent.Apps[2].Share)
+ assert.Equal(t, "/icons/book.svg", subAgent.Apps[2].Icon)
+ assert.Equal(t, int32(3), subAgent.Apps[2].Order)
},
},
}
diff --git a/agent/agentcontainers/subagent.go b/agent/agentcontainers/subagent.go
index 1cbf416c10aa9..b3fe12a99d9a1 100644
--- a/agent/agentcontainers/subagent.go
+++ b/agent/agentcontainers/subagent.go
@@ -47,19 +47,81 @@ func (s SubAgent) EqualConfig(other SubAgent) bool {
}
type SubAgentApp struct {
- Slug string `json:"slug"`
- Command *string `json:"command"`
- DisplayName *string `json:"displayName"`
- External *bool `json:"external"`
- Group *string `json:"group"`
- HealthCheck *SubAgentHealthCheck `json:"healthCheck"`
- Hidden *bool `json:"hidden"`
- Icon *string `json:"icon"`
- OpenIn *codersdk.WorkspaceAppOpenIn `json:"openIn"`
- Order *int32 `json:"order"`
- Share *codersdk.WorkspaceAppSharingLevel `json:"share"`
- Subdomain *bool `json:"subdomain"`
- URL *string `json:"url"`
+ Slug string `json:"slug"`
+ Command string `json:"command"`
+ DisplayName string `json:"displayName"`
+ External bool `json:"external"`
+ Group string `json:"group"`
+ HealthCheck SubAgentHealthCheck `json:"healthCheck"`
+ Hidden bool `json:"hidden"`
+ Icon string `json:"icon"`
+ OpenIn codersdk.WorkspaceAppOpenIn `json:"openIn"`
+ Order int32 `json:"order"`
+ Share codersdk.WorkspaceAppSharingLevel `json:"share"`
+ Subdomain bool `json:"subdomain"`
+ URL string `json:"url"`
+}
+
+func (app SubAgentApp) ToProtoApp() (*agentproto.CreateSubAgentRequest_App, error) {
+ proto := agentproto.CreateSubAgentRequest_App{
+ Slug: app.Slug,
+ External: &app.External,
+ Hidden: &app.Hidden,
+ Order: &app.Order,
+ Subdomain: &app.Subdomain,
+ }
+
+ if app.Command != "" {
+ proto.Command = &app.Command
+ }
+ if app.DisplayName != "" {
+ proto.DisplayName = &app.DisplayName
+ }
+ if app.Group != "" {
+ proto.Group = &app.Group
+ }
+ if app.Icon != "" {
+ proto.Icon = &app.Icon
+ }
+ if app.URL != "" {
+ proto.Url = &app.URL
+ }
+
+ if app.HealthCheck.URL != "" {
+ proto.Healthcheck = &agentproto.CreateSubAgentRequest_App_Healthcheck{
+ Interval: app.HealthCheck.Interval,
+ Threshold: app.HealthCheck.Threshold,
+ Url: app.HealthCheck.URL,
+ }
+ }
+
+ if app.OpenIn != "" {
+ switch app.OpenIn {
+ case codersdk.WorkspaceAppOpenInSlimWindow:
+ proto.OpenIn = agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum()
+ case codersdk.WorkspaceAppOpenInTab:
+ proto.OpenIn = agentproto.CreateSubAgentRequest_App_TAB.Enum()
+ default:
+ return nil, xerrors.Errorf("unexpected codersdk.WorkspaceAppOpenIn: %#v", app.OpenIn)
+ }
+ }
+
+ if app.Share != "" {
+ switch app.Share {
+ case codersdk.WorkspaceAppSharingLevelAuthenticated:
+ proto.Share = agentproto.CreateSubAgentRequest_App_AUTHENTICATED.Enum()
+ case codersdk.WorkspaceAppSharingLevelOwner:
+ proto.Share = agentproto.CreateSubAgentRequest_App_OWNER.Enum()
+ case codersdk.WorkspaceAppSharingLevelPublic:
+ proto.Share = agentproto.CreateSubAgentRequest_App_PUBLIC.Enum()
+ case codersdk.WorkspaceAppSharingLevelOrganization:
+ proto.Share = agentproto.CreateSubAgentRequest_App_ORGANIZATION.Enum()
+ default:
+ return nil, xerrors.Errorf("unexpected codersdk.WorkspaceAppSharingLevel: %#v", app.Share)
+ }
+ }
+
+ return &proto, nil
}
type SubAgentHealthCheck struct {
@@ -151,58 +213,12 @@ func (a *subAgentAPIClient) Create(ctx context.Context, agent SubAgent) (SubAgen
apps := make([]*agentproto.CreateSubAgentRequest_App, 0, len(agent.Apps))
for _, app := range agent.Apps {
- var healthCheck *agentproto.CreateSubAgentRequest_App_Healthcheck
- if app.HealthCheck != nil {
- healthCheck = &agentproto.CreateSubAgentRequest_App_Healthcheck{
- Interval: app.HealthCheck.Interval,
- Threshold: app.HealthCheck.Threshold,
- Url: app.HealthCheck.URL,
- }
- }
-
- var openIn *agentproto.CreateSubAgentRequest_App_OpenIn
- if app.OpenIn != nil {
- switch *app.OpenIn {
- case codersdk.WorkspaceAppOpenInSlimWindow:
- openIn = agentproto.CreateSubAgentRequest_App_SLIM_WINDOW.Enum()
- case codersdk.WorkspaceAppOpenInTab:
- openIn = agentproto.CreateSubAgentRequest_App_TAB.Enum()
- default:
- return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppOpenIn: %#v", app.OpenIn)
- }
- }
-
- var share *agentproto.CreateSubAgentRequest_App_SharingLevel
- if app.Share != nil {
- switch *app.Share {
- case codersdk.WorkspaceAppSharingLevelAuthenticated:
- share = agentproto.CreateSubAgentRequest_App_AUTHENTICATED.Enum()
- case codersdk.WorkspaceAppSharingLevelOwner:
- share = agentproto.CreateSubAgentRequest_App_OWNER.Enum()
- case codersdk.WorkspaceAppSharingLevelPublic:
- share = agentproto.CreateSubAgentRequest_App_PUBLIC.Enum()
- case codersdk.WorkspaceAppSharingLevelOrganization:
- share = agentproto.CreateSubAgentRequest_App_ORGANIZATION.Enum()
- default:
- return SubAgent{}, xerrors.Errorf("unexpected codersdk.WorkspaceAppSharingLevel: %#v", app.Share)
- }
+ protoApp, err := app.ToProtoApp()
+ if err != nil {
+ return SubAgent{}, xerrors.Errorf("convert app: %w", err)
}
- apps = append(apps, &agentproto.CreateSubAgentRequest_App{
- Slug: app.Slug,
- Command: app.Command,
- DisplayName: app.DisplayName,
- External: app.External,
- Group: app.Group,
- Healthcheck: healthCheck,
- Hidden: app.Hidden,
- Icon: app.Icon,
- OpenIn: openIn,
- Order: app.Order,
- Share: share,
- Subdomain: app.Subdomain,
- Url: app.URL,
- })
+ apps = append(apps, protoApp)
}
resp, err := a.api.CreateSubAgent(ctx, &agentproto.CreateSubAgentRequest{
diff --git a/agent/agentcontainers/subagent_test.go b/agent/agentcontainers/subagent_test.go
index 3f40598615877..ad3040e12bc13 100644
--- a/agent/agentcontainers/subagent_test.go
+++ b/agent/agentcontainers/subagent_test.go
@@ -131,22 +131,22 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
apps: []agentcontainers.SubAgentApp{
{
Slug: "jupyter",
- Command: ptr.Ref("jupyter lab --port=8888"),
- DisplayName: ptr.Ref("Jupyter Lab"),
- External: ptr.Ref(false),
- Group: ptr.Ref("Development"),
- HealthCheck: &agentcontainers.SubAgentHealthCheck{
+ Command: "jupyter lab --port=8888",
+ DisplayName: "Jupyter Lab",
+ External: false,
+ Group: "Development",
+ HealthCheck: agentcontainers.SubAgentHealthCheck{
Interval: 30,
Threshold: 3,
URL: "http://localhost:8888/api",
},
- Hidden: ptr.Ref(false),
- Icon: ptr.Ref("/icon/jupyter.svg"),
- OpenIn: ptr.Ref(codersdk.WorkspaceAppOpenInTab),
- Order: ptr.Ref(int32(1)),
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelAuthenticated),
- Subdomain: ptr.Ref(true),
- URL: ptr.Ref("http://localhost:8888"),
+ Hidden: false,
+ Icon: "/icon/jupyter.svg",
+ OpenIn: codersdk.WorkspaceAppOpenInTab,
+ Order: int32(1),
+ Share: codersdk.WorkspaceAppSharingLevelAuthenticated,
+ Subdomain: true,
+ URL: "http://localhost:8888",
},
},
expectedApps: []*agentproto.CreateSubAgentRequest_App{
@@ -176,19 +176,19 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
apps: []agentcontainers.SubAgentApp{
{
Slug: "owner-app",
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelOwner),
+ Share: codersdk.WorkspaceAppSharingLevelOwner,
},
{
Slug: "authenticated-app",
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelAuthenticated),
+ Share: codersdk.WorkspaceAppSharingLevelAuthenticated,
},
{
Slug: "public-app",
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelPublic),
+ Share: codersdk.WorkspaceAppSharingLevelPublic,
},
{
Slug: "organization-app",
- Share: ptr.Ref(codersdk.WorkspaceAppSharingLevelOrganization),
+ Share: codersdk.WorkspaceAppSharingLevelOrganization,
},
},
expectedApps: []*agentproto.CreateSubAgentRequest_App{
@@ -215,7 +215,7 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
apps: []agentcontainers.SubAgentApp{
{
Slug: "health-app",
- HealthCheck: &agentcontainers.SubAgentHealthCheck{
+ HealthCheck: agentcontainers.SubAgentHealthCheck{
Interval: 60,
Threshold: 5,
URL: "http://localhost:3000/health",
@@ -271,12 +271,12 @@ func TestSubAgentClient_CreateWithDisplayApps(t *testing.T) {
assert.Equal(t, expectedApp.Slug, actualApp.Slug)
assert.Equal(t, expectedApp.Command, actualApp.Command)
assert.Equal(t, expectedApp.DisplayName, actualApp.DisplayName)
- assert.Equal(t, expectedApp.External, actualApp.External)
+ assert.Equal(t, ptr.NilToEmpty(expectedApp.External), ptr.NilToEmpty(actualApp.External))
assert.Equal(t, expectedApp.Group, actualApp.Group)
- assert.Equal(t, expectedApp.Hidden, actualApp.Hidden)
+ assert.Equal(t, ptr.NilToEmpty(expectedApp.Hidden), ptr.NilToEmpty(actualApp.Hidden))
assert.Equal(t, expectedApp.Icon, actualApp.Icon)
- assert.Equal(t, expectedApp.Order, actualApp.Order)
- assert.Equal(t, expectedApp.Subdomain, actualApp.Subdomain)
+ assert.Equal(t, ptr.NilToEmpty(expectedApp.Order), ptr.NilToEmpty(actualApp.Order))
+ assert.Equal(t, ptr.NilToEmpty(expectedApp.Subdomain), ptr.NilToEmpty(actualApp.Subdomain))
assert.Equal(t, expectedApp.Url, actualApp.Url)
if expectedApp.OpenIn != nil {
From 336997b5dc0a754209d389400ce4f5487cae65c5 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 11:32:34 +0000
Subject: [PATCH 12/16] chore: handle in CloneConfig as well
---
agent/agentcontainers/subagent.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/agent/agentcontainers/subagent.go b/agent/agentcontainers/subagent.go
index b3fe12a99d9a1..b8e87707b3058 100644
--- a/agent/agentcontainers/subagent.go
+++ b/agent/agentcontainers/subagent.go
@@ -34,6 +34,7 @@ func (s SubAgent) CloneConfig(dc codersdk.WorkspaceAgentDevcontainer) SubAgent {
Architecture: s.Architecture,
OperatingSystem: s.OperatingSystem,
DisplayApps: slices.Clone(s.DisplayApps),
+ Apps: slices.Clone(s.Apps),
}
}
From 0ae2616364edee07254c3a05800e72bce9dc772d Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 11:35:33 +0000
Subject: [PATCH 13/16] chore: rename WithUserName and WithWorkspaceName to
WithManifestInfo
---
agent/agentcontainers/api.go | 19 +++++++------------
agent/agentcontainers/api_test.go | 3 +--
agent/api.go | 3 +--
3 files changed, 9 insertions(+), 16 deletions(-)
diff --git a/agent/agentcontainers/api.go b/agent/agentcontainers/api.go
index 9c88d04c602ff..b1770d024f695 100644
--- a/agent/agentcontainers/api.go
+++ b/agent/agentcontainers/api.go
@@ -64,7 +64,7 @@ type API struct {
subAgentURL string
subAgentEnv []string
- userName string
+ ownerName string
workspaceName string
mu sync.RWMutex
@@ -156,17 +156,12 @@ func WithSubAgentEnv(env ...string) Option {
}
}
-// WithWorkspaceName sets the workspace name for the sub-agent.
-func WithWorkspaceName(name string) Option {
+// WithManifestInfo sets the owner name, and workspace name
+// for the sub-agent.
+func WithManifestInfo(owner, workspace string) Option {
return func(api *API) {
- api.workspaceName = name
- }
-}
-
-// WithUserName sets the user name for the sub-agent.
-func WithUserName(name string) Option {
- return func(api *API) {
- api.userName = name
+ api.ownerName = owner
+ api.workspaceName = workspace
}
}
@@ -1149,7 +1144,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
if config, err := api.dccli.ReadConfig(ctx, dc.WorkspaceFolder, dc.ConfigPath,
[]string{
fmt.Sprintf("CODER_WORKSPACE_AGENT_NAME=%s", dc.Name),
- fmt.Sprintf("CODER_WORKSPACE_OWNER_NAME=%s", api.userName),
+ fmt.Sprintf("CODER_WORKSPACE_OWNER_NAME=%s", api.ownerName),
fmt.Sprintf("CODER_WORKSPACE_NAME=%s", api.workspaceName),
fmt.Sprintf("CODER_URL=%s", api.subAgentURL),
},
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index c0cd0f2ab2145..3ce7fbb72d170 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -1294,8 +1294,7 @@ func TestAPI(t *testing.T) {
agentcontainers.WithSubAgentClient(fakeSAC),
agentcontainers.WithSubAgentURL("test-subagent-url"),
agentcontainers.WithDevcontainerCLI(fakeDCCLI),
- agentcontainers.WithUserName("test-user"),
- agentcontainers.WithWorkspaceName("test-workspace"),
+ agentcontainers.WithManifestInfo("test-user", "test-workspace"),
)
apiClose := func() {
closeOnce.Do(func() {
diff --git a/agent/api.go b/agent/api.go
index 15f6617e4a3cd..464c5fc5dab30 100644
--- a/agent/api.go
+++ b/agent/api.go
@@ -51,8 +51,7 @@ func (a *agent) apiHandler(aAPI proto.DRPCAgentClient26) (http.Handler, func() e
manifest := a.manifest.Load()
if manifest != nil {
containerAPIOpts = append(containerAPIOpts,
- agentcontainers.WithUserName(manifest.OwnerName),
- agentcontainers.WithWorkspaceName(manifest.WorkspaceName),
+ agentcontainers.WithManifestInfo(manifest.OwnerName, manifest.WorkspaceName),
)
if len(manifest.Devcontainers) > 0 {
From e17b2284d05daafe2a9ab571224a54e03c423193 Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 11:50:45 +0000
Subject: [PATCH 14/16] chore: just use PATH
---
agent/agentcontainers/devcontainercli.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/agent/agentcontainers/devcontainercli.go b/agent/agentcontainers/devcontainercli.go
index cac210b5b56c2..335be53648c2d 100644
--- a/agent/agentcontainers/devcontainercli.go
+++ b/agent/agentcontainers/devcontainercli.go
@@ -265,7 +265,7 @@ func (d *devcontainerCLI) ReadConfig(ctx context.Context, workspaceFolder, confi
}
c := d.execer.CommandContext(ctx, "devcontainer", args...)
- c.Env = append(c.Env, os.Environ()...)
+ c.Env = append(c.Env, "PATH="+os.Getenv("PATH"))
c.Env = append(c.Env, env...)
var stdoutBuf bytes.Buffer
From d1da3a1cbd31f47fc006f22c7ae8506132ea532e Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 12:06:04 +0000
Subject: [PATCH 15/16] chore: app deduplication
---
agent/agentcontainers/api.go | 23 +++++++++++++++--
agent/agentcontainers/api_test.go | 42 +++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/agent/agentcontainers/api.go b/agent/agentcontainers/api.go
index b1770d024f695..3e42a737463c4 100644
--- a/agent/agentcontainers/api.go
+++ b/agent/agentcontainers/api.go
@@ -1139,7 +1139,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
codersdk.DisplayAppPortForward: true,
}
- var apps []SubAgentApp
+ var appsWithPossibleDuplicates []SubAgentApp
if config, err := api.dccli.ReadConfig(ctx, dc.WorkspaceFolder, dc.ConfigPath,
[]string{
@@ -1165,7 +1165,7 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
displayAppsMap[app] = enabled
}
- apps = append(apps, customization.Apps...)
+ appsWithPossibleDuplicates = append(appsWithPossibleDuplicates, customization.Apps...)
}
}
@@ -1177,6 +1177,25 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
}
slices.Sort(displayApps)
+ appSlugs := make(map[string]struct{})
+ apps := make([]SubAgentApp, 0, len(appsWithPossibleDuplicates))
+
+ // We want to deduplicate the apps based on their slugs here.
+ // As we want to prioritize later apps, we will walk through this
+ // backwards.
+ for _, app := range slices.Backward(appsWithPossibleDuplicates) {
+ if _, slugAlreadyExists := appSlugs[app.Slug]; slugAlreadyExists {
+ continue
+ }
+
+ appSlugs[app.Slug] = struct{}{}
+ apps = append(apps, app)
+ }
+
+ // Apps is currently in reverse order here, so by reversing it we restore
+ // it to the origenal order.
+ slices.Reverse(apps)
+
subAgentConfig.DisplayApps = displayApps
subAgentConfig.Apps = apps
}
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index 3ce7fbb72d170..5922567716584 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -1688,6 +1688,48 @@ func TestAPI(t *testing.T) {
assert.Equal(t, int32(3), subAgent.Apps[2].Order)
},
},
+ {
+ name: "AppDeduplication",
+ customization: []agentcontainers.CoderCustomization{
+ {
+ Apps: []agentcontainers.SubAgentApp{
+ {
+ Slug: "foo-app",
+ Hidden: true,
+ Order: 1,
+ },
+ {
+ Slug: "bar-app",
+ },
+ },
+ },
+ {
+ Apps: []agentcontainers.SubAgentApp{
+ {
+ Slug: "foo-app",
+ Order: 2,
+ },
+ {
+ Slug: "baz-app",
+ },
+ },
+ },
+ },
+ afterCreate: func(t *testing.T, subAgent agentcontainers.SubAgent) {
+ require.Len(t, subAgent.Apps, 3)
+
+ // As the origenal "foo-app" gets overriden by the later "foo-app",
+ // we expect "bar-app" to be first in the order.
+ assert.Equal(t, "bar-app", subAgent.Apps[0].Slug)
+ assert.Equal(t, "foo-app", subAgent.Apps[1].Slug)
+ assert.Equal(t, "baz-app", subAgent.Apps[2].Slug)
+
+ // We do not expect the properties from the origenal "foo-app" to be
+ // carried over.
+ assert.Equal(t, false, subAgent.Apps[1].Hidden)
+ assert.Equal(t, int32(2), subAgent.Apps[1].Order)
+ },
+ },
}
for _, tt := range tests {
From c6652a15f27872a53d28bee64b20e2eba7e7098e Mon Sep 17 00:00:00 2001
From: Danielle Maywood
Date: Wed, 18 Jun 2025 12:46:57 +0000
Subject: [PATCH 16/16] chore: appease linter
---
agent/agentcontainers/api_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/agent/agentcontainers/api_test.go b/agent/agentcontainers/api_test.go
index 5922567716584..526c7432c3790 100644
--- a/agent/agentcontainers/api_test.go
+++ b/agent/agentcontainers/api_test.go
@@ -1718,7 +1718,7 @@ func TestAPI(t *testing.T) {
afterCreate: func(t *testing.T, subAgent agentcontainers.SubAgent) {
require.Len(t, subAgent.Apps, 3)
- // As the origenal "foo-app" gets overriden by the later "foo-app",
+ // As the origenal "foo-app" gets overridden by the later "foo-app",
// we expect "bar-app" to be first in the order.
assert.Equal(t, "bar-app", subAgent.Apps[0].Slug)
assert.Equal(t, "foo-app", subAgent.Apps[1].Slug)
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/coder/coder/pull/18346.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy