Skip to content

Commit ffccfb9

Browse files
dannykoppingSasSwartevgeniy-scherbina
authored
chore: cherry-pick remaining PRs into 2.22 (#17851)
Co-authored-by: Sas Swart <sas.swart.cdk@gmail.com> Co-authored-by: Yevhenii Shcherbina <evgeniy.shcherbina.es@gmail.com>
1 parent 3a5c2d7 commit ffccfb9

File tree

73 files changed

+4494
-1137
lines changed

Some content is hidden

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

73 files changed

+4494
-1137
lines changed

agent/agent.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,11 @@ func (a *agent) runLoop() {
363363
if ctx.Err() != nil {
364364
// Context canceled errors may come from websocket pings, so we
365365
// don't want to use `errors.Is(err, context.Canceled)` here.
366+
a.logger.Warn(ctx, "runLoop exited with error", slog.Error(ctx.Err()))
366367
return
367368
}
368369
if a.isClosed() {
370+
a.logger.Warn(ctx, "runLoop exited because agent is closed")
369371
return
370372
}
371373
if errors.Is(err, io.EOF) {
@@ -1046,7 +1048,11 @@ func (a *agent) run() (retErr error) {
10461048
return a.statsReporter.reportLoop(ctx, aAPI)
10471049
})
10481050

1049-
return connMan.wait()
1051+
err = connMan.wait()
1052+
if err != nil {
1053+
a.logger.Info(context.Background(), "connection manager errored", slog.Error(err))
1054+
}
1055+
return err
10501056
}
10511057

10521058
// handleManifest returns a function that fetches and processes the manifest

cli/agent.go

+70-43
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"cdr.dev/slog/sloggers/sloghuman"
2626
"cdr.dev/slog/sloggers/slogjson"
2727
"cdr.dev/slog/sloggers/slogstackdriver"
28+
"github.com/coder/serpent"
29+
2830
"github.com/coder/coder/v2/agent"
2931
"github.com/coder/coder/v2/agent/agentexec"
3032
"github.com/coder/coder/v2/agent/agentssh"
@@ -33,7 +35,6 @@ import (
3335
"github.com/coder/coder/v2/cli/clilog"
3436
"github.com/coder/coder/v2/codersdk"
3537
"github.com/coder/coder/v2/codersdk/agentsdk"
36-
"github.com/coder/serpent"
3738
)
3839

3940
func (r *RootCmd) workspaceAgent() *serpent.Command {
@@ -62,8 +63,10 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
6263
// This command isn't useful to manually execute.
6364
Hidden: true,
6465
Handler: func(inv *serpent.Invocation) error {
65-
ctx, cancel := context.WithCancel(inv.Context())
66-
defer cancel()
66+
ctx, cancel := context.WithCancelCause(inv.Context())
67+
defer func() {
68+
cancel(xerrors.New("agent exited"))
69+
}()
6770

6871
var (
6972
ignorePorts = map[int]string{}
@@ -280,7 +283,6 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
280283
return xerrors.Errorf("add executable to $PATH: %w", err)
281284
}
282285

283-
prometheusRegistry := prometheus.NewRegistry()
284286
subsystemsRaw := inv.Environ.Get(agent.EnvAgentSubsystem)
285287
subsystems := []codersdk.AgentSubsystem{}
286288
for _, s := range strings.Split(subsystemsRaw, ",") {
@@ -324,45 +326,70 @@ func (r *RootCmd) workspaceAgent() *serpent.Command {
324326
logger.Info(ctx, "agent devcontainer detection not enabled")
325327
}
326328

327-
agnt := agent.New(agent.Options{
328-
Client: client,
329-
Logger: logger,
330-
LogDir: logDir,
331-
ScriptDataDir: scriptDataDir,
332-
// #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535)
333-
TailnetListenPort: uint16(tailnetListenPort),
334-
ExchangeToken: func(ctx context.Context) (string, error) {
335-
if exchangeToken == nil {
336-
return client.SDK.SessionToken(), nil
337-
}
338-
resp, err := exchangeToken(ctx)
339-
if err != nil {
340-
return "", err
341-
}
342-
client.SetSessionToken(resp.SessionToken)
343-
return resp.SessionToken, nil
344-
},
345-
EnvironmentVariables: environmentVariables,
346-
IgnorePorts: ignorePorts,
347-
SSHMaxTimeout: sshMaxTimeout,
348-
Subsystems: subsystems,
349-
350-
PrometheusRegistry: prometheusRegistry,
351-
BlockFileTransfer: blockFileTransfer,
352-
Execer: execer,
353-
354-
ExperimentalDevcontainersEnabled: experimentalDevcontainersEnabled,
355-
})
356-
357-
promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger)
358-
prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus")
359-
defer prometheusSrvClose()
360-
361-
debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
362-
defer debugSrvClose()
363-
364-
<-ctx.Done()
365-
return agnt.Close()
329+
reinitEvents := agentsdk.WaitForReinitLoop(ctx, logger, client)
330+
331+
var (
332+
lastErr error
333+
mustExit bool
334+
)
335+
for {
336+
prometheusRegistry := prometheus.NewRegistry()
337+
338+
agnt := agent.New(agent.Options{
339+
Client: client,
340+
Logger: logger,
341+
LogDir: logDir,
342+
ScriptDataDir: scriptDataDir,
343+
// #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535)
344+
TailnetListenPort: uint16(tailnetListenPort),
345+
ExchangeToken: func(ctx context.Context) (string, error) {
346+
if exchangeToken == nil {
347+
return client.SDK.SessionToken(), nil
348+
}
349+
resp, err := exchangeToken(ctx)
350+
if err != nil {
351+
return "", err
352+
}
353+
client.SetSessionToken(resp.SessionToken)
354+
return resp.SessionToken, nil
355+
},
356+
EnvironmentVariables: environmentVariables,
357+
IgnorePorts: ignorePorts,
358+
SSHMaxTimeout: sshMaxTimeout,
359+
Subsystems: subsystems,
360+
361+
PrometheusRegistry: prometheusRegistry,
362+
BlockFileTransfer: blockFileTransfer,
363+
Execer: execer,
364+
365+
ExperimentalDevcontainersEnabled: experimentalDevcontainersEnabled,
366+
})
367+
368+
promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger)
369+
prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus")
370+
371+
debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug")
372+
373+
select {
374+
case <-ctx.Done():
375+
logger.Info(ctx, "agent shutting down", slog.Error(context.Cause(ctx)))
376+
mustExit = true
377+
case event := <-reinitEvents:
378+
logger.Info(ctx, "agent received instruction to reinitialize",
379+
slog.F("workspace_id", event.WorkspaceID), slog.F("reason", event.Reason))
380+
}
381+
382+
lastErr = agnt.Close()
383+
debugSrvClose()
384+
prometheusSrvClose()
385+
386+
if mustExit {
387+
break
388+
}
389+
390+
logger.Info(ctx, "agent reinitializing")
391+
}
392+
return lastErr
366393
},
367394
}
368395

cli/server.go

+31-31
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,37 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
910910
options.StatsBatcher = batcher
911911
defer closeBatcher()
912912

913+
// Manage notifications.
914+
var (
915+
notificationsCfg = options.DeploymentValues.Notifications
916+
notificationsManager *notifications.Manager
917+
)
918+
919+
metrics := notifications.NewMetrics(options.PrometheusRegistry)
920+
helpers := templateHelpers(options)
921+
922+
// The enqueuer is responsible for enqueueing notifications to the given store.
923+
enqueuer, err := notifications.NewStoreEnqueuer(notificationsCfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal())
924+
if err != nil {
925+
return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err)
926+
}
927+
options.NotificationsEnqueuer = enqueuer
928+
929+
// The notification manager is responsible for:
930+
// - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications)
931+
// - keeping the store updated with status updates
932+
notificationsManager, err = notifications.NewManager(notificationsCfg, options.Database, options.Pubsub, helpers, metrics, logger.Named("notifications.manager"))
933+
if err != nil {
934+
return xerrors.Errorf("failed to instantiate notification manager: %w", err)
935+
}
936+
937+
// nolint:gocritic // We need to run the manager in a notifier context.
938+
notificationsManager.Run(dbauthz.AsNotifier(ctx))
939+
940+
// Run report generator to distribute periodic reports.
941+
notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal())
942+
defer notificationReportGenerator.Close()
943+
913944
// We use a separate coderAPICloser so the Enterprise API
914945
// can have its own close functions. This is cleaner
915946
// than abstracting the Coder API itself.
@@ -957,37 +988,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
957988
return xerrors.Errorf("write config url: %w", err)
958989
}
959990

960-
// Manage notifications.
961-
var (
962-
notificationsCfg = options.DeploymentValues.Notifications
963-
notificationsManager *notifications.Manager
964-
)
965-
966-
metrics := notifications.NewMetrics(options.PrometheusRegistry)
967-
helpers := templateHelpers(options)
968-
969-
// The enqueuer is responsible for enqueueing notifications to the given store.
970-
enqueuer, err := notifications.NewStoreEnqueuer(notificationsCfg, options.Database, helpers, logger.Named("notifications.enqueuer"), quartz.NewReal())
971-
if err != nil {
972-
return xerrors.Errorf("failed to instantiate notification store enqueuer: %w", err)
973-
}
974-
options.NotificationsEnqueuer = enqueuer
975-
976-
// The notification manager is responsible for:
977-
// - creating notifiers and managing their lifecycles (notifiers are responsible for dequeueing/sending notifications)
978-
// - keeping the store updated with status updates
979-
notificationsManager, err = notifications.NewManager(notificationsCfg, options.Database, options.Pubsub, helpers, metrics, logger.Named("notifications.manager"))
980-
if err != nil {
981-
return xerrors.Errorf("failed to instantiate notification manager: %w", err)
982-
}
983-
984-
// nolint:gocritic // We need to run the manager in a notifier context.
985-
notificationsManager.Run(dbauthz.AsNotifier(ctx))
986-
987-
// Run report generator to distribute periodic reports.
988-
notificationReportGenerator := reports.NewReportGenerator(ctx, logger.Named("notifications.report_generator"), options.Database, options.NotificationsEnqueuer, quartz.NewReal())
989-
defer notificationReportGenerator.Close()
990-
991991
// Since errCh only has one buffered slot, all routines
992992
// sending on it must be wrapped in a select/default to
993993
// avoid leaving dangling goroutines waiting for the

cli/testdata/coder_provisioner_list_--output_json.golden

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"last_seen_at": "====[timestamp]=====",
88
"name": "test",
99
"version": "v0.0.0-devel",
10-
"api_version": "1.4",
10+
"api_version": "1.5",
1111
"provisioners": [
1212
"echo"
1313
],

coderd/apidoc/docs.go

+45
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+37
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)