@@ -39,6 +39,7 @@ type API struct {
39
39
watcher watcher.Watcher
40
40
41
41
cacheDuration time.Duration
42
+ execer agentexec.Execer
42
43
cl Lister
43
44
dccli DevcontainerCLI
44
45
clock quartz.Clock
@@ -56,14 +57,6 @@ type API struct {
56
57
// Option is a functional option for API.
57
58
type Option func (* API )
58
59
59
- // WithLister sets the agentcontainers.Lister implementation to use.
60
- // The default implementation uses the Docker CLI to list containers.
61
- func WithLister (cl Lister ) Option {
62
- return func (api * API ) {
63
- api .cl = cl
64
- }
65
- }
66
-
67
60
// WithClock sets the quartz.Clock implementation to use.
68
61
// This is primarily used for testing to control time.
69
62
func WithClock (clock quartz.Clock ) Option {
@@ -72,6 +65,21 @@ func WithClock(clock quartz.Clock) Option {
72
65
}
73
66
}
74
67
68
+ // WithExecer sets the agentexec.Execer implementation to use.
69
+ func WithExecer (execer agentexec.Execer ) Option {
70
+ return func (api * API ) {
71
+ api .execer = execer
72
+ }
73
+ }
74
+
75
+ // WithLister sets the agentcontainers.Lister implementation to use.
76
+ // The default implementation uses the Docker CLI to list containers.
77
+ func WithLister (cl Lister ) Option {
78
+ return func (api * API ) {
79
+ api .cl = cl
80
+ }
81
+ }
82
+
75
83
// WithDevcontainerCLI sets the DevcontainerCLI implementation to use.
76
84
// This can be used in tests to modify @devcontainer/cli behavior.
77
85
func WithDevcontainerCLI (dccli DevcontainerCLI ) Option {
@@ -113,6 +121,7 @@ func NewAPI(logger slog.Logger, options ...Option) *API {
113
121
done : make (chan struct {}),
114
122
logger : logger ,
115
123
clock : quartz .NewReal (),
124
+ execer : agentexec .DefaultExecer ,
116
125
cacheDuration : defaultGetContainersCacheDuration ,
117
126
lockCh : make (chan struct {}, 1 ),
118
127
devcontainerNames : make (map [string ]struct {}),
@@ -123,30 +132,46 @@ func NewAPI(logger slog.Logger, options ...Option) *API {
123
132
opt (api )
124
133
}
125
134
if api .cl == nil {
126
- api .cl = & DockerCLILister {}
135
+ api .cl = NewDocker ( api . execer )
127
136
}
128
137
if api .dccli == nil {
129
- api .dccli = NewDevcontainerCLI (logger .Named ("devcontainer-cli" ), agentexec . DefaultExecer )
138
+ api .dccli = NewDevcontainerCLI (logger .Named ("devcontainer-cli" ), api . execer )
130
139
}
131
140
if api .watcher == nil {
132
- api .watcher = watcher .NewNoop ()
141
+ var err error
142
+ api .watcher , err = watcher .NewFSNotify ()
143
+ if err != nil {
144
+ logger .Error (ctx , "create file watcher service failed" , slog .Error (err ))
145
+ api .watcher = watcher .NewNoop ()
146
+ }
133
147
}
134
148
149
+ go api .loop ()
150
+
151
+ return api
152
+ }
153
+
154
+ // SignalReady signals the API that we are ready to begin watching for
155
+ // file changes. This is used to prime the cache with the current list
156
+ // of containers and to start watching the devcontainer config files for
157
+ // changes. It should be called after the agent ready.
158
+ func (api * API ) SignalReady () {
159
+ // Prime the cache with the current list of containers.
160
+ _ , _ = api .cl .List (api .ctx )
161
+
135
162
// Make sure we watch the devcontainer config files for changes.
136
163
for _ , devcontainer := range api .knownDevcontainers {
137
- if devcontainer .ConfigPath != "" {
138
- if err := api .watcher .Add (devcontainer .ConfigPath ); err != nil {
139
- api .logger .Error (ctx , "watch devcontainer config file failed" , slog .Error (err ), slog .F ("file" , devcontainer .ConfigPath ))
140
- }
164
+ if devcontainer .ConfigPath == "" {
165
+ continue
141
166
}
142
- }
143
167
144
- go api .start ()
145
-
146
- return api
168
+ if err := api .watcher .Add (devcontainer .ConfigPath ); err != nil {
169
+ api .logger .Error (api .ctx , "watch devcontainer config file failed" , slog .Error (err ), slog .F ("file" , devcontainer .ConfigPath ))
170
+ }
171
+ }
147
172
}
148
173
149
- func (api * API ) start () {
174
+ func (api * API ) loop () {
150
175
defer close (api .done )
151
176
152
177
for {
@@ -187,9 +212,11 @@ func (api *API) start() {
187
212
// Routes returns the HTTP handler for container-related routes.
188
213
func (api * API ) Routes () http.Handler {
189
214
r := chi .NewRouter ()
215
+
190
216
r .Get ("/" , api .handleList )
191
217
r .Get ("/devcontainers" , api .handleListDevcontainers )
192
218
r .Post ("/{id}/recreate" , api .handleRecreate )
219
+
193
220
return r
194
221
}
195
222
0 commit comments