Skip to content

Commit e5a74a7

Browse files
stirbydannykoppingEmyrkBrunoQuaresmaSasSwart
authored
chore: pull in cherry picks for v2.24 (#18674)
Co-authored-by: Danny Kopping <danny@coder.com> Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com> Co-authored-by: Bruno Quaresma <bruno@coder.com> Co-authored-by: Sas Swart <sas.swart.cdk@gmail.com> Co-authored-by: Susana Ferreira <susana@coder.com> Co-authored-by: Danielle Maywood <danielle@themaywoods.com> Co-authored-by: Mathias Fredriksson <mafredri@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Asher <ash@coder.com> Co-authored-by: Hugo Dutka <hugo@coder.com>
1 parent de494d0 commit e5a74a7

File tree

115 files changed

+2208
-8867
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

115 files changed

+2208
-8867
lines changed

agent/agent.go

Lines changed: 92 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ type Options struct {
9191
Execer agentexec.Execer
9292
Devcontainers bool
9393
DevcontainerAPIOptions []agentcontainers.Option // Enable Devcontainers for these to be effective.
94+
Clock quartz.Clock
9495
}
9596

9697
type Client interface {
@@ -144,6 +145,9 @@ func New(options Options) Agent {
144145
if options.PortCacheDuration == 0 {
145146
options.PortCacheDuration = 1 * time.Second
146147
}
148+
if options.Clock == nil {
149+
options.Clock = quartz.NewReal()
150+
}
147151

148152
prometheusRegistry := options.PrometheusRegistry
149153
if prometheusRegistry == nil {
@@ -157,6 +161,7 @@ func New(options Options) Agent {
157161
hardCtx, hardCancel := context.WithCancel(context.Background())
158162
gracefulCtx, gracefulCancel := context.WithCancel(hardCtx)
159163
a := &agent{
164+
clock: options.Clock,
160165
tailnetListenPort: options.TailnetListenPort,
161166
reconnectingPTYTimeout: options.ReconnectingPTYTimeout,
162167
logger: options.Logger,
@@ -204,6 +209,7 @@ func New(options Options) Agent {
204209
}
205210

206211
type agent struct {
212+
clock quartz.Clock
207213
logger slog.Logger
208214
client Client
209215
exchangeToken func(ctx context.Context) (string, error)
@@ -273,7 +279,7 @@ type agent struct {
273279

274280
devcontainers bool
275281
containerAPIOptions []agentcontainers.Option
276-
containerAPI atomic.Pointer[agentcontainers.API] // Set by apiHandler.
282+
containerAPI *agentcontainers.API
277283
}
278284

279285
func (a *agent) TailnetConn() *tailnet.Conn {
@@ -330,6 +336,19 @@ func (a *agent) init() {
330336
// will not report anywhere.
331337
a.scriptRunner.RegisterMetrics(a.prometheusRegistry)
332338

339+
if a.devcontainers {
340+
containerAPIOpts := []agentcontainers.Option{
341+
agentcontainers.WithExecer(a.execer),
342+
agentcontainers.WithCommandEnv(a.sshServer.CommandEnv),
343+
agentcontainers.WithScriptLogger(func(logSourceID uuid.UUID) agentcontainers.ScriptLogger {
344+
return a.logSender.GetScriptLogger(logSourceID)
345+
}),
346+
}
347+
containerAPIOpts = append(containerAPIOpts, a.containerAPIOptions...)
348+
349+
a.containerAPI = agentcontainers.NewAPI(a.logger.Named("containers"), containerAPIOpts...)
350+
}
351+
333352
a.reconnectingPTYServer = reconnectingpty.NewServer(
334353
a.logger.Named("reconnecting-pty"),
335354
a.sshServer,
@@ -1141,17 +1160,27 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
11411160
}
11421161

11431162
var (
1144-
scripts = manifest.Scripts
1145-
scriptRunnerOpts []agentscripts.InitOption
1163+
scripts = manifest.Scripts
1164+
devcontainerScripts map[uuid.UUID]codersdk.WorkspaceAgentScript
11461165
)
1147-
if a.devcontainers {
1148-
var dcScripts []codersdk.WorkspaceAgentScript
1149-
scripts, dcScripts = agentcontainers.ExtractAndInitializeDevcontainerScripts(manifest.Devcontainers, scripts)
1150-
// See ExtractAndInitializeDevcontainerScripts for motivation
1151-
// behind running dcScripts as post start scripts.
1152-
scriptRunnerOpts = append(scriptRunnerOpts, agentscripts.WithPostStartScripts(dcScripts...))
1166+
if a.containerAPI != nil {
1167+
// Init the container API with the manifest and client so that
1168+
// we can start accepting requests. The final start of the API
1169+
// happens after the startup scripts have been executed to
1170+
// ensure the presence of required tools. This means we can
1171+
// return existing devcontainers but actual container detection
1172+
// and creation will be deferred.
1173+
a.containerAPI.Init(
1174+
agentcontainers.WithManifestInfo(manifest.OwnerName, manifest.WorkspaceName, manifest.AgentName),
1175+
agentcontainers.WithDevcontainers(manifest.Devcontainers, manifest.Scripts),
1176+
agentcontainers.WithSubAgentClient(agentcontainers.NewSubAgentClientFromAPI(a.logger, aAPI)),
1177+
)
1178+
1179+
// Since devcontainer are enabled, remove devcontainer scripts
1180+
// from the main scripts list to avoid showing an error.
1181+
scripts, devcontainerScripts = agentcontainers.ExtractDevcontainerScripts(manifest.Devcontainers, scripts)
11531182
}
1154-
err = a.scriptRunner.Init(scripts, aAPI.ScriptCompleted, scriptRunnerOpts...)
1183+
err = a.scriptRunner.Init(scripts, aAPI.ScriptCompleted)
11551184
if err != nil {
11561185
return xerrors.Errorf("init script runner: %w", err)
11571186
}
@@ -1168,7 +1197,18 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
11681197
// finished (both start and post start). For instance, an
11691198
// autostarted devcontainer will be included in this time.
11701199
err := a.scriptRunner.Execute(a.gracefulCtx, agentscripts.ExecuteStartScripts)
1171-
err = errors.Join(err, a.scriptRunner.Execute(a.gracefulCtx, agentscripts.ExecutePostStartScripts))
1200+
1201+
if a.containerAPI != nil {
1202+
// Start the container API after the startup scripts have
1203+
// been executed to ensure that the required tools can be
1204+
// installed.
1205+
a.containerAPI.Start()
1206+
for _, dc := range manifest.Devcontainers {
1207+
cErr := a.createDevcontainer(ctx, aAPI, dc, devcontainerScripts[dc.ID])
1208+
err = errors.Join(err, cErr)
1209+
}
1210+
}
1211+
11721212
dur := time.Since(start).Seconds()
11731213
if err != nil {
11741214
a.logger.Warn(ctx, "startup script(s) failed", slog.Error(err))
@@ -1187,14 +1227,6 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
11871227
}
11881228
a.metrics.startupScriptSeconds.WithLabelValues(label).Set(dur)
11891229
a.scriptRunner.StartCron()
1190-
1191-
// If the container API is enabled, trigger an immediate refresh
1192-
// for quick sub agent injection.
1193-
if cAPI := a.containerAPI.Load(); cAPI != nil {
1194-
if err := cAPI.RefreshContainers(ctx); err != nil {
1195-
a.logger.Error(ctx, "failed to refresh containers", slog.Error(err))
1196-
}
1197-
}
11981230
})
11991231
if err != nil {
12001232
return xerrors.Errorf("track conn goroutine: %w", err)
@@ -1204,6 +1236,38 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
12041236
}
12051237
}
12061238

1239+
func (a *agent) createDevcontainer(
1240+
ctx context.Context,
1241+
aAPI proto.DRPCAgentClient26,
1242+
dc codersdk.WorkspaceAgentDevcontainer,
1243+
script codersdk.WorkspaceAgentScript,
1244+
) (err error) {
1245+
var (
1246+
exitCode = int32(0)
1247+
startTime = a.clock.Now()
1248+
status = proto.Timing_OK
1249+
)
1250+
if err = a.containerAPI.CreateDevcontainer(dc.WorkspaceFolder, dc.ConfigPath); err != nil {
1251+
exitCode = 1
1252+
status = proto.Timing_EXIT_FAILURE
1253+
}
1254+
endTime := a.clock.Now()
1255+
1256+
if _, scriptErr := aAPI.ScriptCompleted(ctx, &proto.WorkspaceAgentScriptCompletedRequest{
1257+
Timing: &proto.Timing{
1258+
ScriptId: script.ID[:],
1259+
Start: timestamppb.New(startTime),
1260+
End: timestamppb.New(endTime),
1261+
ExitCode: exitCode,
1262+
Stage: proto.Timing_START,
1263+
Status: status,
1264+
},
1265+
}); scriptErr != nil {
1266+
a.logger.Warn(ctx, "reporting script completed failed", slog.Error(scriptErr))
1267+
}
1268+
return err
1269+
}
1270+
12071271
// createOrUpdateNetwork waits for the manifest to be set using manifestOK, then creates or updates
12081272
// the tailnet using the information in the manifest
12091273
func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(context.Context, proto.DRPCAgentClient26) error {
@@ -1227,7 +1291,6 @@ func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(co
12271291
// agent API.
12281292
network, err = a.createTailnet(
12291293
a.gracefulCtx,
1230-
aAPI,
12311294
manifest.AgentID,
12321295
manifest.DERPMap,
12331296
manifest.DERPForceWebSockets,
@@ -1262,9 +1325,9 @@ func (a *agent) createOrUpdateNetwork(manifestOK, networkOK *checkpoint) func(co
12621325
network.SetBlockEndpoints(manifest.DisableDirectConnections)
12631326

12641327
// Update the subagent client if the container API is available.
1265-
if cAPI := a.containerAPI.Load(); cAPI != nil {
1328+
if a.containerAPI != nil {
12661329
client := agentcontainers.NewSubAgentClientFromAPI(a.logger, aAPI)
1267-
cAPI.UpdateSubAgentClient(client)
1330+
a.containerAPI.UpdateSubAgentClient(client)
12681331
}
12691332
}
12701333
return nil
@@ -1382,7 +1445,6 @@ func (a *agent) trackGoroutine(fn func()) error {
13821445

13831446
func (a *agent) createTailnet(
13841447
ctx context.Context,
1385-
aAPI proto.DRPCAgentClient26,
13861448
agentID uuid.UUID,
13871449
derpMap *tailcfg.DERPMap,
13881450
derpForceWebSockets, disableDirectConnections bool,
@@ -1515,10 +1577,7 @@ func (a *agent) createTailnet(
15151577
}()
15161578
if err = a.trackGoroutine(func() {
15171579
defer apiListener.Close()
1518-
apiHandler, closeAPIHAndler := a.apiHandler(aAPI)
1519-
defer func() {
1520-
_ = closeAPIHAndler()
1521-
}()
1580+
apiHandler := a.apiHandler()
15221581
server := &http.Server{
15231582
BaseContext: func(net.Listener) context.Context { return ctx },
15241583
Handler: apiHandler,
@@ -1532,7 +1591,6 @@ func (a *agent) createTailnet(
15321591
case <-ctx.Done():
15331592
case <-a.hardCtx.Done():
15341593
}
1535-
_ = closeAPIHAndler()
15361594
_ = server.Close()
15371595
}()
15381596

@@ -1871,6 +1929,12 @@ func (a *agent) Close() error {
18711929
a.logger.Error(a.hardCtx, "script runner close", slog.Error(err))
18721930
}
18731931

1932+
if a.containerAPI != nil {
1933+
if err := a.containerAPI.Close(); err != nil {
1934+
a.logger.Error(a.hardCtx, "container API close", slog.Error(err))
1935+
}
1936+
}
1937+
18741938
// Wait for the graceful shutdown to complete, but don't wait forever so
18751939
// that we don't break user expectations.
18761940
go func() {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy