Skip to content

Commit 9ccaf86

Browse files
authored
fix(agent/agentcontainers): always derive devcontainer name from workspace folder (#18666)
1 parent 715c7b0 commit 9ccaf86

File tree

2 files changed

+100
-7
lines changed

2 files changed

+100
-7
lines changed

agent/agentcontainers/api.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,25 @@ func WithDevcontainers(devcontainers []codersdk.WorkspaceAgentDevcontainer, scri
211211
if dc.Status == "" {
212212
dc.Status = codersdk.WorkspaceAgentDevcontainerStatusStarting
213213
}
214+
logger := api.logger.With(
215+
slog.F("devcontainer_id", dc.ID),
216+
slog.F("devcontainer_name", dc.Name),
217+
slog.F("workspace_folder", dc.WorkspaceFolder),
218+
slog.F("config_path", dc.ConfigPath),
219+
)
220+
221+
// Devcontainers have a name originating from Terraform, but
222+
// we need to ensure that the name is unique. We will use
223+
// the workspace folder name to generate a unique agent name,
224+
// and if that fails, we will fall back to the devcontainers
225+
// original name.
226+
name, usingWorkspaceFolder := api.makeAgentName(dc.WorkspaceFolder, dc.Name)
227+
if name != dc.Name {
228+
logger = logger.With(slog.F("devcontainer_name", name))
229+
logger.Debug(api.ctx, "updating devcontainer name", slog.F("devcontainer_old_name", dc.Name))
230+
dc.Name = name
231+
api.usingWorkspaceFolderName[dc.WorkspaceFolder] = usingWorkspaceFolder
232+
}
214233

215234
api.knownDevcontainers[dc.WorkspaceFolder] = dc
216235
api.devcontainerNames[dc.Name] = true
@@ -223,12 +242,7 @@ func WithDevcontainers(devcontainers []codersdk.WorkspaceAgentDevcontainer, scri
223242
}
224243
}
225244
if api.devcontainerLogSourceIDs[dc.WorkspaceFolder] == uuid.Nil {
226-
api.logger.Error(api.ctx, "devcontainer log source ID not found for devcontainer",
227-
slog.F("devcontainer_id", dc.ID),
228-
slog.F("devcontainer_name", dc.Name),
229-
slog.F("workspace_folder", dc.WorkspaceFolder),
230-
slog.F("config_path", dc.ConfigPath),
231-
)
245+
logger.Error(api.ctx, "devcontainer log source ID not found for devcontainer")
232246
}
233247
}
234248
}
@@ -872,7 +886,7 @@ func (api *API) getContainers() (codersdk.WorkspaceAgentListContainersResponse,
872886
devcontainers = append(devcontainers, dc)
873887
}
874888
slices.SortFunc(devcontainers, func(a, b codersdk.WorkspaceAgentDevcontainer) int {
875-
return strings.Compare(a.Name, b.Name)
889+
return strings.Compare(a.WorkspaceFolder, b.WorkspaceFolder)
876890
})
877891
}
878892

agent/agentcontainers/api_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2596,3 +2596,82 @@ func fakeContainer(t *testing.T, mut ...func(*codersdk.WorkspaceAgentContainer))
25962596
}
25972597
return ct
25982598
}
2599+
2600+
func TestWithDevcontainersNameGeneration(t *testing.T) {
2601+
t.Parallel()
2602+
2603+
if runtime.GOOS == "windows" {
2604+
t.Skip("Dev Container tests are not supported on Windows")
2605+
}
2606+
2607+
devcontainers := []codersdk.WorkspaceAgentDevcontainer{
2608+
{
2609+
ID: uuid.New(),
2610+
Name: "original-name",
2611+
WorkspaceFolder: "/home/coder/foo/project",
2612+
ConfigPath: "/home/coder/foo/project/.devcontainer/devcontainer.json",
2613+
},
2614+
{
2615+
ID: uuid.New(),
2616+
Name: "another-name",
2617+
WorkspaceFolder: "/home/coder/bar/project",
2618+
ConfigPath: "/home/coder/bar/project/.devcontainer/devcontainer.json",
2619+
},
2620+
}
2621+
2622+
scripts := []codersdk.WorkspaceAgentScript{
2623+
{ID: devcontainers[0].ID, LogSourceID: uuid.New()},
2624+
{ID: devcontainers[1].ID, LogSourceID: uuid.New()},
2625+
}
2626+
2627+
logger := testutil.Logger(t)
2628+
2629+
// This should trigger the WithDevcontainers code path where names are generated
2630+
api := agentcontainers.NewAPI(logger,
2631+
agentcontainers.WithDevcontainers(devcontainers, scripts),
2632+
agentcontainers.WithContainerCLI(&fakeContainerCLI{
2633+
containers: codersdk.WorkspaceAgentListContainersResponse{
2634+
Containers: []codersdk.WorkspaceAgentContainer{
2635+
fakeContainer(t, func(c *codersdk.WorkspaceAgentContainer) {
2636+
c.ID = "some-container-id-1"
2637+
c.FriendlyName = "container-name-1"
2638+
c.Labels[agentcontainers.DevcontainerLocalFolderLabel] = "/home/coder/baz/project"
2639+
c.Labels[agentcontainers.DevcontainerConfigFileLabel] = "/home/coder/baz/project/.devcontainer/devcontainer.json"
2640+
}),
2641+
},
2642+
},
2643+
}),
2644+
agentcontainers.WithDevcontainerCLI(&fakeDevcontainerCLI{}),
2645+
agentcontainers.WithSubAgentClient(&fakeSubAgentClient{}),
2646+
agentcontainers.WithWatcher(watcher.NewNoop()),
2647+
)
2648+
defer api.Close()
2649+
api.Start()
2650+
2651+
r := chi.NewRouter()
2652+
r.Mount("/", api.Routes())
2653+
2654+
ctx := context.Background()
2655+
2656+
err := api.RefreshContainers(ctx)
2657+
require.NoError(t, err, "RefreshContainers should not error")
2658+
2659+
// Initial request returns the initial data.
2660+
req := httptest.NewRequest(http.MethodGet, "/", nil).
2661+
WithContext(ctx)
2662+
rec := httptest.NewRecorder()
2663+
r.ServeHTTP(rec, req)
2664+
2665+
require.Equal(t, http.StatusOK, rec.Code)
2666+
var response codersdk.WorkspaceAgentListContainersResponse
2667+
err = json.NewDecoder(rec.Body).Decode(&response)
2668+
require.NoError(t, err)
2669+
2670+
// Verify the devcontainers have the expected names.
2671+
require.Len(t, response.Devcontainers, 3, "should have two devcontainers")
2672+
assert.NotEqual(t, "original-name", response.Devcontainers[2].Name, "first devcontainer should not keep original name")
2673+
assert.Equal(t, "project", response.Devcontainers[2].Name, "first devcontainer should use the project folder name")
2674+
assert.NotEqual(t, "another-name", response.Devcontainers[0].Name, "second devcontainer should not keep original name")
2675+
assert.Equal(t, "bar-project", response.Devcontainers[0].Name, "second devcontainer should has a collision and uses the folder name with a prefix")
2676+
assert.Equal(t, "baz-project", response.Devcontainers[1].Name, "third devcontainer should use the folder name with a prefix since it collides with the first two")
2677+
}

0 commit comments

Comments
 (0)