Skip to content

Commit 410241d

Browse files
committed
feat: show devcontainer dirty status and allow recreate
Updates #16424
1 parent c7917ea commit 410241d

File tree

14 files changed

+425
-35
lines changed

14 files changed

+425
-35
lines changed

agent/agentcontainers/acmock/acmock.go

Lines changed: 47 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

agent/agentcontainers/acmock/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
// Package acmock contains a mock implementation of agentcontainers.Lister for use in tests.
22
package acmock
33

4-
//go:generate mockgen -destination ./acmock.go -package acmock .. Lister
4+
//go:generate mockgen -destination ./acmock.go -package acmock .. Lister,DevcontainerCLI

agent/agentcontainers/api.go

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,29 @@ func (api *API) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListC
336336
}
337337

338338
// Check if the container is running and update the known devcontainers.
339-
for _, container := range updated.Containers {
339+
for i := range updated.Containers {
340+
container := &updated.Containers[i]
340341
workspaceFolder := container.Labels[DevcontainerLocalFolderLabel]
341342
configFile := container.Labels[DevcontainerConfigFileLabel]
342343

343344
if workspaceFolder == "" {
344345
continue
345346
}
346347

348+
container.DevcontainerDirty = dirtyStates[workspaceFolder]
349+
if container.DevcontainerDirty {
350+
lastModified, hasModTime := api.configFileModifiedTimes[configFile]
351+
if hasModTime && container.CreatedAt.After(lastModified) {
352+
api.logger.Info(ctx, "new container created after config modification, not marking as dirty",
353+
slog.F("container", container.ID),
354+
slog.F("created_at", container.CreatedAt),
355+
slog.F("config_modified_at", lastModified),
356+
slog.F("file", configFile),
357+
)
358+
container.DevcontainerDirty = false
359+
}
360+
}
361+
347362
// Check if this is already in our known list.
348363
if knownIndex := slices.IndexFunc(api.knownDevcontainers, func(dc codersdk.WorkspaceAgentDevcontainer) bool {
349364
return dc.WorkspaceFolder == workspaceFolder
@@ -356,7 +371,7 @@ func (api *API) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListC
356371
}
357372
}
358373
api.knownDevcontainers[knownIndex].Running = container.Running
359-
api.knownDevcontainers[knownIndex].Container = &container
374+
api.knownDevcontainers[knownIndex].Container = container
360375

361376
// Check if this container was created after the config
362377
// file was modified.
@@ -395,28 +410,14 @@ func (api *API) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListC
395410
}
396411
}
397412

398-
dirty := dirtyStates[workspaceFolder]
399-
if dirty {
400-
lastModified, hasModTime := api.configFileModifiedTimes[configFile]
401-
if hasModTime && container.CreatedAt.After(lastModified) {
402-
api.logger.Info(ctx, "new container created after config modification, not marking as dirty",
403-
slog.F("container", container.ID),
404-
slog.F("created_at", container.CreatedAt),
405-
slog.F("config_modified_at", lastModified),
406-
slog.F("file", configFile),
407-
)
408-
dirty = false
409-
}
410-
}
411-
412413
api.knownDevcontainers = append(api.knownDevcontainers, codersdk.WorkspaceAgentDevcontainer{
413414
ID: uuid.New(),
414415
Name: name,
415416
WorkspaceFolder: workspaceFolder,
416417
ConfigPath: configFile,
417418
Running: container.Running,
418-
Dirty: dirty,
419-
Container: &container,
419+
Dirty: container.DevcontainerDirty,
420+
Container: container,
420421
})
421422
}
422423

@@ -510,6 +511,7 @@ func (api *API) handleDevcontainerRecreate(w http.ResponseWriter, r *http.Reques
510511
slog.F("name", api.knownDevcontainers[i].Name),
511512
)
512513
api.knownDevcontainers[i].Dirty = false
514+
api.knownDevcontainers[i].Container = nil
513515
}
514516
return
515517
}
@@ -579,6 +581,9 @@ func (api *API) markDevcontainerDirty(configPath string, modifiedAt time.Time) {
579581
slog.F("modified_at", modifiedAt),
580582
)
581583
api.knownDevcontainers[i].Dirty = true
584+
if api.knownDevcontainers[i].Container != nil {
585+
api.knownDevcontainers[i].Container.DevcontainerDirty = true
586+
}
582587
}
583588
}
584589
})

agent/agentcontainers/api_test.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,6 +660,9 @@ func TestAPI(t *testing.T) {
660660
require.NoError(t, err)
661661
require.Len(t, response.Devcontainers, 1)
662662
assert.False(t, response.Devcontainers[0].Dirty,
663+
"devcontainer should not be marked as dirty initially")
664+
require.NotNil(t, response.Devcontainers[0].Container, "container should not be nil")
665+
assert.False(t, response.Devcontainers[0].Container.DevcontainerDirty,
663666
"container should not be marked as dirty initially")
664667

665668
// Verify the watcher is watching the config file.
@@ -689,6 +692,9 @@ func TestAPI(t *testing.T) {
689692
require.Len(t, response.Devcontainers, 1)
690693
assert.True(t, response.Devcontainers[0].Dirty,
691694
"container should be marked as dirty after config file was modified")
695+
require.NotNil(t, response.Devcontainers[0].Container, "container should not be nil")
696+
assert.True(t, response.Devcontainers[0].Container.DevcontainerDirty,
697+
"container should be marked as dirty after config file was modified")
692698

693699
mClock.Advance(time.Minute).MustWait(ctx)
694700

@@ -707,7 +713,10 @@ func TestAPI(t *testing.T) {
707713
require.NoError(t, err)
708714
require.Len(t, response.Devcontainers, 1)
709715
assert.False(t, response.Devcontainers[0].Dirty,
710-
"dirty flag should be cleared after container recreation")
716+
"dirty flag should be cleared on the devcontainer after container recreation")
717+
require.NotNil(t, response.Devcontainers[0].Container, "container should not be nil")
718+
assert.False(t, response.Devcontainers[0].Container.DevcontainerDirty,
719+
"dirty flag should be cleared on the container after container recreation")
711720
})
712721
}
713722

coderd/apidoc/docs.go

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,7 @@ func New(options *Options) *API {
13261326
r.Get("/listening-ports", api.workspaceAgentListeningPorts)
13271327
r.Get("/connection", api.workspaceAgentConnection)
13281328
r.Get("/containers", api.workspaceAgentListContainers)
1329+
r.Post("/containers/devcontainers/container/{container}/recreate", api.workspaceAgentRecreateDevcontainer)
13291330
r.Get("/coordinate", api.workspaceAgentClientCoordinate)
13301331

13311332
// PTY is part of workspaceAppServer.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy