Skip to content

Commit 9afa5ea

Browse files
committed
add WithContainerLabelIncludeFilter
1 parent 591a9bf commit 9afa5ea

File tree

1 file changed

+67
-39
lines changed

1 file changed

+67
-39
lines changed

agent/agentcontainers/api.go

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,23 @@ const (
4242
// API is responsible for container-related operations in the agent.
4343
// It provides methods to list and manage containers.
4444
type API struct {
45-
ctx context.Context
46-
cancel context.CancelFunc
47-
watcherDone chan struct{}
48-
updaterDone chan struct{}
49-
initialUpdateDone chan struct{} // Closed after first update in updaterLoop.
50-
updateTrigger chan chan error // Channel to trigger manual refresh.
51-
updateInterval time.Duration // Interval for periodic container updates.
52-
logger slog.Logger
53-
watcher watcher.Watcher
54-
execer agentexec.Execer
55-
ccli ContainerCLI
56-
dccli DevcontainerCLI
57-
clock quartz.Clock
58-
scriptLogger func(logSourceID uuid.UUID) ScriptLogger
59-
subAgentClient SubAgentClient
60-
subAgentURL string
45+
ctx context.Context
46+
cancel context.CancelFunc
47+
watcherDone chan struct{}
48+
updaterDone chan struct{}
49+
initialUpdateDone chan struct{} // Closed after first update in updaterLoop.
50+
updateTrigger chan chan error // Channel to trigger manual refresh.
51+
updateInterval time.Duration // Interval for periodic container updates.
52+
logger slog.Logger
53+
watcher watcher.Watcher
54+
execer agentexec.Execer
55+
ccli ContainerCLI
56+
containerLabelIncludeFilter map[string]string // Labels to filter containers by.
57+
dccli DevcontainerCLI
58+
clock quartz.Clock
59+
scriptLogger func(logSourceID uuid.UUID) ScriptLogger
60+
subAgentClient SubAgentClient
61+
subAgentURL string
6162

6263
mu sync.RWMutex
6364
closed bool
@@ -106,6 +107,16 @@ func WithContainerCLI(ccli ContainerCLI) Option {
106107
}
107108
}
108109

110+
// WithContainerLabelIncludeFilter sets a label filter for containers.
111+
// This option can be given multiple times to filter by multiple labels.
112+
// The behavior is such that only containers matching one or more of the
113+
// provided labels will be included.
114+
func WithContainerLabelIncludeFilter(label, value string) Option {
115+
return func(api *API) {
116+
api.containerLabelIncludeFilter[label] = value
117+
}
118+
}
119+
109120
// WithDevcontainerCLI sets the DevcontainerCLI implementation to use.
110121
// This can be used in tests to modify @devcontainer/cli behavior.
111122
func WithDevcontainerCLI(dccli DevcontainerCLI) Option {
@@ -198,24 +209,25 @@ func WithScriptLogger(scriptLogger func(logSourceID uuid.UUID) ScriptLogger) Opt
198209
func NewAPI(logger slog.Logger, options ...Option) *API {
199210
ctx, cancel := context.WithCancel(context.Background())
200211
api := &API{
201-
ctx: ctx,
202-
cancel: cancel,
203-
watcherDone: make(chan struct{}),
204-
updaterDone: make(chan struct{}),
205-
initialUpdateDone: make(chan struct{}),
206-
updateTrigger: make(chan chan error),
207-
updateInterval: defaultUpdateInterval,
208-
logger: logger,
209-
clock: quartz.NewReal(),
210-
execer: agentexec.DefaultExecer,
211-
subAgentClient: noopSubAgentClient{},
212-
devcontainerNames: make(map[string]bool),
213-
knownDevcontainers: make(map[string]codersdk.WorkspaceAgentDevcontainer),
214-
configFileModifiedTimes: make(map[string]time.Time),
215-
recreateSuccessTimes: make(map[string]time.Time),
216-
recreateErrorTimes: make(map[string]time.Time),
217-
scriptLogger: func(uuid.UUID) ScriptLogger { return noopScriptLogger{} },
218-
injectedSubAgentProcs: make(map[string]subAgentProcess),
212+
ctx: ctx,
213+
cancel: cancel,
214+
watcherDone: make(chan struct{}),
215+
updaterDone: make(chan struct{}),
216+
initialUpdateDone: make(chan struct{}),
217+
updateTrigger: make(chan chan error),
218+
updateInterval: defaultUpdateInterval,
219+
logger: logger,
220+
clock: quartz.NewReal(),
221+
execer: agentexec.DefaultExecer,
222+
subAgentClient: noopSubAgentClient{},
223+
containerLabelIncludeFilter: make(map[string]string),
224+
devcontainerNames: make(map[string]bool),
225+
knownDevcontainers: make(map[string]codersdk.WorkspaceAgentDevcontainer),
226+
configFileModifiedTimes: make(map[string]time.Time),
227+
recreateSuccessTimes: make(map[string]time.Time),
228+
recreateErrorTimes: make(map[string]time.Time),
229+
scriptLogger: func(uuid.UUID) ScriptLogger { return noopScriptLogger{} },
230+
injectedSubAgentProcs: make(map[string]subAgentProcess),
219231
}
220232
// The ctx and logger must be set before applying options to avoid
221233
// nil pointer dereference.
@@ -266,7 +278,7 @@ func (api *API) watcherLoop() {
266278
continue
267279
}
268280

269-
now := api.clock.Now("watcherLoop")
281+
now := api.clock.Now("agentcontainers", "watcherLoop")
270282
switch {
271283
case event.Has(fsnotify.Create | fsnotify.Write):
272284
api.logger.Debug(api.ctx, "devcontainer config file changed", slog.F("file", event.Name))
@@ -333,9 +345,9 @@ func (api *API) updaterLoop() {
333345
}
334346

335347
return nil // Always nil to keep the ticker going.
336-
}, "updaterLoop")
348+
}, "agentcontainers", "updaterLoop")
337349
defer func() {
338-
if err := ticker.Wait("updaterLoop"); err != nil && !errors.Is(err, context.Canceled) {
350+
if err := ticker.Wait("agentcontainers", "updaterLoop"); err != nil && !errors.Is(err, context.Canceled) {
339351
api.logger.Error(api.ctx, "updater loop ticker failed", slog.Error(err))
340352
}
341353
}()
@@ -481,6 +493,22 @@ func (api *API) processUpdatedContainersLocked(ctx context.Context, updated code
481493
slog.F("config_file", configFile),
482494
)
483495

496+
if len(api.containerLabelIncludeFilter) > 0 {
497+
var ok bool
498+
for label, value := range api.containerLabelIncludeFilter {
499+
if v, found := container.Labels[label]; found && v == value {
500+
ok = true
501+
}
502+
}
503+
// Verbose debug logging is fine here since typically filters
504+
// are only used in development or testing environments.
505+
if !ok {
506+
logger.Debug(ctx, "container does not match include filter, ignoring dev container", slog.F("container_labels", container.Labels), slog.F("include_filter", api.containerLabelIncludeFilter))
507+
continue
508+
}
509+
logger.Debug(ctx, "container matches include filter, processing dev container", slog.F("container_labels", container.Labels), slog.F("include_filter", api.containerLabelIncludeFilter))
510+
}
511+
484512
if dc, ok := api.knownDevcontainers[workspaceFolder]; ok {
485513
// If no config path is set, this devcontainer was defined
486514
// in Terraform without the optional config file. Assume the
@@ -781,7 +809,7 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
781809
dc.Container.DevcontainerStatus = dc.Status
782810
}
783811
api.knownDevcontainers[dc.WorkspaceFolder] = dc
784-
api.recreateErrorTimes[dc.WorkspaceFolder] = api.clock.Now("recreate", "errorTimes")
812+
api.recreateErrorTimes[dc.WorkspaceFolder] = api.clock.Now("agentcontainers", "recreate", "errorTimes")
785813
api.mu.Unlock()
786814
return
787815
}
@@ -803,7 +831,7 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
803831
dc.Container.DevcontainerStatus = dc.Status
804832
}
805833
dc.Dirty = false
806-
api.recreateSuccessTimes[dc.WorkspaceFolder] = api.clock.Now("recreate", "successTimes")
834+
api.recreateSuccessTimes[dc.WorkspaceFolder] = api.clock.Now("agentcontainers", "recreate", "successTimes")
807835
api.knownDevcontainers[dc.WorkspaceFolder] = dc
808836
api.mu.Unlock()
809837

0 commit comments

Comments
 (0)