Skip to content

Commit 98e2ec4

Browse files
authored
feat: show devcontainer dirty status and allow recreate (#17880)
Updates #16424
1 parent c775ea8 commit 98e2ec4

File tree

15 files changed

+598
-198
lines changed

15 files changed

+598
-198
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: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,15 @@ func WithClock(clock quartz.Clock) Option {
6969
}
7070
}
7171

72+
// WithCacheDuration sets the cache duration for the API.
73+
// This is used to control how often the API refreshes the list of
74+
// containers. The default is 10 seconds.
75+
func WithCacheDuration(d time.Duration) Option {
76+
return func(api *API) {
77+
api.cacheDuration = d
78+
}
79+
}
80+
7281
// WithExecer sets the agentexec.Execer implementation to use.
7382
func WithExecer(execer agentexec.Execer) Option {
7483
return func(api *API) {
@@ -336,14 +345,29 @@ func (api *API) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListC
336345
}
337346

338347
// Check if the container is running and update the known devcontainers.
339-
for _, container := range updated.Containers {
348+
for i := range updated.Containers {
349+
container := &updated.Containers[i]
340350
workspaceFolder := container.Labels[DevcontainerLocalFolderLabel]
341351
configFile := container.Labels[DevcontainerConfigFileLabel]
342352

343353
if workspaceFolder == "" {
344354
continue
345355
}
346356

357+
container.DevcontainerDirty = dirtyStates[workspaceFolder]
358+
if container.DevcontainerDirty {
359+
lastModified, hasModTime := api.configFileModifiedTimes[configFile]
360+
if hasModTime && container.CreatedAt.After(lastModified) {
361+
api.logger.Info(ctx, "new container created after config modification, not marking as dirty",
362+
slog.F("container", container.ID),
363+
slog.F("created_at", container.CreatedAt),
364+
slog.F("config_modified_at", lastModified),
365+
slog.F("file", configFile),
366+
)
367+
container.DevcontainerDirty = false
368+
}
369+
}
370+
347371
// Check if this is already in our known list.
348372
if knownIndex := slices.IndexFunc(api.knownDevcontainers, func(dc codersdk.WorkspaceAgentDevcontainer) bool {
349373
return dc.WorkspaceFolder == workspaceFolder
@@ -356,7 +380,7 @@ func (api *API) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListC
356380
}
357381
}
358382
api.knownDevcontainers[knownIndex].Running = container.Running
359-
api.knownDevcontainers[knownIndex].Container = &container
383+
api.knownDevcontainers[knownIndex].Container = container
360384

361385
// Check if this container was created after the config
362386
// file was modified.
@@ -395,28 +419,14 @@ func (api *API) getContainers(ctx context.Context) (codersdk.WorkspaceAgentListC
395419
}
396420
}
397421

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-
412422
api.knownDevcontainers = append(api.knownDevcontainers, codersdk.WorkspaceAgentDevcontainer{
413423
ID: uuid.New(),
414424
Name: name,
415425
WorkspaceFolder: workspaceFolder,
416426
ConfigPath: configFile,
417427
Running: container.Running,
418-
Dirty: dirty,
419-
Container: &container,
428+
Dirty: container.DevcontainerDirty,
429+
Container: container,
420430
})
421431
}
422432

@@ -510,6 +520,13 @@ func (api *API) handleDevcontainerRecreate(w http.ResponseWriter, r *http.Reques
510520
slog.F("name", api.knownDevcontainers[i].Name),
511521
)
512522
api.knownDevcontainers[i].Dirty = false
523+
// TODO(mafredri): This should be handled by a service that
524+
// updates the devcontainer state periodically and on-demand.
525+
api.knownDevcontainers[i].Container = nil
526+
// Set the modified time to the zero value to indicate that
527+
// the containers list must be refreshed. This will see to
528+
// it that the new container is re-assigned.
529+
api.mtime = time.Time{}
513530
}
514531
return
515532
}
@@ -579,6 +596,9 @@ func (api *API) markDevcontainerDirty(configPath string, modifiedAt time.Time) {
579596
slog.F("modified_at", modifiedAt),
580597
)
581598
api.knownDevcontainers[i].Dirty = true
599+
if api.knownDevcontainers[i].Container != nil {
600+
api.knownDevcontainers[i].Container.DevcontainerDirty = true
601+
}
582602
}
583603
}
584604
})

agent/agentcontainers/api_internal_test.go

Lines changed: 0 additions & 163 deletions
This file was deleted.

0 commit comments

Comments
 (0)