- Notifications
You must be signed in to change notification settings - Fork1.1k
feat: reinitialize agents when a prebuilt workspace is claimed#17475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes from6 commits
c09c9b9476fe718c8bca67ce4eea52ac64e362db7cdcc7379ff66b3fefff5d9cebd5db26791389feebefb117b5ca22b4149bbd2c758042017e8dcee725f97ba9b156721ee970e54d7e727998581d93003763fc120f879c761784c9604eb27bf4d2cf38b4f0d20df5384bb3b6883972db146b1585eb16cd730d803150adc0b4ecf103fa3edf7e45919a63250872125ecb65eea7e1339f3c1a8ba65363dcc7ad9b6d394571d890747bb3870dbFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -36,6 +36,9 @@ import ( | ||
| "tailscale.com/util/clientmetric" | ||
| "cdr.dev/slog" | ||
| "github.com/coder/retry" | ||
SasSwart marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| "github.com/coder/clistat" | ||
| "github.com/coder/coder/v2/agent/agentcontainers" | ||
| "github.com/coder/coder/v2/agent/agentexec" | ||
| @@ -53,7 +56,6 @@ import ( | ||
| "github.com/coder/coder/v2/tailnet" | ||
| tailnetproto "github.com/coder/coder/v2/tailnet/proto" | ||
| "github.com/coder/quartz" | ||
| ) | ||
| const ( | ||
| @@ -365,9 +367,11 @@ func (a *agent) runLoop() { | ||
| if ctx.Err() != nil { | ||
| // Context canceled errors may come from websocket pings, so we | ||
| // don't want to use `errors.Is(err, context.Canceled)` here. | ||
| a.logger.Warn(ctx, "runLoop exited with error", slog.Error(ctx.Err())) | ||
| return | ||
| } | ||
| if a.isClosed() { | ||
| a.logger.Warn(ctx, "runLoop exited because agent is closed") | ||
| return | ||
| } | ||
| if errors.Is(err, io.EOF) { | ||
| @@ -1048,7 +1052,11 @@ func (a *agent) run() (retErr error) { | ||
| return a.statsReporter.reportLoop(ctx, aAPI) | ||
| }) | ||
| err = connMan.wait() | ||
| if err != nil { | ||
| a.logger.Warn(context.Background(), "connection manager errored", slog.Error(err)) | ||
SasSwart marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| } | ||
| return err | ||
| } | ||
| // handleManifest returns a function that fetches and processes the manifest | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -19,12 +19,16 @@ import ( | ||
| "golang.org/x/xerrors" | ||
| "gopkg.in/natefinch/lumberjack.v2" | ||
| "github.com/coder/retry" | ||
| "github.com/prometheus/client_golang/prometheus" | ||
| "cdr.dev/slog" | ||
| "cdr.dev/slog/sloggers/sloghuman" | ||
| "cdr.dev/slog/sloggers/slogjson" | ||
| "cdr.dev/slog/sloggers/slogstackdriver" | ||
| "github.com/coder/serpent" | ||
| "github.com/coder/coder/v2/agent" | ||
| "github.com/coder/coder/v2/agent/agentcontainers" | ||
| "github.com/coder/coder/v2/agent/agentexec" | ||
| @@ -34,7 +38,6 @@ import ( | ||
| "github.com/coder/coder/v2/cli/clilog" | ||
| "github.com/coder/coder/v2/codersdk" | ||
| "github.com/coder/coder/v2/codersdk/agentsdk" | ||
| ) | ||
| func (r *RootCmd) workspaceAgent() *serpent.Command { | ||
| @@ -63,8 +66,10 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { | ||
| // This command isn't useful to manually execute. | ||
| Hidden: true, | ||
| Handler: func(inv *serpent.Invocation) error { | ||
| ctx, cancel := context.WithCancelCause(inv.Context()) | ||
| defer func() { | ||
| cancel(xerrors.New("defer")) | ||
SasSwart marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| }() | ||
| var ( | ||
| ignorePorts = map[int]string{} | ||
| @@ -281,7 +286,6 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { | ||
| return xerrors.Errorf("add executable to $PATH: %w", err) | ||
| } | ||
| subsystemsRaw := inv.Environ.Get(agent.EnvAgentSubsystem) | ||
| subsystems := []codersdk.AgentSubsystem{} | ||
| for _, s := range strings.Split(subsystemsRaw, ",") { | ||
| @@ -328,46 +332,90 @@ func (r *RootCmd) workspaceAgent() *serpent.Command { | ||
| containerLister = agentcontainers.NewDocker(execer) | ||
| } | ||
| // TODO: timeout ok? | ||
| reinitCtx, reinitCancel := context.WithTimeout(context.Background(), time.Hour*24) | ||
| defer reinitCancel() | ||
| reinitEvents := make(chan agentsdk.ReinitializationResponse) | ||
| go func() { | ||
| // Retry to wait for reinit, main context cancels the retrier. | ||
| for retrier := retry.New(100*time.Millisecond, 10*time.Second); retrier.Wait(ctx); { | ||
| select { | ||
| case <-reinitCtx.Done(): | ||
| return | ||
| default: | ||
| } | ||
| err := client.WaitForReinit(reinitCtx, reinitEvents) | ||
| if err != nil { | ||
| logger.Error(ctx, "failed to wait for reinit instructions, will retry",slog.Error(err)) | ||
| } | ||
| } | ||
| }() | ||
| var ( | ||
| lastErr error | ||
| mustExit bool | ||
| ) | ||
| for { | ||
| prometheusRegistry := prometheus.NewRegistry() | ||
| agnt := agent.New(agent.Options{ | ||
| Client: client, | ||
| Logger: logger, | ||
| LogDir: logDir, | ||
| ScriptDataDir: scriptDataDir, | ||
| // #nosec G115 - Safe conversion as tailnet listen port is within uint16 range (0-65535) | ||
| TailnetListenPort: uint16(tailnetListenPort), | ||
| ExchangeToken: func(ctx context.Context) (string, error) { | ||
| if exchangeToken == nil { | ||
| return client.SDK.SessionToken(), nil | ||
| } | ||
| resp, err := exchangeToken(ctx) | ||
| if err != nil { | ||
| return "", err | ||
| } | ||
| client.SetSessionToken(resp.SessionToken) | ||
| return resp.SessionToken, nil | ||
| }, | ||
| EnvironmentVariables: environmentVariables, | ||
| IgnorePorts: ignorePorts, | ||
| SSHMaxTimeout: sshMaxTimeout, | ||
| Subsystems: subsystems, | ||
| PrometheusRegistry: prometheusRegistry, | ||
| BlockFileTransfer: blockFileTransfer, | ||
| Execer: execer, | ||
| ContainerLister: containerLister, | ||
| ExperimentalDevcontainersEnabled: experimentalDevcontainersEnabled, | ||
| }) | ||
| promHandler := agent.PrometheusMetricsHandler(prometheusRegistry, logger) | ||
| prometheusSrvClose := ServeHandler(ctx, logger, promHandler, prometheusAddress, "prometheus") | ||
| debugSrvClose := ServeHandler(ctx, logger, agnt.HTTPDebug(), debugAddress, "debug") | ||
| select { | ||
| case <-ctx.Done(): | ||
| logger.Warn(ctx, "agent shutting down", slog.Error(ctx.Err()), slog.F("cause", context.Cause(ctx))) | ||
| mustExit = true | ||
| case event := <-reinitEvents: | ||
| logger.Warn(ctx, "agent received instruction to reinitialize", | ||
| slog.F("message", event.Message), slog.F("reason", event.Reason)) | ||
| } | ||
| lastErr = agnt.Close() | ||
| debugSrvClose() | ||
| prometheusSrvClose() | ||
| if mustExit { | ||
| reinitCancel() | ||
| break | ||
| } | ||
| logger.Info(ctx, "agent reinitializing") | ||
| } | ||
| return lastErr | ||
| }, | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -109,6 +109,8 @@ func New(opts Options) *API { | ||
| Database: opts.Database, | ||
| DerpMapFn: opts.DerpMapFn, | ||
| WorkspaceID: opts.WorkspaceID, | ||
| Log: opts.Log.Named("manifests"), | ||
SasSwart marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| Pubsub: opts.Pubsub, | ||
| } | ||
| api.AnnouncementBannerAPI = &AnnouncementBannerAPI{ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -8,6 +8,10 @@ import ( | ||
| "strings" | ||
| "time" | ||
| "cdr.dev/slog" | ||
| "github.com/coder/coder/v2/coderd/database/pubsub" | ||
| "github.com/google/uuid" | ||
| "golang.org/x/sync/errgroup" | ||
| "golang.org/x/xerrors" | ||
| @@ -35,6 +39,8 @@ type ManifestAPI struct { | ||
| AgentFn func(context.Context) (database.WorkspaceAgent, error) | ||
| Database database.Store | ||
| DerpMapFn func() *tailcfg.DERPMap | ||
| Pubsub pubsub.Pubsub | ||
SasSwart marked this conversation as resolved. OutdatedShow resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| Log slog.Logger | ||
| } | ||
| func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifestRequest) (*agentproto.Manifest, error) { | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.