@@ -14,6 +14,7 @@ import (
14
14
"slices"
15
15
"strings"
16
16
"sync"
17
+ "sync/atomic"
17
18
"time"
18
19
19
20
"github.com/fsnotify/fsnotify"
@@ -59,7 +60,7 @@ type API struct {
59
60
dccli DevcontainerCLI
60
61
clock quartz.Clock
61
62
scriptLogger func (logSourceID uuid.UUID ) ScriptLogger
62
- subAgentClient SubAgentClient
63
+ subAgentClient atomic. Pointer [ SubAgentClient ]
63
64
subAgentURL string
64
65
subAgentEnv []string
65
66
@@ -133,7 +134,7 @@ func WithDevcontainerCLI(dccli DevcontainerCLI) Option {
133
134
// This is used to list, create, and delete devcontainer agents.
134
135
func WithSubAgentClient (client SubAgentClient ) Option {
135
136
return func (api * API ) {
136
- api .subAgentClient = client
137
+ api .subAgentClient . Store ( & client )
137
138
}
138
139
}
139
140
@@ -230,7 +231,6 @@ func NewAPI(logger slog.Logger, options ...Option) *API {
230
231
logger : logger ,
231
232
clock : quartz .NewReal (),
232
233
execer : agentexec .DefaultExecer ,
233
- subAgentClient : noopSubAgentClient {},
234
234
containerLabelIncludeFilter : make (map [string ]string ),
235
235
devcontainerNames : make (map [string ]bool ),
236
236
knownDevcontainers : make (map [string ]codersdk.WorkspaceAgentDevcontainer ),
@@ -259,6 +259,10 @@ func NewAPI(logger slog.Logger, options ...Option) *API {
259
259
api .watcher = watcher .NewNoop ()
260
260
}
261
261
}
262
+ if api .subAgentClient .Load () == nil {
263
+ var c SubAgentClient = noopSubAgentClient {}
264
+ api .subAgentClient .Store (& c )
265
+ }
262
266
263
267
go api .watcherLoop ()
264
268
go api .updaterLoop ()
@@ -375,6 +379,11 @@ func (api *API) updaterLoop() {
375
379
}
376
380
}
377
381
382
+ // UpdateSubAgentClient updates the `SubAgentClient` for the API.
383
+ func (api * API ) UpdateSubAgentClient (client SubAgentClient ) {
384
+ api .subAgentClient .Store (& client )
385
+ }
386
+
378
387
// Routes returns the HTTP handler for container-related routes.
379
388
func (api * API ) Routes () http.Handler {
380
389
r := chi .NewRouter ()
@@ -623,9 +632,9 @@ func safeFriendlyName(name string) string {
623
632
return name
624
633
}
625
634
626
- // refreshContainers triggers an immediate update of the container list
635
+ // RefreshContainers triggers an immediate update of the container list
627
636
// and waits for it to complete.
628
- func (api * API ) refreshContainers (ctx context.Context ) (err error ) {
637
+ func (api * API ) RefreshContainers (ctx context.Context ) (err error ) {
629
638
defer func () {
630
639
if err != nil {
631
640
err = xerrors .Errorf ("refresh containers failed: %w" , err )
@@ -860,7 +869,7 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
860
869
861
870
// Ensure an immediate refresh to accurately reflect the
862
871
// devcontainer state after recreation.
863
- if err := api .refreshContainers (ctx ); err != nil {
872
+ if err := api .RefreshContainers (ctx ); err != nil {
864
873
logger .Error (ctx , "failed to trigger immediate refresh after devcontainer recreation" , slog .Error (err ))
865
874
}
866
875
}
@@ -904,7 +913,8 @@ func (api *API) markDevcontainerDirty(configPath string, modifiedAt time.Time) {
904
913
// slate. This method has an internal timeout to prevent blocking
905
914
// indefinitely if something goes wrong with the subagent deletion.
906
915
func (api * API ) cleanupSubAgents (ctx context.Context ) error {
907
- agents , err := api .subAgentClient .List (ctx )
916
+ client := * api .subAgentClient .Load ()
917
+ agents , err := client .List (ctx )
908
918
if err != nil {
909
919
return xerrors .Errorf ("list agents: %w" , err )
910
920
}
@@ -927,7 +937,8 @@ func (api *API) cleanupSubAgents(ctx context.Context) error {
927
937
if injected [agent .ID ] {
928
938
continue
929
939
}
930
- err := api .subAgentClient .Delete (ctx , agent .ID )
940
+ client := * api .subAgentClient .Load ()
941
+ err := client .Delete (ctx , agent .ID )
931
942
if err != nil {
932
943
api .logger .Error (ctx , "failed to delete agent" ,
933
944
slog .Error (err ),
@@ -1101,7 +1112,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1101
1112
1102
1113
if proc .agent .ID != uuid .Nil && recreateSubAgent {
1103
1114
logger .Debug (ctx , "deleting existing subagent for recreation" , slog .F ("agent_id" , proc .agent .ID ))
1104
- err = api .subAgentClient .Delete (ctx , proc .agent .ID )
1115
+ client := * api .subAgentClient .Load ()
1116
+ err = client .Delete (ctx , proc .agent .ID )
1105
1117
if err != nil {
1106
1118
return xerrors .Errorf ("delete existing subagent failed: %w" , err )
1107
1119
}
@@ -1144,7 +1156,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1144
1156
)
1145
1157
1146
1158
// Create new subagent record in the database to receive the auth token.
1147
- proc .agent , err = api .subAgentClient .Create (ctx , SubAgent {
1159
+ client := * api .subAgentClient .Load ()
1160
+ proc .agent , err = client .Create (ctx , SubAgent {
1148
1161
Name : dc .Name ,
1149
1162
Directory : directory ,
1150
1163
OperatingSystem : "linux" , // Assuming Linux for devcontainers.
@@ -1163,7 +1176,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1163
1176
if api .closed {
1164
1177
deleteCtx , deleteCancel := context .WithTimeout (context .Background (), defaultOperationTimeout )
1165
1178
defer deleteCancel ()
1166
- err := api .subAgentClient .Delete (deleteCtx , proc .agent .ID )
1179
+ client := * api .subAgentClient .Load ()
1180
+ err := client .Delete (deleteCtx , proc .agent .ID )
1167
1181
if err != nil {
1168
1182
return xerrors .Errorf ("delete existing subagent failed after API closed: %w" , err )
1169
1183
}
@@ -1249,8 +1263,9 @@ func (api *API) Close() error {
1249
1263
// Note: We can't use api.ctx here because it's canceled.
1250
1264
deleteCtx , deleteCancel := context .WithTimeout (context .Background (), defaultOperationTimeout )
1251
1265
defer deleteCancel ()
1266
+ client := * api .subAgentClient .Load ()
1252
1267
for _ , id := range subAgentIDs {
1253
- err := api . subAgentClient .Delete (deleteCtx , id )
1268
+ err := client .Delete (deleteCtx , id )
1254
1269
if err != nil {
1255
1270
api .logger .Error (api .ctx , "delete subagent record during shutdown failed" ,
1256
1271
slog .Error (err ),
0 commit comments