@@ -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 ()
@@ -622,9 +631,9 @@ func safeFriendlyName(name string) string {
622
631
return name
623
632
}
624
633
625
- // refreshContainers triggers an immediate update of the container list
634
+ // RefreshContainers triggers an immediate update of the container list
626
635
// and waits for it to complete.
627
- func (api * API ) refreshContainers (ctx context.Context ) (err error ) {
636
+ func (api * API ) RefreshContainers (ctx context.Context ) (err error ) {
628
637
defer func () {
629
638
if err != nil {
630
639
err = xerrors .Errorf ("refresh containers failed: %w" , err )
@@ -859,7 +868,7 @@ func (api *API) recreateDevcontainer(dc codersdk.WorkspaceAgentDevcontainer, con
859
868
860
869
// Ensure an immediate refresh to accurately reflect the
861
870
// devcontainer state after recreation.
862
- if err := api .refreshContainers (ctx ); err != nil {
871
+ if err := api .RefreshContainers (ctx ); err != nil {
863
872
logger .Error (ctx , "failed to trigger immediate refresh after devcontainer recreation" , slog .Error (err ))
864
873
}
865
874
}
@@ -903,7 +912,8 @@ func (api *API) markDevcontainerDirty(configPath string, modifiedAt time.Time) {
903
912
// slate. This method has an internal timeout to prevent blocking
904
913
// indefinitely if something goes wrong with the subagent deletion.
905
914
func (api * API ) cleanupSubAgents (ctx context.Context ) error {
906
- agents , err := api .subAgentClient .List (ctx )
915
+ client := * api .subAgentClient .Load ()
916
+ agents , err := client .List (ctx )
907
917
if err != nil {
908
918
return xerrors .Errorf ("list agents: %w" , err )
909
919
}
@@ -926,7 +936,8 @@ func (api *API) cleanupSubAgents(ctx context.Context) error {
926
936
if injected [agent .ID ] {
927
937
continue
928
938
}
929
- err := api .subAgentClient .Delete (ctx , agent .ID )
939
+ client := * api .subAgentClient .Load ()
940
+ err := client .Delete (ctx , agent .ID )
930
941
if err != nil {
931
942
api .logger .Error (ctx , "failed to delete agent" ,
932
943
slog .Error (err ),
@@ -1100,7 +1111,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1100
1111
1101
1112
if proc .agent .ID != uuid .Nil && recreateSubAgent {
1102
1113
logger .Debug (ctx , "deleting existing subagent for recreation" , slog .F ("agent_id" , proc .agent .ID ))
1103
- err = api .subAgentClient .Delete (ctx , proc .agent .ID )
1114
+ client := * api .subAgentClient .Load ()
1115
+ err = client .Delete (ctx , proc .agent .ID )
1104
1116
if err != nil {
1105
1117
return xerrors .Errorf ("delete existing subagent failed: %w" , err )
1106
1118
}
@@ -1143,7 +1155,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1143
1155
)
1144
1156
1145
1157
// Create new subagent record in the database to receive the auth token.
1146
- proc .agent , err = api .subAgentClient .Create (ctx , SubAgent {
1158
+ client := * api .subAgentClient .Load ()
1159
+ proc .agent , err = client .Create (ctx , SubAgent {
1147
1160
Name : dc .Name ,
1148
1161
Directory : directory ,
1149
1162
OperatingSystem : "linux" , // Assuming Linux for devcontainers.
@@ -1162,7 +1175,8 @@ func (api *API) maybeInjectSubAgentIntoContainerLocked(ctx context.Context, dc c
1162
1175
if api .closed {
1163
1176
deleteCtx , deleteCancel := context .WithTimeout (context .Background (), defaultOperationTimeout )
1164
1177
defer deleteCancel ()
1165
- err := api .subAgentClient .Delete (deleteCtx , proc .agent .ID )
1178
+ client := * api .subAgentClient .Load ()
1179
+ err := client .Delete (deleteCtx , proc .agent .ID )
1166
1180
if err != nil {
1167
1181
return xerrors .Errorf ("delete existing subagent failed after API closed: %w" , err )
1168
1182
}
@@ -1248,8 +1262,9 @@ func (api *API) Close() error {
1248
1262
// Note: We can't use api.ctx here because it's canceled.
1249
1263
deleteCtx , deleteCancel := context .WithTimeout (context .Background (), defaultOperationTimeout )
1250
1264
defer deleteCancel ()
1265
+ client := * api .subAgentClient .Load ()
1251
1266
for _ , id := range subAgentIDs {
1252
- err := api . subAgentClient .Delete (deleteCtx , id )
1267
+ err := client .Delete (deleteCtx , id )
1253
1268
if err != nil {
1254
1269
api .logger .Error (api .ctx , "delete subagent record during shutdown failed" ,
1255
1270
slog .Error (err ),
0 commit comments