From 7199651a857440744f2f2a60e0ada9b969378628 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 3 Sep 2023 17:53:41 +0000 Subject: [PATCH 01/59] Add basic migrations --- .../migrations/000152_workspace_agent_script.down.sql | 0 .../migrations/000152_workspace_agent_script.up.sql | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 coderd/database/migrations/000152_workspace_agent_script.down.sql create mode 100644 coderd/database/migrations/000152_workspace_agent_script.up.sql diff --git a/coderd/database/migrations/000152_workspace_agent_script.down.sql b/coderd/database/migrations/000152_workspace_agent_script.down.sql new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/coderd/database/migrations/000152_workspace_agent_script.up.sql b/coderd/database/migrations/000152_workspace_agent_script.up.sql new file mode 100644 index 0000000000000..d57788cb691f7 --- /dev/null +++ b/coderd/database/migrations/000152_workspace_agent_script.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE workspace_agent_log_source ( + workspace_agent_id uuid NOT NULL, + id + display_name varchar(127) NOT NULL, + icon text NOT NULL, +); + +-- Set the table to unlogged to speed up the inserts +ALTER TABLE workspace_agent_logs SET UNLOGGED; From c18a401d5dc0f691d0d68e7109191abeb6726b8d Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Tue, 5 Sep 2023 14:21:57 +0000 Subject: [PATCH 02/59] Improve schema --- .../000152_workspace_agent_script.up.sql | 9 ------- ...=> 000154_workspace_agent_script.down.sql} | 0 .../000154_workspace_agent_script.up.sql | 25 +++++++++++++++++++ 3 files changed, 25 insertions(+), 9 deletions(-) delete mode 100644 coderd/database/migrations/000152_workspace_agent_script.up.sql rename coderd/database/migrations/{000152_workspace_agent_script.down.sql => 000154_workspace_agent_script.down.sql} (100%) create mode 100644 coderd/database/migrations/000154_workspace_agent_script.up.sql diff --git a/coderd/database/migrations/000152_workspace_agent_script.up.sql b/coderd/database/migrations/000152_workspace_agent_script.up.sql deleted file mode 100644 index d57788cb691f7..0000000000000 --- a/coderd/database/migrations/000152_workspace_agent_script.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE workspace_agent_log_source ( - workspace_agent_id uuid NOT NULL, - id - display_name varchar(127) NOT NULL, - icon text NOT NULL, -); - --- Set the table to unlogged to speed up the inserts -ALTER TABLE workspace_agent_logs SET UNLOGGED; diff --git a/coderd/database/migrations/000152_workspace_agent_script.down.sql b/coderd/database/migrations/000154_workspace_agent_script.down.sql similarity index 100% rename from coderd/database/migrations/000152_workspace_agent_script.down.sql rename to coderd/database/migrations/000154_workspace_agent_script.down.sql diff --git a/coderd/database/migrations/000154_workspace_agent_script.up.sql b/coderd/database/migrations/000154_workspace_agent_script.up.sql new file mode 100644 index 0000000000000..b84a9d1ef6cbf --- /dev/null +++ b/coderd/database/migrations/000154_workspace_agent_script.up.sql @@ -0,0 +1,25 @@ +BEGIN; +CREATE TABLE workspace_agent_log_source ( + workspace_agent_id uuid NOT NULL, + id uuid NOT NULL, + created_at timestamptz NOT NULL, + display_name varchar(127) NOT NULL, + icon text NOT NULL, + PRIMARY KEY (workspace_agent_id, id) +); + +CREATE TABLE workspace_agent_script ( + workspace_agent_id uuid NOT NULL, + log_source_id uuid NOT NULL, + created_at timestamptz NOT NULL, + script text NOT NULL, + schedule text NOT NULL, + login_before_ready boolean NOT NULL, + name varchar(127) NOT NULL, + description text NOT NULL, + PRIMARY KEY (workspace_agent_id, id) +); + +-- Set the table to unlogged to speed up the inserts +ALTER TABLE workspace_agent_logs SET UNLOGGED; +COMMIT; From 70ebaf3374a2e4000d9fe47c4a11c149eb96dadf Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 17:28:29 +0000 Subject: [PATCH 03/59] Refactor agent scripts into it's own package --- agent/agent.go | 170 +++++------- agent/agent_test.go | 150 ++++------- agent/agentscripts/agentscripts.go | 246 ++++++++++++++++++ agent/agentscripts/agentscripts_test.go | 82 ++++++ coderd/database/dbauthz/dbauthz.go | 4 + coderd/database/dbfake/dbfake.go | 24 +- coderd/database/dbgen/dbgen.go | 26 +- coderd/database/dbmetrics/dbmetrics.go | 7 + coderd/database/dump.sql | 50 ++-- .../000154_workspace_agent_script.up.sql | 19 +- coderd/database/models.go | 112 ++------ coderd/database/querier_test.go | 86 +++--- coderd/database/queries.sql.go | 177 +++++++------ coderd/database/queries/workspaceagents.sql | 20 +- coderd/workspaceagents.go | 17 +- codersdk/agentsdk/agentsdk.go | 25 +- codersdk/agentsdk/logs.go | 16 +- codersdk/workspaceagents.go | 96 ++++--- site/src/api/typesGenerated.ts | 42 ++- 19 files changed, 789 insertions(+), 580 deletions(-) create mode 100644 agent/agentscripts/agentscripts.go create mode 100644 agent/agentscripts/agentscripts_test.go diff --git a/agent/agent.go b/agent/agent.go index 44f55fcedc83d..761aae91c861a 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -34,6 +34,7 @@ import ( "tailscale.com/types/netlogtype" "cdr.dev/slog" + "github.com/coder/coder/v2/agent/agentscripts" "github.com/coder/coder/v2/agent/agentssh" "github.com/coder/coder/v2/agent/reconnectingpty" "github.com/coder/coder/v2/buildinfo" @@ -177,6 +178,7 @@ type agent struct { manifest atomic.Pointer[agentsdk.Manifest] // manifest is atomic because values can change after reconnection. reportMetadataInterval time.Duration + scriptRunner *agentscripts.Runner serviceBanner atomic.Pointer[codersdk.ServiceBannerConfig] // serviceBanner is atomic because it is periodically updated. serviceBannerRefreshInterval time.Duration sessionToken atomic.Pointer[string] @@ -213,7 +215,13 @@ func (a *agent) init(ctx context.Context) { sshSrv.Manifest = &a.manifest sshSrv.ServiceBanner = &a.serviceBanner a.sshServer = sshSrv - + a.scriptRunner = agentscripts.New(ctx, agentscripts.Options{ + LogDir: a.logDir, + Logger: a.logger, + SSHServer: sshSrv, + Filesystem: a.filesystem, + PatchLogs: a.client.PatchLogs, + }) go a.runLoop(ctx) } @@ -631,41 +639,28 @@ func (a *agent) run(ctx context.Context) error { } } - lifecycleState := codersdk.WorkspaceAgentLifecycleReady - scriptDone := make(chan error, 1) - err = a.trackConnGoroutine(func() { - defer close(scriptDone) - scriptDone <- a.runStartupScript(ctx, manifest.StartupScript) - }) + err = a.scriptRunner.Init(manifest.Scripts) if err != nil { - return xerrors.Errorf("track startup script: %w", err) + return xerrors.Errorf("init script runner: %w", err) } - go func() { - var timeout <-chan time.Time - // If timeout is zero, an older version of the coder - // provider was used. Otherwise a timeout is always > 0. - if manifest.StartupScriptTimeout > 0 { - t := time.NewTimer(manifest.StartupScriptTimeout) - defer t.Stop() - timeout = t.C - } - - var err error - select { - case err = <-scriptDone: - case <-timeout: - a.logger.Warn(ctx, "script timed out", slog.F("lifecycle", "startup"), slog.F("timeout", manifest.StartupScriptTimeout)) - a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleStartTimeout) - err = <-scriptDone // The script can still complete after a timeout. - } + err = a.trackConnGoroutine(func() { + err := a.scriptRunner.Execute(func(script codersdk.WorkspaceAgentScript) bool { + return script.RunOnStart + }) if err != nil { - if errors.Is(err, context.Canceled) { - return + a.logger.Warn(ctx, "startup script failed", slog.Error(err)) + if errors.Is(err, agentscripts.ErrTimeout) { + a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleStartTimeout) + } else { + a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleStartError) } - lifecycleState = codersdk.WorkspaceAgentLifecycleStartError + } else { + a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleReady) } - a.setLifecycle(ctx, lifecycleState) - }() + }) + if err != nil { + return xerrors.Errorf("track conn goroutine: %w", err) + } } // This automatically closes when the context ends! @@ -980,63 +975,48 @@ func (a *agent) runDERPMapSubscriber(ctx context.Context, network *tailnet.Conn) } } -func (a *agent) runStartupScript(ctx context.Context, script string) error { - return a.runScript(ctx, "startup", script) -} - -func (a *agent) runShutdownScript(ctx context.Context, script string) error { - return a.runScript(ctx, "shutdown", script) -} - -func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err error) { - if script == "" { +func (a *agent) runScript(ctx context.Context, script codersdk.WorkspaceAgentScript) (err error) { + if script.Script == "" { return nil } - logger := a.logger.With(slog.F("lifecycle", lifecycle)) + logger := a.logger.With(slog.F("log_source", script.LogSourceDisplayName)) - logger.Info(ctx, fmt.Sprintf("running %s script", lifecycle), slog.F("script", script)) - fileWriter, err := a.filesystem.OpenFile(filepath.Join(a.logDir, fmt.Sprintf("coder-%s-script.log", lifecycle)), os.O_CREATE|os.O_RDWR, 0o600) + logger.Info(ctx, "running script", slog.F("script", script.Script)) + fileWriter, err := a.filesystem.OpenFile(filepath.Join(a.logDir, fmt.Sprintf("coder-%s-script.log", script.LogSourceDisplayName)), os.O_CREATE|os.O_RDWR, 0o600) if err != nil { - return xerrors.Errorf("open %s script log file: %w", lifecycle, err) + return xerrors.Errorf("open %s script log file: %w", script.LogSourceDisplayName, err) } defer func() { err := fileWriter.Close() if err != nil { - logger.Warn(ctx, fmt.Sprintf("close %s script log file", lifecycle), slog.Error(err)) + logger.Warn(ctx, fmt.Sprintf("close %s script log file", script.LogSourceDisplayName), slog.Error(err)) } }() - cmdPty, err := a.sshServer.CreateCommand(ctx, script, nil) + cmdPty, err := a.sshServer.CreateCommand(ctx, script.Script, nil) if err != nil { - return xerrors.Errorf("%s script: create command: %w", lifecycle, err) + return xerrors.Errorf("%s script: create command: %w", script.LogSourceDisplayName, err) } cmd := cmdPty.AsExec() - var stdout, stderr io.Writer = fileWriter, fileWriter - if lifecycle == "startup" { - send, flushAndClose := agentsdk.LogsSender(a.client.PatchLogs, logger) - // If ctx is canceled here (or in a writer below), we may be - // discarding logs, but that's okay because we're shutting down - // anyway. We could consider creating a new context here if we - // want better control over flush during shutdown. - defer func() { - if err := flushAndClose(ctx); err != nil { - logger.Warn(ctx, "flush startup logs failed", slog.Error(err)) - } - }() - - infoW := agentsdk.StartupLogsWriter(ctx, send, codersdk.WorkspaceAgentLogSourceStartupScript, codersdk.LogLevelInfo) - defer infoW.Close() - errW := agentsdk.StartupLogsWriter(ctx, send, codersdk.WorkspaceAgentLogSourceStartupScript, codersdk.LogLevelError) - defer errW.Close() - - stdout = io.MultiWriter(fileWriter, infoW) - stderr = io.MultiWriter(fileWriter, errW) - } + send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, a.client.PatchLogs, logger) + // If ctx is canceled here (or in a writer below), we may be + // discarding logs, but that's okay because we're shutting down + // anyway. We could consider creating a new context here if we + // want better control over flush during shutdown. + defer func() { + if err := flushAndClose(ctx); err != nil { + logger.Warn(ctx, "flush startup logs failed", slog.Error(err)) + } + }() - cmd.Stdout = stdout - cmd.Stderr = stderr + infoW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelInfo) + defer infoW.Close() + errW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelError) + defer errW.Close() + cmd.Stdout = io.MultiWriter(fileWriter, infoW) + cmd.Stderr = io.MultiWriter(fileWriter, errW) start := time.Now() defer func() { @@ -1049,9 +1029,9 @@ func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err er if xerrors.As(err, &exitError) { exitCode = exitError.ExitCode() } - logger.Warn(ctx, fmt.Sprintf("%s script failed", lifecycle), slog.F("execution_time", execTime), slog.F("exit_code", exitCode), slog.Error(err)) + logger.Warn(ctx, fmt.Sprintf("%s script failed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode), slog.Error(err)) } else { - logger.Info(ctx, fmt.Sprintf("%s script completed", lifecycle), slog.F("execution_time", execTime), slog.F("exit_code", exitCode)) + logger.Info(ctx, fmt.Sprintf("%s script completed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode)) } }() @@ -1062,7 +1042,7 @@ func (a *agent) runScript(ctx context.Context, lifecycle, script string) (err er return ctx.Err() } - return xerrors.Errorf("%s script: run: %w", lifecycle, err) + return xerrors.Errorf("%s script: run: %w", script.LogSourceDisplayName, err) } return nil } @@ -1336,39 +1316,25 @@ func (a *agent) Close() error { } lifecycleState := codersdk.WorkspaceAgentLifecycleOff - if manifest := a.manifest.Load(); manifest != nil && manifest.ShutdownScript != "" { - scriptDone := make(chan error, 1) - go func() { - defer close(scriptDone) - scriptDone <- a.runShutdownScript(ctx, manifest.ShutdownScript) - }() - - var timeout <-chan time.Time - // If timeout is zero, an older version of the coder - // provider was used. Otherwise a timeout is always > 0. - if manifest.ShutdownScriptTimeout > 0 { - t := time.NewTimer(manifest.ShutdownScriptTimeout) - defer t.Stop() - timeout = t.C - } - - var err error - select { - case err = <-scriptDone: - case <-timeout: - a.logger.Warn(ctx, "script timed out", slog.F("lifecycle", "shutdown"), slog.F("timeout", manifest.ShutdownScriptTimeout)) - a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleShutdownTimeout) - err = <-scriptDone // The script can still complete after a timeout. - } - if err != nil { + err = a.scriptRunner.Execute(func(script codersdk.WorkspaceAgentScript) bool { + return script.RunOnStop + }) + if err != nil { + if errors.Is(err, agentscripts.ErrTimeout) { + lifecycleState = codersdk.WorkspaceAgentLifecycleShutdownTimeout + } else { lifecycleState = codersdk.WorkspaceAgentLifecycleShutdownError } + } else { + lifecycleState = codersdk.WorkspaceAgentLifecycleOff } - - // Set final state and wait for it to be reported because context - // cancellation will stop the report loop. a.setLifecycle(ctx, lifecycleState) + err = a.scriptRunner.Close() + if err != nil { + a.logger.Error(ctx, "script runner close", slog.Error(err)) + } + // Wait for the lifecycle to be reported, but don't wait forever so // that we don't break user expectations. ctx, cancel := context.WithTimeout(ctx, 5*time.Second) diff --git a/agent/agent_test.go b/agent/agent_test.go index 126e0f4fa4c97..35dad5f2f22f8 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -45,7 +45,6 @@ import ( "github.com/coder/coder/v2/agent" "github.com/coder/coder/v2/agent/agentssh" "github.com/coder/coder/v2/agent/agenttest" - "github.com/coder/coder/v2/coderd/httpapi" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk/agentsdk" "github.com/coder/coder/v2/pty" @@ -1055,84 +1054,6 @@ func TestAgent_SSHConnectionEnvVars(t *testing.T) { } } -func TestAgent_StartupScript(t *testing.T) { - t.Parallel() - output := "something" - command := "sh -c 'echo " + output + "'" - if runtime.GOOS == "windows" { - command = "cmd.exe /c echo " + output - } - t.Run("Success", func(t *testing.T) { - t.Parallel() - logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) - client := agenttest.NewClient(t, - logger, - uuid.New(), - agentsdk.Manifest{ - StartupScript: command, - DERPMap: &tailcfg.DERPMap{}, - }, - make(chan *agentsdk.Stats), - tailnet.NewCoordinator(logger), - ) - closer := agent.New(agent.Options{ - Client: client, - Filesystem: afero.NewMemMapFs(), - Logger: logger.Named("agent"), - ReconnectingPTYTimeout: 0, - }) - t.Cleanup(func() { - _ = closer.Close() - }) - assert.Eventually(t, func() bool { - got := client.GetLifecycleStates() - return len(got) > 0 && got[len(got)-1] == codersdk.WorkspaceAgentLifecycleReady - }, testutil.WaitShort, testutil.IntervalMedium) - - require.Len(t, client.GetStartupLogs(), 1) - require.Equal(t, output, client.GetStartupLogs()[0].Output) - }) - // This ensures that even when coderd sends back that the startup - // script has written too many lines it will still succeed! - t.Run("OverflowsAndSkips", func(t *testing.T) { - t.Parallel() - logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) - client := agenttest.NewClient(t, - logger, - uuid.New(), - agentsdk.Manifest{ - StartupScript: command, - DERPMap: &tailcfg.DERPMap{}, - }, - make(chan *agentsdk.Stats, 50), - tailnet.NewCoordinator(logger), - ) - client.PatchWorkspaceLogs = func() error { - resp := httptest.NewRecorder() - httpapi.Write(context.Background(), resp, http.StatusRequestEntityTooLarge, codersdk.Response{ - Message: "Too many lines!", - }) - res := resp.Result() - defer res.Body.Close() - return codersdk.ReadBodyAsError(res) - } - closer := agent.New(agent.Options{ - Client: client, - Filesystem: afero.NewMemMapFs(), - Logger: logger.Named("agent"), - ReconnectingPTYTimeout: 0, - }) - t.Cleanup(func() { - _ = closer.Close() - }) - assert.Eventually(t, func() bool { - got := client.GetLifecycleStates() - return len(got) > 0 && got[len(got)-1] == codersdk.WorkspaceAgentLifecycleReady - }, testutil.WaitShort, testutil.IntervalMedium) - require.Len(t, client.GetStartupLogs(), 0) - }) -} - func TestAgent_Metadata(t *testing.T) { t.Parallel() @@ -1287,8 +1208,11 @@ func TestAgent_Lifecycle(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "sleep 3", - StartupScriptTimeout: time.Nanosecond, + Scripts: []codersdk.WorkspaceAgentScript{{ + Script: "sleep 3", + Timeout: time.Nanosecond, + RunOnStart: true, + }}, }, 0) want := []codersdk.WorkspaceAgentLifecycle{ @@ -1309,8 +1233,11 @@ func TestAgent_Lifecycle(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "false", - StartupScriptTimeout: 30 * time.Second, + Scripts: []codersdk.WorkspaceAgentScript{{ + Script: "false", + Timeout: 30 * time.Second, + RunOnStart: true, + }}, }, 0) want := []codersdk.WorkspaceAgentLifecycle{ @@ -1331,8 +1258,11 @@ func TestAgent_Lifecycle(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "true", - StartupScriptTimeout: 30 * time.Second, + Scripts: []codersdk.WorkspaceAgentScript{{ + Script: "true", + Timeout: 30 * time.Second, + RunOnStart: true, + }}, }, 0) want := []codersdk.WorkspaceAgentLifecycle{ @@ -1353,8 +1283,11 @@ func TestAgent_Lifecycle(t *testing.T) { t.Parallel() _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ - ShutdownScript: "sleep 3", - StartupScriptTimeout: 30 * time.Second, + Scripts: []codersdk.WorkspaceAgentScript{{ + Script: "sleep 3", + Timeout: 30 * time.Second, + RunOnStop: true, + }}, }, 0) assert.Eventually(t, func() bool { @@ -1391,8 +1324,11 @@ func TestAgent_Lifecycle(t *testing.T) { t.Parallel() _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ - ShutdownScript: "sleep 3", - ShutdownScriptTimeout: time.Nanosecond, + Scripts: []codersdk.WorkspaceAgentScript{{ + Script: "sleep 3", + Timeout: time.Nanosecond, + RunOnStop: true, + }}, }, 0) assert.Eventually(t, func() bool { @@ -1430,8 +1366,11 @@ func TestAgent_Lifecycle(t *testing.T) { t.Parallel() _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ - ShutdownScript: "false", - ShutdownScriptTimeout: 30 * time.Second, + Scripts: []codersdk.WorkspaceAgentScript{{ + Script: "false", + Timeout: 30 * time.Second, + RunOnStop: true, + }}, }, 0) assert.Eventually(t, func() bool { @@ -1475,9 +1414,16 @@ func TestAgent_Lifecycle(t *testing.T) { logger, uuid.New(), agentsdk.Manifest{ - DERPMap: derpMap, - StartupScript: "echo 1", - ShutdownScript: "echo " + expected, + DERPMap: derpMap, + Scripts: []codersdk.WorkspaceAgentScript{{ + LogSourceDisplayName: "startup", + Script: "echo 1", + RunOnStart: true, + }, { + LogSourceDisplayName: "shutdown", + Script: "echo " + expected, + RunOnStop: true, + }}, }, make(chan *agentsdk.Stats, 50), tailnet.NewCoordinator(logger), @@ -1528,9 +1474,7 @@ func TestAgent_Startup(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "true", - StartupScriptTimeout: 30 * time.Second, - Directory: "", + Directory: "", }, 0) assert.Eventually(t, func() bool { return client.GetStartup().Version != "" @@ -1542,9 +1486,7 @@ func TestAgent_Startup(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "true", - StartupScriptTimeout: 30 * time.Second, - Directory: "~", + Directory: "~", }, 0) assert.Eventually(t, func() bool { return client.GetStartup().Version != "" @@ -1558,9 +1500,7 @@ func TestAgent_Startup(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "true", - StartupScriptTimeout: 30 * time.Second, - Directory: "coder/coder", + Directory: "coder/coder", }, 0) assert.Eventually(t, func() bool { return client.GetStartup().Version != "" @@ -1574,9 +1514,7 @@ func TestAgent_Startup(t *testing.T) { t.Parallel() _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ - StartupScript: "true", - StartupScriptTimeout: 30 * time.Second, - Directory: "$HOME", + Directory: "$HOME", }, 0) assert.Eventually(t, func() bool { return client.GetStartup().Version != "" diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go new file mode 100644 index 0000000000000..bf00ffd8bc895 --- /dev/null +++ b/agent/agentscripts/agentscripts.go @@ -0,0 +1,246 @@ +package agentscripts + +import ( + "context" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "sync" + "time" + + "github.com/robfig/cron/v3" + "github.com/spf13/afero" + "golang.org/x/sync/errgroup" + "golang.org/x/xerrors" + + "cdr.dev/slog" + "github.com/coder/coder/v2/agent/agentssh" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/codersdk/agentsdk" +) + +var ( + // ErrTimeout is returned when a script times out. + ErrTimeout = xerrors.New("script timed out") + + parser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) +) + +// Options are a set of options for the runner. +type Options struct { + LogDir string + Logger slog.Logger + SSHServer *agentssh.Server + Filesystem afero.Fs + PatchLogs func(ctx context.Context, req agentsdk.PatchLogs) error +} + +// New creates a runner for the provided scripts. +func New(ctx context.Context, opts Options) *Runner { + return &Runner{ + Options: opts, + cron: cron.New(cron.WithParser(parser)), + ctx: ctx, + closed: make(chan struct{}), + } +} + +type Runner struct { + Options + + cmdCloseWait sync.WaitGroup + closed chan struct{} + closeMutex sync.Mutex + ctx context.Context + cron *cron.Cron + scripts []codersdk.WorkspaceAgentScript +} + +// Init initializes the runner with the provided scripts. +// It also schedules any scripts that have a schedule. +// This function must be called before Execute. +func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { + r.scripts = scripts + + for _, script := range scripts { + if script.Schedule == "" { + continue + } + script := script + _, err := r.cron.AddFunc(script.Schedule, func() { + err := r.run(script) + if err != nil { + r.Logger.Warn(r.ctx, "run agent script on schedule", slog.Error(err)) + } + }) + if err != nil { + return xerrors.Errorf("add schedule: %w", err) + } + } + return nil +} + +// Execute runs a set of scripts according to a filter. +func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) error { + if filter == nil { + // Execute em' all! + filter = func(script codersdk.WorkspaceAgentScript) bool { + return true + } + } + var eg errgroup.Group + for _, script := range r.scripts { + if !filter(script) { + continue + } + script := script + eg.Go(func() error { + err := r.run(script) + if err != nil { + return xerrors.Errorf("run agent script %q: %w", script.LogSourceDisplayName, err) + } + return nil + }) + } + return eg.Wait() +} + +// run executes the provided script with the timeout. +// If the timeout is exceeded, the process is sent an interrupt signal. +// If the process does not exit after a few seconds, it is forcefully killed. +// This function immediately returns after a timeout, and does not wait for the process to exit. +func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { + logger := r.Logger.With(slog.F("log_source", script.LogSourceDisplayName)) + ctx := r.ctx + logger.Info(ctx, "running agent script", slog.F("script", script.Script)) + fileWriter, err := r.Filesystem.OpenFile(filepath.Join(r.LogDir, fmt.Sprintf("coder-%s-script.log", script.LogSourceDisplayName)), os.O_CREATE|os.O_RDWR, 0o600) + if err != nil { + return xerrors.Errorf("open %s script log file: %w", script.LogSourceDisplayName, err) + } + defer func() { + err := fileWriter.Close() + if err != nil { + logger.Warn(ctx, fmt.Sprintf("close %s script log file", script.LogSourceDisplayName), slog.Error(err)) + } + }() + + var cmd *exec.Cmd + if script.Timeout > 0 { + var cancel context.CancelFunc + // Add a buffer to forcefully kill with the context. + ctx, cancel = context.WithTimeout(ctx, script.Timeout+(3*time.Second)) + defer cancel() + } + + cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Script, nil) + if err != nil { + return xerrors.Errorf("%s script: create command: %w", script.LogSourceDisplayName, err) + } + cmd = cmdPty.AsExec() + + send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, r.PatchLogs, logger) + // If ctx is canceled here (or in a writer below), we may be + // discarding logs, but that's okay because we're shutting down + // anyway. We could consider creating a new context here if we + // want better control over flush during shutdown. + defer func() { + if err := flushAndClose(ctx); err != nil { + logger.Warn(ctx, "flush startup logs failed", slog.Error(err)) + } + }() + + infoW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelInfo) + defer infoW.Close() + errW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelError) + defer errW.Close() + cmd.Stdout = io.MultiWriter(fileWriter, infoW) + cmd.Stderr = io.MultiWriter(fileWriter, errW) + + start := time.Now() + defer func() { + end := time.Now() + execTime := end.Sub(start) + exitCode := 0 + if err != nil { + exitCode = 255 // Unknown status. + var exitError *exec.ExitError + if xerrors.As(err, &exitError) { + exitCode = exitError.ExitCode() + } + logger.Warn(ctx, fmt.Sprintf("%s script failed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode), slog.Error(err)) + } else { + logger.Info(ctx, fmt.Sprintf("%s script completed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode)) + } + }() + + err = cmd.Start() + if err != nil { + return xerrors.Errorf("%s script: start command: %w", script.LogSourceDisplayName, err) + } + + // timeout stores whether the process timed out then was gracefully killed. + var timeout chan struct{} + if script.Timeout > 0 { + timeout = make(chan struct{}) + timer := time.AfterFunc(script.Timeout, func() { + close(timeout) + err := cmd.Process.Signal(os.Interrupt) + if err != nil { + logger.Warn(ctx, "send interrupt signal to script", slog.Error(err)) + } + }) + defer timer.Stop() + } + + cmdDone := make(chan error, 1) + err = r.trackCommandGoroutine(func() { + cmdDone <- cmd.Wait() + }) + if err != nil { + return xerrors.Errorf("%s script: track command goroutine: %w", script.LogSourceDisplayName, err) + } + select { + case <-timeout: + err = ErrTimeout + case <-ctx.Done(): + err = ctx.Err() + case err = <-cmdDone: + } + return err +} + +func (r *Runner) Close() error { + r.closeMutex.Lock() + defer r.closeMutex.Unlock() + if r.isClosed() { + return nil + } + close(r.closed) + r.cmdCloseWait.Wait() + return nil +} + +func (r *Runner) trackCommandGoroutine(fn func()) error { + r.closeMutex.Lock() + defer r.closeMutex.Unlock() + if r.isClosed() { + return xerrors.New("track command goroutine: closed") + } + r.cmdCloseWait.Add(1) + go func() { + defer r.cmdCloseWait.Done() + fn() + }() + return nil +} + +func (r *Runner) isClosed() bool { + select { + case <-r.closed: + return true + default: + return false + } +} diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go new file mode 100644 index 0000000000000..4521484966ebd --- /dev/null +++ b/agent/agentscripts/agentscripts_test.go @@ -0,0 +1,82 @@ +package agentscripts_test + +import ( + "context" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/spf13/afero" + "github.com/stretchr/testify/require" + "go.uber.org/atomic" + "go.uber.org/goleak" + + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/agent/agentscripts" + "github.com/coder/coder/v2/agent/agentssh" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/codersdk/agentsdk" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + +func TestExecuteBasic(t *testing.T) { + t.Parallel() + logs := make(chan agentsdk.PatchLogs, 1) + runner := setup(t, func(ctx context.Context, req agentsdk.PatchLogs) error { + logs <- req + return nil + }) + defer runner.Close() + err := runner.Init([]codersdk.WorkspaceAgentScript{{ + LogSourceDisplayName: "test", + Script: "echo hello", + }}) + require.NoError(t, err) + require.NoError(t, runner.Execute(func(script codersdk.WorkspaceAgentScript) bool { + return true + })) + log := <-logs + require.Equal(t, "hello", log.Logs[0].Output) +} + +func TestTimeout(t *testing.T) { + t.Parallel() + runner := setup(t, nil) + defer runner.Close() + err := runner.Init([]codersdk.WorkspaceAgentScript{{ + Script: "sleep 3", + Timeout: time.Nanosecond, + }}) + require.NoError(t, err) + require.ErrorIs(t, runner.Execute(nil), agentscripts.ErrTimeout) +} + +func setup(t *testing.T, patchLogs func(ctx context.Context, req agentsdk.PatchLogs) error) *agentscripts.Runner { + t.Helper() + if patchLogs == nil { + // noop + patchLogs = func(ctx context.Context, req agentsdk.PatchLogs) error { + return nil + } + } + fs := afero.NewMemMapFs() + ctx := context.Background() + logger := slogtest.Make(t, nil) + s, err := agentssh.NewServer(ctx, logger, prometheus.NewRegistry(), fs, 0, "") + require.NoError(t, err) + s.AgentToken = func() string { return "" } + s.Manifest = atomic.NewPointer(&agentsdk.Manifest{}) + t.Cleanup(func() { + _ = s.Close() + }) + return agentscripts.New(ctx, agentscripts.Options{ + LogDir: t.TempDir(), + Logger: logger, + SSHServer: s, + Filesystem: fs, + PatchLogs: patchLogs, + }) +} diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index e4b802092f03d..7feb81bdf934b 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -2031,6 +2031,10 @@ func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertW return q.db.InsertWorkspaceAgent(ctx, arg) } +func (q *querier) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { + panic("not implemented") +} + func (q *querier) InsertWorkspaceAgentLogs(ctx context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { return q.db.InsertWorkspaceAgentLogs(ctx, arg) } diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 9ba29ddf6d682..b814492184438 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -4420,15 +4420,12 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser Architecture: arg.Architecture, OperatingSystem: arg.OperatingSystem, Directory: arg.Directory, - StartupScriptBehavior: arg.StartupScriptBehavior, - StartupScript: arg.StartupScript, InstanceMetadata: arg.InstanceMetadata, ResourceMetadata: arg.ResourceMetadata, ConnectionTimeoutSeconds: arg.ConnectionTimeoutSeconds, TroubleshootingURL: arg.TroubleshootingURL, MOTDFile: arg.MOTDFile, LifecycleState: database.WorkspaceAgentLifecycleStateCreated, - ShutdownScript: arg.ShutdownScript, DisplayApps: arg.DisplayApps, } @@ -4436,6 +4433,15 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser return agent, nil } +func (q *FakeQuerier) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { + err := validateDatabaseType(arg) + if err != nil { + return nil, err + } + + panic("not implemented") +} + func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { if err := validateDatabaseType(arg); err != nil { return nil, err @@ -4453,12 +4459,12 @@ func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.I for index, output := range arg.Output { id++ logs = append(logs, database.WorkspaceAgentLog{ - ID: id, - AgentID: arg.AgentID, - CreatedAt: arg.CreatedAt[index], - Level: arg.Level[index], - Source: arg.Source[index], - Output: output, + ID: id, + AgentID: arg.AgentID, + CreatedAt: arg.CreatedAt[index], + Level: arg.Level[index], + LogSourceID: arg.LogSourceID, + Output: output, }) outputLength += int32(len(output)) } diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 12036274eb811..7057255116401 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -140,11 +140,7 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen Valid: takeFirst(orig.EnvironmentVariables.Valid, false), }, OperatingSystem: takeFirst(orig.OperatingSystem, "linux"), - StartupScript: sql.NullString{ - String: takeFirst(orig.StartupScript.String, ""), - Valid: takeFirst(orig.StartupScript.Valid, false), - }, - Directory: takeFirst(orig.Directory, ""), + Directory: takeFirst(orig.Directory, ""), InstanceMetadata: pqtype.NullRawMessage{ RawMessage: takeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), Valid: takeFirst(orig.ResourceMetadata.Valid, false), @@ -153,11 +149,9 @@ func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgen RawMessage: takeFirstSlice(orig.ResourceMetadata.RawMessage, []byte("{}")), Valid: takeFirst(orig.ResourceMetadata.Valid, false), }, - ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600), - TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"), - MOTDFile: takeFirst(orig.TroubleshootingURL, ""), - StartupScriptBehavior: takeFirst(orig.StartupScriptBehavior, "non-blocking"), - StartupScriptTimeoutSeconds: takeFirst(orig.StartupScriptTimeoutSeconds, 3600), + ConnectionTimeoutSeconds: takeFirst(orig.ConnectionTimeoutSeconds, 3600), + TroubleshootingURL: takeFirst(orig.TroubleshootingURL, "https://example.com"), + MOTDFile: takeFirst(orig.TroubleshootingURL, ""), }) require.NoError(t, err, "insert workspace agent") return workspace @@ -180,6 +174,18 @@ func Workspace(t testing.TB, db database.Store, orig database.Workspace) databas return workspace } +func WorkspaceAgentLogSource(t testing.TB, db database.Store, orig database.WorkspaceAgentLogSource) database.WorkspaceAgentLogSource { + sources, err := db.InsertWorkspaceAgentLogSources(genCtx, database.InsertWorkspaceAgentLogSourcesParams{ + WorkspaceAgentID: takeFirst(orig.WorkspaceAgentID, uuid.New()), + ID: []uuid.UUID{takeFirst(orig.ID, uuid.New())}, + CreatedAt: []time.Time{takeFirst(orig.CreatedAt, dbtime.Now())}, + DisplayName: []string{takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1))}, + Icon: []string{takeFirst(orig.Icon, namesgenerator.GetRandomName(1))}, + }) + require.NoError(t, err, "insert workspace agent log source") + return sources[0] +} + func WorkspaceBuild(t testing.TB, db database.Store, orig database.WorkspaceBuild) database.WorkspaceBuild { buildID := takeFirst(orig.ID, uuid.New()) var build database.WorkspaceBuild diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 8526eb4da1078..2850551904d84 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -1236,6 +1236,13 @@ func (m metricsStore) InsertWorkspaceAgent(ctx context.Context, arg database.Ins return agent, err } +func (m metricsStore) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { + start := time.Now() + r0, r1 := m.s.InsertWorkspaceAgentLogSources(ctx, arg) + m.queryLatencies.WithLabelValues("InsertWorkspaceAgentLogSources").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) InsertWorkspaceAgentLogs(ctx context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { start := time.Now() r0, r1 := m.s.InsertWorkspaceAgentLogs(ctx, arg) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 48e4e1a8862c1..c0ff45acb9147 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -144,15 +144,6 @@ CREATE TYPE workspace_agent_lifecycle_state AS ENUM ( 'off' ); -CREATE TYPE workspace_agent_log_source AS ENUM ( - 'startup_script', - 'shutdown_script', - 'kubernetes_logs', - 'envbox', - 'envbuilder', - 'external' -); - CREATE TYPE workspace_agent_subsystem AS ENUM ( 'envbuilder', 'envbox', @@ -704,13 +695,21 @@ CREATE TABLE user_links ( oauth_expiry timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL ); -CREATE TABLE workspace_agent_logs ( +CREATE TABLE workspace_agent_log_sources ( + workspace_agent_id uuid NOT NULL, + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + display_name character varying(127) NOT NULL, + icon text NOT NULL +); + +CREATE UNLOGGED TABLE workspace_agent_logs ( agent_id uuid NOT NULL, created_at timestamp with time zone NOT NULL, output character varying(1024) NOT NULL, id bigint NOT NULL, level log_level DEFAULT 'info'::log_level NOT NULL, - source workspace_agent_log_source DEFAULT 'startup_script'::workspace_agent_log_source NOT NULL + log_source_id uuid NOT NULL ); CREATE UNLOGGED TABLE workspace_agent_metadata ( @@ -725,6 +724,19 @@ CREATE UNLOGGED TABLE workspace_agent_metadata ( collected_at timestamp with time zone DEFAULT '0001-01-01 00:00:00+00'::timestamp with time zone NOT NULL ); +CREATE TABLE workspace_agent_scripts ( + workspace_agent_id uuid NOT NULL, + log_source_id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + script text NOT NULL, + schedule text NOT NULL, + login_before_ready boolean NOT NULL, + run_on_start boolean NOT NULL, + run_on_stop boolean NOT NULL, + name character varying(127) NOT NULL, + description text NOT NULL +); + CREATE SEQUENCE workspace_agent_startup_logs_id_seq START WITH 1 INCREMENT BY 1 @@ -768,7 +780,6 @@ CREATE TABLE workspace_agents ( architecture character varying(64) NOT NULL, environment_variables jsonb, operating_system character varying(64) NOT NULL, - startup_script character varying(65534), instance_metadata jsonb, resource_metadata jsonb, directory character varying(4096) DEFAULT ''::character varying NOT NULL, @@ -778,13 +789,9 @@ CREATE TABLE workspace_agents ( troubleshooting_url text DEFAULT ''::text NOT NULL, motd_file text DEFAULT ''::text NOT NULL, lifecycle_state workspace_agent_lifecycle_state DEFAULT 'created'::workspace_agent_lifecycle_state NOT NULL, - startup_script_timeout_seconds integer DEFAULT 0 NOT NULL, expanded_directory character varying(4096) DEFAULT ''::character varying NOT NULL, - shutdown_script character varying(65534), - shutdown_script_timeout_seconds integer DEFAULT 0 NOT NULL, logs_length integer DEFAULT 0 NOT NULL, logs_overflowed boolean DEFAULT false NOT NULL, - startup_script_behavior startup_script_behavior DEFAULT 'non-blocking'::startup_script_behavior NOT NULL, started_at timestamp with time zone, ready_at timestamp with time zone, subsystems workspace_agent_subsystem[] DEFAULT '{}'::workspace_agent_subsystem[], @@ -803,20 +810,12 @@ COMMENT ON COLUMN workspace_agents.motd_file IS 'Path to file inside workspace c COMMENT ON COLUMN workspace_agents.lifecycle_state IS 'The current lifecycle state reported by the workspace agent.'; -COMMENT ON COLUMN workspace_agents.startup_script_timeout_seconds IS 'The number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout.'; - COMMENT ON COLUMN workspace_agents.expanded_directory IS 'The resolved path of a user-specified directory. e.g. ~/coder -> /home/coder/coder'; -COMMENT ON COLUMN workspace_agents.shutdown_script IS 'Script that is executed before the agent is stopped.'; - -COMMENT ON COLUMN workspace_agents.shutdown_script_timeout_seconds IS 'The number of seconds to wait for the shutdown script to complete. If the script does not complete within this time, the agent lifecycle will be marked as shutdown_timeout.'; - COMMENT ON COLUMN workspace_agents.logs_length IS 'Total length of startup logs'; COMMENT ON COLUMN workspace_agents.logs_overflowed IS 'Whether the startup logs overflowed in length'; -COMMENT ON COLUMN workspace_agents.startup_script_behavior IS 'When startup script behavior is non-blocking, the workspace will be ready and accessible upon agent connection, when it is blocking, workspace will wait for the startup script to complete before becoming ready and accessible.'; - COMMENT ON COLUMN workspace_agents.started_at IS 'The time the agent entered the starting lifecycle state'; COMMENT ON COLUMN workspace_agents.ready_at IS 'The time the agent entered the ready or start_error lifecycle state'; @@ -1127,6 +1126,9 @@ ALTER TABLE ONLY user_links ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); +ALTER TABLE ONLY workspace_agent_log_sources + ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); + ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key); diff --git a/coderd/database/migrations/000154_workspace_agent_script.up.sql b/coderd/database/migrations/000154_workspace_agent_script.up.sql index b84a9d1ef6cbf..434389185150d 100644 --- a/coderd/database/migrations/000154_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000154_workspace_agent_script.up.sql @@ -1,5 +1,5 @@ BEGIN; -CREATE TABLE workspace_agent_log_source ( +CREATE TABLE workspace_agent_log_sources ( workspace_agent_id uuid NOT NULL, id uuid NOT NULL, created_at timestamptz NOT NULL, @@ -8,18 +8,29 @@ CREATE TABLE workspace_agent_log_source ( PRIMARY KEY (workspace_agent_id, id) ); -CREATE TABLE workspace_agent_script ( +CREATE TABLE workspace_agent_scripts ( workspace_agent_id uuid NOT NULL, log_source_id uuid NOT NULL, created_at timestamptz NOT NULL, script text NOT NULL, schedule text NOT NULL, login_before_ready boolean NOT NULL, + run_on_start boolean NOT NULL, + run_on_stop boolean NOT NULL, name varchar(127) NOT NULL, - description text NOT NULL, - PRIMARY KEY (workspace_agent_id, id) + description text NOT NULL ); +ALTER TABLE workspace_agent_logs ADD COLUMN log_source_id uuid NOT NULL; +ALTER TABLE workspace_agent_logs DROP COLUMN source; +DROP TYPE workspace_agent_log_source; + +ALTER TABLE workspace_agents DROP COLUMN startup_script_timeout_seconds; +ALTER TABLE workspace_agents DROP COLUMN shutdown_script; +ALTER TABLE workspace_agents DROP COLUMN shutdown_script_timeout_seconds; +ALTER TABLE workspace_agents DROP COLUMN startup_script_behavior; +ALTER TABLE workspace_agents DROP COLUMN startup_script; + -- Set the table to unlogged to speed up the inserts ALTER TABLE workspace_agent_logs SET UNLOGGED; COMMIT; diff --git a/coderd/database/models.go b/coderd/database/models.go index cd9ba8f990d2d..9ef86bc4d6c13 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1298,76 +1298,6 @@ func AllWorkspaceAgentLifecycleStateValues() []WorkspaceAgentLifecycleState { } } -type WorkspaceAgentLogSource string - -const ( - WorkspaceAgentLogSourceStartupScript WorkspaceAgentLogSource = "startup_script" - WorkspaceAgentLogSourceShutdownScript WorkspaceAgentLogSource = "shutdown_script" - WorkspaceAgentLogSourceKubernetesLogs WorkspaceAgentLogSource = "kubernetes_logs" - WorkspaceAgentLogSourceEnvbox WorkspaceAgentLogSource = "envbox" - WorkspaceAgentLogSourceEnvbuilder WorkspaceAgentLogSource = "envbuilder" - WorkspaceAgentLogSourceExternal WorkspaceAgentLogSource = "external" -) - -func (e *WorkspaceAgentLogSource) Scan(src interface{}) error { - switch s := src.(type) { - case []byte: - *e = WorkspaceAgentLogSource(s) - case string: - *e = WorkspaceAgentLogSource(s) - default: - return fmt.Errorf("unsupported scan type for WorkspaceAgentLogSource: %T", src) - } - return nil -} - -type NullWorkspaceAgentLogSource struct { - WorkspaceAgentLogSource WorkspaceAgentLogSource `json:"workspace_agent_log_source"` - Valid bool `json:"valid"` // Valid is true if WorkspaceAgentLogSource is not NULL -} - -// Scan implements the Scanner interface. -func (ns *NullWorkspaceAgentLogSource) Scan(value interface{}) error { - if value == nil { - ns.WorkspaceAgentLogSource, ns.Valid = "", false - return nil - } - ns.Valid = true - return ns.WorkspaceAgentLogSource.Scan(value) -} - -// Value implements the driver Valuer interface. -func (ns NullWorkspaceAgentLogSource) Value() (driver.Value, error) { - if !ns.Valid { - return nil, nil - } - return string(ns.WorkspaceAgentLogSource), nil -} - -func (e WorkspaceAgentLogSource) Valid() bool { - switch e { - case WorkspaceAgentLogSourceStartupScript, - WorkspaceAgentLogSourceShutdownScript, - WorkspaceAgentLogSourceKubernetesLogs, - WorkspaceAgentLogSourceEnvbox, - WorkspaceAgentLogSourceEnvbuilder, - WorkspaceAgentLogSourceExternal: - return true - } - return false -} - -func AllWorkspaceAgentLogSourceValues() []WorkspaceAgentLogSource { - return []WorkspaceAgentLogSource{ - WorkspaceAgentLogSourceStartupScript, - WorkspaceAgentLogSourceShutdownScript, - WorkspaceAgentLogSourceKubernetesLogs, - WorkspaceAgentLogSourceEnvbox, - WorkspaceAgentLogSourceEnvbuilder, - WorkspaceAgentLogSourceExternal, - } -} - type WorkspaceAgentSubsystem string const ( @@ -1988,7 +1918,6 @@ type WorkspaceAgent struct { Architecture string `db:"architecture" json:"architecture"` EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` OperatingSystem string `db:"operating_system" json:"operating_system"` - StartupScript sql.NullString `db:"startup_script" json:"startup_script"` InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` Directory string `db:"directory" json:"directory"` @@ -2003,20 +1932,12 @@ type WorkspaceAgent struct { MOTDFile string `db:"motd_file" json:"motd_file"` // The current lifecycle state reported by the workspace agent. LifecycleState WorkspaceAgentLifecycleState `db:"lifecycle_state" json:"lifecycle_state"` - // The number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. - StartupScriptTimeoutSeconds int32 `db:"startup_script_timeout_seconds" json:"startup_script_timeout_seconds"` // The resolved path of a user-specified directory. e.g. ~/coder -> /home/coder/coder ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"` - // Script that is executed before the agent is stopped. - ShutdownScript sql.NullString `db:"shutdown_script" json:"shutdown_script"` - // The number of seconds to wait for the shutdown script to complete. If the script does not complete within this time, the agent lifecycle will be marked as shutdown_timeout. - ShutdownScriptTimeoutSeconds int32 `db:"shutdown_script_timeout_seconds" json:"shutdown_script_timeout_seconds"` // Total length of startup logs LogsLength int32 `db:"logs_length" json:"logs_length"` // Whether the startup logs overflowed in length LogsOverflowed bool `db:"logs_overflowed" json:"logs_overflowed"` - // When startup script behavior is non-blocking, the workspace will be ready and accessible upon agent connection, when it is blocking, workspace will wait for the startup script to complete before becoming ready and accessible. - StartupScriptBehavior StartupScriptBehavior `db:"startup_script_behavior" json:"startup_script_behavior"` // The time the agent entered the starting lifecycle state StartedAt sql.NullTime `db:"started_at" json:"started_at"` // The time the agent entered the ready or start_error lifecycle state @@ -2026,12 +1947,20 @@ type WorkspaceAgent struct { } type WorkspaceAgentLog struct { - AgentID uuid.UUID `db:"agent_id" json:"agent_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - Output string `db:"output" json:"output"` - ID int64 `db:"id" json:"id"` - Level LogLevel `db:"level" json:"level"` - Source WorkspaceAgentLogSource `db:"source" json:"source"` + AgentID uuid.UUID `db:"agent_id" json:"agent_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Output string `db:"output" json:"output"` + ID int64 `db:"id" json:"id"` + Level LogLevel `db:"level" json:"level"` + LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` +} + +type WorkspaceAgentLogSource struct { + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + DisplayName string `db:"display_name" json:"display_name"` + Icon string `db:"icon" json:"icon"` } type WorkspaceAgentMetadatum struct { @@ -2046,6 +1975,19 @@ type WorkspaceAgentMetadatum struct { CollectedAt time.Time `db:"collected_at" json:"collected_at"` } +type WorkspaceAgentScript struct { + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Script string `db:"script" json:"script"` + Schedule string `db:"schedule" json:"schedule"` + LoginBeforeReady bool `db:"login_before_ready" json:"login_before_ready"` + RunOnStart bool `db:"run_on_start" json:"run_on_start"` + RunOnStop bool `db:"run_on_stop" json:"run_on_stop"` + Name string `db:"name" json:"name"` + Description string `db:"description" json:"description"` +} + type WorkspaceAgentStat struct { ID uuid.UUID `db:"id" json:"id"` CreatedAt time.Time `db:"created_at" json:"created_at"` diff --git a/coderd/database/querier_test.go b/coderd/database/querier_test.go index 2e3ceeb95b644..48f16cf4059b4 100644 --- a/coderd/database/querier_test.go +++ b/coderd/database/querier_test.go @@ -92,48 +92,50 @@ func TestGetDeploymentWorkspaceAgentStats(t *testing.T) { }) } -func TestInsertWorkspaceAgentLogs(t *testing.T) { - t.Parallel() - if testing.Short() { - t.SkipNow() - } - sqlDB := testSQLDB(t) - ctx := context.Background() - err := migrations.Up(sqlDB) - require.NoError(t, err) - db := database.New(sqlDB) - org := dbgen.Organization(t, db, database.Organization{}) - job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{ - OrganizationID: org.ID, - }) - resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ - JobID: job.ID, - }) - agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ - ResourceID: resource.ID, - }) - logs, err := db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ - AgentID: agent.ID, - CreatedAt: []time.Time{dbtime.Now()}, - Output: []string{"first"}, - Level: []database.LogLevel{database.LogLevelInfo}, - Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal}, - // 1 MB is the max - OutputLength: 1 << 20, - }) - require.NoError(t, err) - require.Equal(t, int64(1), logs[0].ID) - - _, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ - AgentID: agent.ID, - CreatedAt: []time.Time{dbtime.Now()}, - Output: []string{"second"}, - Level: []database.LogLevel{database.LogLevelInfo}, - Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal}, - OutputLength: 1, - }) - require.True(t, database.IsWorkspaceAgentLogsLimitError(err)) -} +// func TestInsertWorkspaceAgentLogs(t *testing.T) { +// t.Parallel() +// if testing.Short() { +// t.SkipNow() +// } +// sqlDB := testSQLDB(t) +// ctx := context.Background() +// err := migrations.Up(sqlDB) +// require.NoError(t, err) +// db := database.New(sqlDB) +// org := dbgen.Organization(t, db, database.Organization{}) +// job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{ +// OrganizationID: org.ID, +// }) +// resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ +// JobID: job.ID, +// }) +// agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ +// ResourceID: resource.ID, +// }) +// source := dbgen.WorkspaceAgentLogSource(t, db, database.WorkspaceAgentLogSource{}) +// logs, err := db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ +// AgentID: agent.ID, +// CreatedAt: []time.Time{dbtime.Now()}, +// Output: []string{"first"}, +// Level: []database.LogLevel{database.LogLevelInfo}, +// LogSourceID: uuid.New(), +// Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal}, +// // 1 MB is the max +// OutputLength: 1 << 20, +// }) +// require.NoError(t, err) +// require.Equal(t, int64(1), logs[0].ID) + +// _, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ +// AgentID: agent.ID, +// CreatedAt: []time.Time{dbtime.Now()}, +// Output: []string{"second"}, +// Level: []database.LogLevel{database.LogLevelInfo}, +// Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal}, +// OutputLength: 1, +// }) +// require.True(t, database.IsWorkspaceAgentLogsLimitError(err)) +// } func TestProxyByHostname(t *testing.T) { t.Parallel() diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index dbbb5d4085e93..b153c88d8bfa2 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -6362,7 +6362,7 @@ func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context) error { const getWorkspaceAgentAndOwnerByAuthToken = `-- name: GetWorkspaceAgentAndOwnerByAuthToken :one SELECT - workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.startup_script, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, + workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspaces.id AS workspace_id, users.id AS owner_id, users.username AS owner_name, @@ -6441,7 +6441,6 @@ func (q *sqlQuerier) GetWorkspaceAgentAndOwnerByAuthToken(ctx context.Context, a &i.WorkspaceAgent.Architecture, &i.WorkspaceAgent.EnvironmentVariables, &i.WorkspaceAgent.OperatingSystem, - &i.WorkspaceAgent.StartupScript, &i.WorkspaceAgent.InstanceMetadata, &i.WorkspaceAgent.ResourceMetadata, &i.WorkspaceAgent.Directory, @@ -6451,13 +6450,9 @@ func (q *sqlQuerier) GetWorkspaceAgentAndOwnerByAuthToken(ctx context.Context, a &i.WorkspaceAgent.TroubleshootingURL, &i.WorkspaceAgent.MOTDFile, &i.WorkspaceAgent.LifecycleState, - &i.WorkspaceAgent.StartupScriptTimeoutSeconds, &i.WorkspaceAgent.ExpandedDirectory, - &i.WorkspaceAgent.ShutdownScript, - &i.WorkspaceAgent.ShutdownScriptTimeoutSeconds, &i.WorkspaceAgent.LogsLength, &i.WorkspaceAgent.LogsOverflowed, - &i.WorkspaceAgent.StartupScriptBehavior, &i.WorkspaceAgent.StartedAt, &i.WorkspaceAgent.ReadyAt, pq.Array(&i.WorkspaceAgent.Subsystems), @@ -6474,7 +6469,7 @@ func (q *sqlQuerier) GetWorkspaceAgentAndOwnerByAuthToken(ctx context.Context, a const getWorkspaceAgentByID = `-- name: GetWorkspaceAgentByID :one SELECT - id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems, display_apps + id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps FROM workspace_agents WHERE @@ -6498,7 +6493,6 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W &i.Architecture, &i.EnvironmentVariables, &i.OperatingSystem, - &i.StartupScript, &i.InstanceMetadata, &i.ResourceMetadata, &i.Directory, @@ -6508,13 +6502,9 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W &i.TroubleshootingURL, &i.MOTDFile, &i.LifecycleState, - &i.StartupScriptTimeoutSeconds, &i.ExpandedDirectory, - &i.ShutdownScript, - &i.ShutdownScriptTimeoutSeconds, &i.LogsLength, &i.LogsOverflowed, - &i.StartupScriptBehavior, &i.StartedAt, &i.ReadyAt, pq.Array(&i.Subsystems), @@ -6525,7 +6515,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W const getWorkspaceAgentByInstanceID = `-- name: GetWorkspaceAgentByInstanceID :one SELECT - id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems, display_apps + id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps FROM workspace_agents WHERE @@ -6551,7 +6541,6 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst &i.Architecture, &i.EnvironmentVariables, &i.OperatingSystem, - &i.StartupScript, &i.InstanceMetadata, &i.ResourceMetadata, &i.Directory, @@ -6561,13 +6550,9 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst &i.TroubleshootingURL, &i.MOTDFile, &i.LifecycleState, - &i.StartupScriptTimeoutSeconds, &i.ExpandedDirectory, - &i.ShutdownScript, - &i.ShutdownScriptTimeoutSeconds, &i.LogsLength, &i.LogsOverflowed, - &i.StartupScriptBehavior, &i.StartedAt, &i.ReadyAt, pq.Array(&i.Subsystems), @@ -6602,7 +6587,7 @@ func (q *sqlQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id const getWorkspaceAgentLogsAfter = `-- name: GetWorkspaceAgentLogsAfter :many SELECT - agent_id, created_at, output, id, level, source + agent_id, created_at, output, id, level, log_source_id FROM workspace_agent_logs WHERE @@ -6632,7 +6617,7 @@ func (q *sqlQuerier) GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWork &i.Output, &i.ID, &i.Level, - &i.Source, + &i.LogSourceID, ); err != nil { return nil, err } @@ -6691,7 +6676,7 @@ func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAge const getWorkspaceAgentsByResourceIDs = `-- name: GetWorkspaceAgentsByResourceIDs :many SELECT - id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems, display_apps + id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps FROM workspace_agents WHERE @@ -6721,7 +6706,6 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids [] &i.Architecture, &i.EnvironmentVariables, &i.OperatingSystem, - &i.StartupScript, &i.InstanceMetadata, &i.ResourceMetadata, &i.Directory, @@ -6731,13 +6715,9 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids [] &i.TroubleshootingURL, &i.MOTDFile, &i.LifecycleState, - &i.StartupScriptTimeoutSeconds, &i.ExpandedDirectory, - &i.ShutdownScript, - &i.ShutdownScriptTimeoutSeconds, &i.LogsLength, &i.LogsOverflowed, - &i.StartupScriptBehavior, &i.StartedAt, &i.ReadyAt, pq.Array(&i.Subsystems), @@ -6757,7 +6737,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids [] } const getWorkspaceAgentsCreatedAfter = `-- name: GetWorkspaceAgentsCreatedAfter :many -SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems, display_apps FROM workspace_agents WHERE created_at > $1 +SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps FROM workspace_agents WHERE created_at > $1 ` func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) { @@ -6783,7 +6763,6 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created &i.Architecture, &i.EnvironmentVariables, &i.OperatingSystem, - &i.StartupScript, &i.InstanceMetadata, &i.ResourceMetadata, &i.Directory, @@ -6793,13 +6772,9 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created &i.TroubleshootingURL, &i.MOTDFile, &i.LifecycleState, - &i.StartupScriptTimeoutSeconds, &i.ExpandedDirectory, - &i.ShutdownScript, - &i.ShutdownScriptTimeoutSeconds, &i.LogsLength, &i.LogsOverflowed, - &i.StartupScriptBehavior, &i.StartedAt, &i.ReadyAt, pq.Array(&i.Subsystems), @@ -6820,7 +6795,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created const getWorkspaceAgentsInLatestBuildByWorkspaceID = `-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many SELECT - workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.startup_script, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps + workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps FROM workspace_agents JOIN @@ -6862,7 +6837,6 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co &i.Architecture, &i.EnvironmentVariables, &i.OperatingSystem, - &i.StartupScript, &i.InstanceMetadata, &i.ResourceMetadata, &i.Directory, @@ -6872,13 +6846,9 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co &i.TroubleshootingURL, &i.MOTDFile, &i.LifecycleState, - &i.StartupScriptTimeoutSeconds, &i.ExpandedDirectory, - &i.ShutdownScript, - &i.ShutdownScriptTimeoutSeconds, &i.LogsLength, &i.LogsOverflowed, - &i.StartupScriptBehavior, &i.StartedAt, &i.ReadyAt, pq.Array(&i.Subsystems), @@ -6910,46 +6880,36 @@ INSERT INTO architecture, environment_variables, operating_system, - startup_script, directory, instance_metadata, resource_metadata, connection_timeout_seconds, troubleshooting_url, motd_file, - startup_script_behavior, - startup_script_timeout_seconds, - shutdown_script, - shutdown_script_timeout_seconds, display_apps ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems, display_apps + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps ` type InsertWorkspaceAgentParams struct { - ID uuid.UUID `db:"id" json:"id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UpdatedAt time.Time `db:"updated_at" json:"updated_at"` - Name string `db:"name" json:"name"` - ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` - AuthToken uuid.UUID `db:"auth_token" json:"auth_token"` - AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"` - Architecture string `db:"architecture" json:"architecture"` - EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` - OperatingSystem string `db:"operating_system" json:"operating_system"` - StartupScript sql.NullString `db:"startup_script" json:"startup_script"` - Directory string `db:"directory" json:"directory"` - InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` - ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` - ConnectionTimeoutSeconds int32 `db:"connection_timeout_seconds" json:"connection_timeout_seconds"` - TroubleshootingURL string `db:"troubleshooting_url" json:"troubleshooting_url"` - MOTDFile string `db:"motd_file" json:"motd_file"` - StartupScriptBehavior StartupScriptBehavior `db:"startup_script_behavior" json:"startup_script_behavior"` - StartupScriptTimeoutSeconds int32 `db:"startup_script_timeout_seconds" json:"startup_script_timeout_seconds"` - ShutdownScript sql.NullString `db:"shutdown_script" json:"shutdown_script"` - ShutdownScriptTimeoutSeconds int32 `db:"shutdown_script_timeout_seconds" json:"shutdown_script_timeout_seconds"` - DisplayApps []DisplayApp `db:"display_apps" json:"display_apps"` + ID uuid.UUID `db:"id" json:"id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + UpdatedAt time.Time `db:"updated_at" json:"updated_at"` + Name string `db:"name" json:"name"` + ResourceID uuid.UUID `db:"resource_id" json:"resource_id"` + AuthToken uuid.UUID `db:"auth_token" json:"auth_token"` + AuthInstanceID sql.NullString `db:"auth_instance_id" json:"auth_instance_id"` + Architecture string `db:"architecture" json:"architecture"` + EnvironmentVariables pqtype.NullRawMessage `db:"environment_variables" json:"environment_variables"` + OperatingSystem string `db:"operating_system" json:"operating_system"` + Directory string `db:"directory" json:"directory"` + InstanceMetadata pqtype.NullRawMessage `db:"instance_metadata" json:"instance_metadata"` + ResourceMetadata pqtype.NullRawMessage `db:"resource_metadata" json:"resource_metadata"` + ConnectionTimeoutSeconds int32 `db:"connection_timeout_seconds" json:"connection_timeout_seconds"` + TroubleshootingURL string `db:"troubleshooting_url" json:"troubleshooting_url"` + MOTDFile string `db:"motd_file" json:"motd_file"` + DisplayApps []DisplayApp `db:"display_apps" json:"display_apps"` } func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) { @@ -6964,17 +6924,12 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa arg.Architecture, arg.EnvironmentVariables, arg.OperatingSystem, - arg.StartupScript, arg.Directory, arg.InstanceMetadata, arg.ResourceMetadata, arg.ConnectionTimeoutSeconds, arg.TroubleshootingURL, arg.MOTDFile, - arg.StartupScriptBehavior, - arg.StartupScriptTimeoutSeconds, - arg.ShutdownScript, - arg.ShutdownScriptTimeoutSeconds, pq.Array(arg.DisplayApps), ) var i WorkspaceAgent @@ -6992,7 +6947,6 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa &i.Architecture, &i.EnvironmentVariables, &i.OperatingSystem, - &i.StartupScript, &i.InstanceMetadata, &i.ResourceMetadata, &i.Directory, @@ -7002,13 +6956,9 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa &i.TroubleshootingURL, &i.MOTDFile, &i.LifecycleState, - &i.StartupScriptTimeoutSeconds, &i.ExpandedDirectory, - &i.ShutdownScript, - &i.ShutdownScriptTimeoutSeconds, &i.LogsLength, &i.LogsOverflowed, - &i.StartupScriptBehavior, &i.StartedAt, &i.ReadyAt, pq.Array(&i.Subsystems), @@ -7017,6 +6967,61 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa return i, err } +const insertWorkspaceAgentLogSources = `-- name: InsertWorkspaceAgentLogSources :many +INSERT INTO + workspace_agent_log_sources (workspace_agent_id, id, created_at, display_name, icon) + SELECT + $1 :: uuid AS workspace_agent_id, + unnest($2 :: uuid [ ]) AS id, + unnest($3 :: timestamptz [ ]) AS created_at, + unnest($4 :: VARCHAR(127) [ ]) AS display_name, + unnest($5 :: text [ ]) AS icon + RETURNING workspace_agent_log_sources.workspace_agent_id, workspace_agent_log_sources.id, workspace_agent_log_sources.created_at, workspace_agent_log_sources.display_name, workspace_agent_log_sources.icon +` + +type InsertWorkspaceAgentLogSourcesParams struct { + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + ID []uuid.UUID `db:"id" json:"id"` + CreatedAt []time.Time `db:"created_at" json:"created_at"` + DisplayName []string `db:"display_name" json:"display_name"` + Icon []string `db:"icon" json:"icon"` +} + +func (q *sqlQuerier) InsertWorkspaceAgentLogSources(ctx context.Context, arg InsertWorkspaceAgentLogSourcesParams) ([]WorkspaceAgentLogSource, error) { + rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentLogSources, + arg.WorkspaceAgentID, + pq.Array(arg.ID), + pq.Array(arg.CreatedAt), + pq.Array(arg.DisplayName), + pq.Array(arg.Icon), + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentLogSource + for rows.Next() { + var i WorkspaceAgentLogSource + if err := rows.Scan( + &i.WorkspaceAgentID, + &i.ID, + &i.CreatedAt, + &i.DisplayName, + &i.Icon, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const insertWorkspaceAgentLogs = `-- name: InsertWorkspaceAgentLogs :many WITH new_length AS ( UPDATE workspace_agents SET @@ -7029,17 +7034,17 @@ INSERT INTO unnest($2 :: timestamptz [ ]) AS created_at, unnest($3 :: VARCHAR(1024) [ ]) AS output, unnest($4 :: log_level [ ]) AS level, - unnest($5 :: workspace_agent_log_source [ ]) AS source - RETURNING workspace_agent_logs.agent_id, workspace_agent_logs.created_at, workspace_agent_logs.output, workspace_agent_logs.id, workspace_agent_logs.level, workspace_agent_logs.source + $5 :: uuid AS log_source_id + RETURNING workspace_agent_logs.agent_id, workspace_agent_logs.created_at, workspace_agent_logs.output, workspace_agent_logs.id, workspace_agent_logs.level, workspace_agent_logs.log_source_id ` type InsertWorkspaceAgentLogsParams struct { - AgentID uuid.UUID `db:"agent_id" json:"agent_id"` - CreatedAt []time.Time `db:"created_at" json:"created_at"` - Output []string `db:"output" json:"output"` - Level []LogLevel `db:"level" json:"level"` - Source []WorkspaceAgentLogSource `db:"source" json:"source"` - OutputLength int32 `db:"output_length" json:"output_length"` + AgentID uuid.UUID `db:"agent_id" json:"agent_id"` + CreatedAt []time.Time `db:"created_at" json:"created_at"` + Output []string `db:"output" json:"output"` + Level []LogLevel `db:"level" json:"level"` + LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` + OutputLength int32 `db:"output_length" json:"output_length"` } func (q *sqlQuerier) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error) { @@ -7048,7 +7053,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWor pq.Array(arg.CreatedAt), pq.Array(arg.Output), pq.Array(arg.Level), - pq.Array(arg.Source), + arg.LogSourceID, arg.OutputLength, ) if err != nil { @@ -7064,7 +7069,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWor &i.Output, &i.ID, &i.Level, - &i.Source, + &i.LogSourceID, ); err != nil { return nil, err } diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index bc2145df70e22..55459ea3a7cc6 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -40,21 +40,16 @@ INSERT INTO architecture, environment_variables, operating_system, - startup_script, directory, instance_metadata, resource_metadata, connection_timeout_seconds, troubleshooting_url, motd_file, - startup_script_behavior, - startup_script_timeout_seconds, - shutdown_script, - shutdown_script_timeout_seconds, display_apps ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) RETURNING *; -- name: UpdateWorkspaceAgentConnectionByID :exec UPDATE @@ -162,9 +157,20 @@ INSERT INTO unnest(@created_at :: timestamptz [ ]) AS created_at, unnest(@output :: VARCHAR(1024) [ ]) AS output, unnest(@level :: log_level [ ]) AS level, - unnest(@source :: workspace_agent_log_source [ ]) AS source + @log_source_id :: uuid AS log_source_id RETURNING workspace_agent_logs.*; +-- name: InsertWorkspaceAgentLogSources :many +INSERT INTO + workspace_agent_log_sources (workspace_agent_id, id, created_at, display_name, icon) + SELECT + @workspace_agent_id :: uuid AS workspace_agent_id, + unnest(@id :: uuid [ ]) AS id, + unnest(@created_at :: timestamptz [ ]) AS created_at, + unnest(@display_name :: VARCHAR(127) [ ]) AS display_name, + unnest(@icon :: text [ ]) AS icon + RETURNING workspace_agent_log_sources.*; + -- If an agent hasn't connected in the last 7 days, we purge it's logs. -- Logs can take up a lot of space, so it's important we clean up frequently. -- name: DeleteOldWorkspaceAgentLogs :exec diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index b7e69d718bde9..aca56b0e397b6 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -289,7 +289,6 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) createdAt := make([]time.Time, 0) output := make([]string, 0) level := make([]database.LogLevel, 0) - source := make([]database.WorkspaceAgentLogSource, 0) outputLength := 0 for _, logEntry := range req.Logs { createdAt = append(createdAt, logEntry.CreatedAt) @@ -308,20 +307,6 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) return } level = append(level, parsedLevel) - - if logEntry.Source == "" { - // Default to "startup_script" to support older agents that didn't have the source field. - logEntry.Source = codersdk.WorkspaceAgentLogSourceStartupScript - } - parsedSource := database.WorkspaceAgentLogSource(logEntry.Source) - if !parsedSource.Valid() { - httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{ - Message: "Invalid log source provided.", - Detail: fmt.Sprintf("invalid log source: %q", logEntry.Source), - }) - return - } - source = append(source, parsedSource) } logs, err := api.Database.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ @@ -329,7 +314,7 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) CreatedAt: createdAt, Output: output, Level: level, - Source: source, + LogSourceID: req.LogSourceID, OutputLength: int32(outputLength), }) if err != nil { diff --git a/codersdk/agentsdk/agentsdk.go b/codersdk/agentsdk/agentsdk.go index fb1b2f497410b..af374c8d1404d 100644 --- a/codersdk/agentsdk/agentsdk.go +++ b/codersdk/agentsdk/agentsdk.go @@ -91,14 +91,21 @@ type Manifest struct { DERPMap *tailcfg.DERPMap `json:"derpmap"` DERPForceWebSockets bool `json:"derp_force_websockets"` EnvironmentVariables map[string]string `json:"environment_variables"` - StartupScript string `json:"startup_script"` - StartupScriptTimeout time.Duration `json:"startup_script_timeout"` Directory string `json:"directory"` MOTDFile string `json:"motd_file"` - ShutdownScript string `json:"shutdown_script"` - ShutdownScriptTimeout time.Duration `json:"shutdown_script_timeout"` DisableDirectConnections bool `json:"disable_direct_connections"` Metadata []codersdk.WorkspaceAgentMetadataDescription `json:"metadata"` + Scripts []codersdk.WorkspaceAgentScript `json:"scripts"` +} + +type LogSource struct { + ID uuid.UUID `json:"id"` + DisplayName string `json:"display_name"` + Icon string `json:"icon"` +} + +type Script struct { + Script string `json:"script"` } // Manifest fetches manifest for the currently authenticated workspace agent. @@ -631,14 +638,14 @@ func (c *Client) PostStartup(ctx context.Context, req PostStartupRequest) error } type Log struct { - CreatedAt time.Time `json:"created_at"` - Output string `json:"output"` - Level codersdk.LogLevel `json:"level"` - Source codersdk.WorkspaceAgentLogSource `json:"source"` + CreatedAt time.Time `json:"created_at"` + Output string `json:"output"` + Level codersdk.LogLevel `json:"level"` } type PatchLogs struct { - Logs []Log `json:"logs"` + LogSourceID uuid.UUID `json:"log_source_id" validate:"required"` + Logs []Log `json:"logs"` } // PatchLogs writes log messages to the agent startup script. diff --git a/codersdk/agentsdk/logs.go b/codersdk/agentsdk/logs.go index 4b859070dbd94..89b8b90848b75 100644 --- a/codersdk/agentsdk/logs.go +++ b/codersdk/agentsdk/logs.go @@ -10,6 +10,8 @@ import ( "golang.org/x/xerrors" + "github.com/google/uuid" + "cdr.dev/slog" "github.com/coder/coder/v2/codersdk" "github.com/coder/retry" @@ -20,7 +22,7 @@ type startupLogsWriter struct { ctx context.Context send func(ctx context.Context, log ...Log) error level codersdk.LogLevel - source codersdk.WorkspaceAgentLogSource + source uuid.UUID } func (w *startupLogsWriter) Write(p []byte) (int, error) { @@ -44,7 +46,6 @@ func (w *startupLogsWriter) Write(p []byte) (int, error) { CreatedAt: time.Now().UTC(), // UTC, like dbtime.Now(). Level: w.level, Output: string(partial) + string(p[:nl-cr]), - Source: w.source, }) if err != nil { return n - len(p), err @@ -67,7 +68,6 @@ func (w *startupLogsWriter) Close() error { CreatedAt: time.Now().UTC(), // UTC, like dbtime.Now(). Level: w.level, Output: w.buf.String(), - Source: w.source, }) } return nil @@ -81,10 +81,7 @@ func (w *startupLogsWriter) Close() error { // // Neither Write nor Close is safe for concurrent use and must be used // by a single goroutine. -func StartupLogsWriter(ctx context.Context, sender func(ctx context.Context, log ...Log) error, source codersdk.WorkspaceAgentLogSource, level codersdk.LogLevel) io.WriteCloser { - if source == "" { - source = codersdk.WorkspaceAgentLogSourceExternal - } +func StartupLogsWriter(ctx context.Context, sender func(ctx context.Context, log ...Log) error, source uuid.UUID, level codersdk.LogLevel) io.WriteCloser { return &startupLogsWriter{ ctx: ctx, send: sender, @@ -98,7 +95,7 @@ func StartupLogsWriter(ctx context.Context, sender func(ctx context.Context, log // has been called. Calling sendLog concurrently is not supported. If // the context passed to flushAndClose is canceled, any remaining logs // will be discarded. -func LogsSender(patchLogs func(ctx context.Context, req PatchLogs) error, logger slog.Logger) (sendLog func(ctx context.Context, log ...Log) error, flushAndClose func(context.Context) error) { +func LogsSender(sourceID uuid.UUID, patchLogs func(ctx context.Context, req PatchLogs) error, logger slog.Logger) (sendLog func(ctx context.Context, log ...Log) error, flushAndClose func(context.Context) error) { // The main context is used to close the sender goroutine and cancel // any outbound requests to the API. The shutdown context is used to // signal the sender goroutine to flush logs and then exit. @@ -158,7 +155,8 @@ func LogsSender(patchLogs func(ctx context.Context, req PatchLogs) error, logger // shutdown. for r := retry.New(time.Second, 5*time.Second); r.Wait(ctx); { err := patchLogs(ctx, PatchLogs{ - Logs: backlog, + Logs: backlog, + LogSourceID: sourceID, }) if err == nil { break diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 846134ba89f45..b023d051185c2 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -144,42 +144,58 @@ const ( ) type WorkspaceAgent struct { - ID uuid.UUID `json:"id" format:"uuid"` - CreatedAt time.Time `json:"created_at" format:"date-time"` - UpdatedAt time.Time `json:"updated_at" format:"date-time"` - FirstConnectedAt *time.Time `json:"first_connected_at,omitempty" format:"date-time"` - LastConnectedAt *time.Time `json:"last_connected_at,omitempty" format:"date-time"` - DisconnectedAt *time.Time `json:"disconnected_at,omitempty" format:"date-time"` - StartedAt *time.Time `json:"started_at,omitempty" format:"date-time"` - ReadyAt *time.Time `json:"ready_at,omitempty" format:"date-time"` - Status WorkspaceAgentStatus `json:"status"` - LifecycleState WorkspaceAgentLifecycle `json:"lifecycle_state"` - Name string `json:"name"` - ResourceID uuid.UUID `json:"resource_id" format:"uuid"` - InstanceID string `json:"instance_id,omitempty"` - Architecture string `json:"architecture"` - EnvironmentVariables map[string]string `json:"environment_variables"` - OperatingSystem string `json:"operating_system"` - StartupScript string `json:"startup_script,omitempty"` - StartupScriptBehavior WorkspaceAgentStartupScriptBehavior `json:"startup_script_behavior"` - StartupScriptTimeoutSeconds int32 `json:"startup_script_timeout_seconds"` // StartupScriptTimeoutSeconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. - LogsLength int32 `json:"logs_length"` - LogsOverflowed bool `json:"logs_overflowed"` - Directory string `json:"directory,omitempty"` - ExpandedDirectory string `json:"expanded_directory,omitempty"` - Version string `json:"version"` - Apps []WorkspaceApp `json:"apps"` + ID uuid.UUID `json:"id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + UpdatedAt time.Time `json:"updated_at" format:"date-time"` + FirstConnectedAt *time.Time `json:"first_connected_at,omitempty" format:"date-time"` + LastConnectedAt *time.Time `json:"last_connected_at,omitempty" format:"date-time"` + DisconnectedAt *time.Time `json:"disconnected_at,omitempty" format:"date-time"` + StartedAt *time.Time `json:"started_at,omitempty" format:"date-time"` + ReadyAt *time.Time `json:"ready_at,omitempty" format:"date-time"` + Status WorkspaceAgentStatus `json:"status"` + LifecycleState WorkspaceAgentLifecycle `json:"lifecycle_state"` + Name string `json:"name"` + ResourceID uuid.UUID `json:"resource_id" format:"uuid"` + InstanceID string `json:"instance_id,omitempty"` + Architecture string `json:"architecture"` + EnvironmentVariables map[string]string `json:"environment_variables"` + OperatingSystem string `json:"operating_system"` + LogsLength int32 `json:"logs_length"` + LogsOverflowed bool `json:"logs_overflowed"` + Directory string `json:"directory,omitempty"` + ExpandedDirectory string `json:"expanded_directory,omitempty"` + Version string `json:"version"` + Apps []WorkspaceApp `json:"apps"` // DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). DERPLatency map[string]DERPRegion `json:"latency,omitempty"` ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"` TroubleshootingURL string `json:"troubleshooting_url"` // Deprecated: Use StartupScriptBehavior instead. - LoginBeforeReady bool `json:"login_before_ready"` - ShutdownScript string `json:"shutdown_script,omitempty"` - ShutdownScriptTimeoutSeconds int32 `json:"shutdown_script_timeout_seconds"` - Subsystems []AgentSubsystem `json:"subsystems"` - Health WorkspaceAgentHealth `json:"health"` // Health reports the health of the agent. - DisplayApps []DisplayApp `json:"display_apps"` + LoginBeforeReady bool `json:"login_before_ready"` + Subsystems []AgentSubsystem `json:"subsystems"` + Health WorkspaceAgentHealth `json:"health"` // Health reports the health of the agent. + DisplayApps []DisplayApp `json:"display_apps"` + LogSources []WorkspaceAgentLogSource `json:"log_sources"` + Scripts []WorkspaceAgentScript `json:"scripts"` +} + +type WorkspaceAgentLogSource struct { + WorkspaceAgentID uuid.UUID `json:"workspace_agent_id" format:"uuid"` + ID uuid.UUID `json:"id" format:"uuid"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + DisplayName string `json:"display_name"` + Icon string `json:"icon"` +} + +type WorkspaceAgentScript struct { + LogSourceDisplayName string `json:"log_source_display_name"` + LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` + Script string `json:"script"` + Schedule string `json:"schedule"` + RunOnStart bool `json:"run_on_start"` + RunOnStop bool `json:"run_on_stop"` + LoginBeforeReady bool `json:"login_before_ready"` + Timeout time.Duration `json:"timeout_seconds"` } type WorkspaceAgentHealth struct { @@ -530,13 +546,6 @@ func (c *Client) WorkspaceAgent(ctx context.Context, id uuid.UUID) (WorkspaceAge if err != nil { return WorkspaceAgent{}, err } - // Backwards compatibility for cases where the API is older then the client. - if workspaceAgent.StartupScriptBehavior == "" { - workspaceAgent.StartupScriptBehavior = WorkspaceAgentStartupScriptBehaviorNonBlocking - if !workspaceAgent.LoginBeforeReady { - workspaceAgent.StartupScriptBehavior = WorkspaceAgentStartupScriptBehaviorBlocking - } - } return workspaceAgent, nil } @@ -780,14 +789,3 @@ func (s AgentSubsystem) Valid() bool { return false } } - -type WorkspaceAgentLogSource string - -const ( - WorkspaceAgentLogSourceStartupScript WorkspaceAgentLogSource = "startup_script" - WorkspaceAgentLogSourceShutdownScript WorkspaceAgentLogSource = "shutdown_script" - WorkspaceAgentLogSourceKubernetes WorkspaceAgentLogSource = "kubernetes" - WorkspaceAgentLogSourceEnvbox WorkspaceAgentLogSource = "envbox" - WorkspaceAgentLogSourceEnvbuilder WorkspaceAgentLogSource = "envbuilder" - WorkspaceAgentLogSourceExternal WorkspaceAgentLogSource = "external" -) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 41239d8bd984f..928ffde5cead0 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1335,9 +1335,6 @@ export interface WorkspaceAgent { readonly architecture: string readonly environment_variables: Record readonly operating_system: string - readonly startup_script?: string - readonly startup_script_behavior: WorkspaceAgentStartupScriptBehavior - readonly startup_script_timeout_seconds: number readonly logs_length: number readonly logs_overflowed: boolean readonly directory?: string @@ -1348,11 +1345,11 @@ export interface WorkspaceAgent { readonly connection_timeout_seconds: number readonly troubleshooting_url: string readonly login_before_ready: boolean - readonly shutdown_script?: string - readonly shutdown_script_timeout_seconds: number readonly subsystems: AgentSubsystem[] readonly health: WorkspaceAgentHealth readonly display_apps: DisplayApp[] + readonly log_sources: WorkspaceAgentLogSource[] + readonly scripts: WorkspaceAgentScript[] } // From codersdk/workspaceagents.go @@ -1381,6 +1378,15 @@ export interface WorkspaceAgentLog { readonly level: LogLevel } +// From codersdk/workspaceagents.go +export interface WorkspaceAgentLogSource { + readonly workspace_agent_id: string + readonly id: string + readonly created_at: string + readonly display_name: string + readonly icon: string +} + // From codersdk/workspaceagents.go export interface WorkspaceAgentMetadata { readonly result: WorkspaceAgentMetadataResult @@ -1404,6 +1410,15 @@ export interface WorkspaceAgentMetadataResult { readonly error: string } +// From codersdk/workspaceagents.go +export interface WorkspaceAgentScript { + readonly log_source_id: string + readonly script: string + readonly schedule: string + readonly login_before_ready: boolean + readonly timeout_seconds: number +} + // From codersdk/workspaceapps.go export interface WorkspaceApp { readonly id: string @@ -1881,23 +1896,6 @@ export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = [ "starting", ] -// From codersdk/workspaceagents.go -export type WorkspaceAgentLogSource = - | "envbox" - | "envbuilder" - | "external" - | "kubernetes" - | "shutdown_script" - | "startup_script" -export const WorkspaceAgentLogSources: WorkspaceAgentLogSource[] = [ - "envbox", - "envbuilder", - "external", - "kubernetes", - "shutdown_script", - "startup_script", -] - // From codersdk/workspaceagents.go export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking" export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = From 89c7af1118053e3445b1abcfbeee9b8aa94291d4 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 17:56:41 +0000 Subject: [PATCH 04/59] Support legacy start and stop script format --- agent/agent.go | 6 +- agent/agent_test.go | 16 +- agent/agentscripts/agentscripts.go | 8 +- agent/agentscripts/agentscripts_test.go | 4 +- cli/cliui/agent_test.go | 9 +- cli/ssh.go | 12 +- .../provisionerdserver/provisionerdserver.go | 41 +- codersdk/workspaceagents.go | 15 +- provisioner/terraform/resources.go | 126 ++- provisioner/terraform/resources_test.go | 179 ++-- provisionersdk/proto/provisioner.pb.go | 973 ++++++++++-------- provisionersdk/proto/provisioner.proto | 21 +- 12 files changed, 763 insertions(+), 647 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 761aae91c861a..1c58d14673ee9 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -976,13 +976,13 @@ func (a *agent) runDERPMapSubscriber(ctx context.Context, network *tailnet.Conn) } func (a *agent) runScript(ctx context.Context, script codersdk.WorkspaceAgentScript) (err error) { - if script.Script == "" { + if script.Source == "" { return nil } logger := a.logger.With(slog.F("log_source", script.LogSourceDisplayName)) - logger.Info(ctx, "running script", slog.F("script", script.Script)) + logger.Info(ctx, "running script", slog.F("script", script.Source)) fileWriter, err := a.filesystem.OpenFile(filepath.Join(a.logDir, fmt.Sprintf("coder-%s-script.log", script.LogSourceDisplayName)), os.O_CREATE|os.O_RDWR, 0o600) if err != nil { return xerrors.Errorf("open %s script log file: %w", script.LogSourceDisplayName, err) @@ -994,7 +994,7 @@ func (a *agent) runScript(ctx context.Context, script codersdk.WorkspaceAgentScr } }() - cmdPty, err := a.sshServer.CreateCommand(ctx, script.Script, nil) + cmdPty, err := a.sshServer.CreateCommand(ctx, script.Source, nil) if err != nil { return xerrors.Errorf("%s script: create command: %w", script.LogSourceDisplayName, err) } diff --git a/agent/agent_test.go b/agent/agent_test.go index 35dad5f2f22f8..5cc3863fd3f05 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1209,7 +1209,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", + Source: "sleep 3", Timeout: time.Nanosecond, RunOnStart: true, }}, @@ -1234,7 +1234,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "false", + Source: "false", Timeout: 30 * time.Second, RunOnStart: true, }}, @@ -1259,7 +1259,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "true", + Source: "true", Timeout: 30 * time.Second, RunOnStart: true, }}, @@ -1284,7 +1284,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", + Source: "sleep 3", Timeout: 30 * time.Second, RunOnStop: true, }}, @@ -1325,7 +1325,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", + Source: "sleep 3", Timeout: time.Nanosecond, RunOnStop: true, }}, @@ -1367,7 +1367,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "false", + Source: "false", Timeout: 30 * time.Second, RunOnStop: true, }}, @@ -1417,11 +1417,11 @@ func TestAgent_Lifecycle(t *testing.T) { DERPMap: derpMap, Scripts: []codersdk.WorkspaceAgentScript{{ LogSourceDisplayName: "startup", - Script: "echo 1", + Source: "echo 1", RunOnStart: true, }, { LogSourceDisplayName: "shutdown", - Script: "echo " + expected, + Source: "echo " + expected, RunOnStop: true, }}, }, diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index bf00ffd8bc895..b03438716cb71 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -65,11 +65,11 @@ func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { r.scripts = scripts for _, script := range scripts { - if script.Schedule == "" { + if script.CRON == "" { continue } script := script - _, err := r.cron.AddFunc(script.Schedule, func() { + _, err := r.cron.AddFunc(script.CRON, func() { err := r.run(script) if err != nil { r.Logger.Warn(r.ctx, "run agent script on schedule", slog.Error(err)) @@ -114,7 +114,7 @@ func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { logger := r.Logger.With(slog.F("log_source", script.LogSourceDisplayName)) ctx := r.ctx - logger.Info(ctx, "running agent script", slog.F("script", script.Script)) + logger.Info(ctx, "running agent script", slog.F("script", script.Source)) fileWriter, err := r.Filesystem.OpenFile(filepath.Join(r.LogDir, fmt.Sprintf("coder-%s-script.log", script.LogSourceDisplayName)), os.O_CREATE|os.O_RDWR, 0o600) if err != nil { return xerrors.Errorf("open %s script log file: %w", script.LogSourceDisplayName, err) @@ -134,7 +134,7 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { defer cancel() } - cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Script, nil) + cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Source, nil) if err != nil { return xerrors.Errorf("%s script: create command: %w", script.LogSourceDisplayName, err) } diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index 4521484966ebd..22450302df7a7 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -32,7 +32,7 @@ func TestExecuteBasic(t *testing.T) { defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ LogSourceDisplayName: "test", - Script: "echo hello", + Source: "echo hello", }}) require.NoError(t, err) require.NoError(t, runner.Execute(func(script codersdk.WorkspaceAgentScript) bool { @@ -47,7 +47,7 @@ func TestTimeout(t *testing.T) { runner := setup(t, nil) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", + Source: "sleep 3", Timeout: time.Nanosecond, }}) require.NoError(t, err) diff --git a/cli/cliui/agent_test.go b/cli/cliui/agent_test.go index c5b5f9f4a3965..4c08155317f7b 100644 --- a/cli/cliui/agent_test.go +++ b/cli/cliui/agent_test.go @@ -288,11 +288,10 @@ func TestAgent(t *testing.T) { var buf bytes.Buffer agent := codersdk.WorkspaceAgent{ - ID: uuid.New(), - Status: codersdk.WorkspaceAgentConnecting, - StartupScriptBehavior: codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking, - CreatedAt: time.Now(), - LifecycleState: codersdk.WorkspaceAgentLifecycleCreated, + ID: uuid.New(), + Status: codersdk.WorkspaceAgentConnecting, + CreatedAt: time.Now(), + LifecycleState: codersdk.WorkspaceAgentLifecycleCreated, } logs := make(chan []codersdk.WorkspaceAgentLog, 1) diff --git a/cli/ssh.go b/cli/ssh.go index 4455b8987cc5f..dbff0ea52017e 100644 --- a/cli/ssh.go +++ b/cli/ssh.go @@ -143,13 +143,11 @@ func (r *RootCmd) ssh() *clibase.Cmd { case "no": wait = false case "auto": - switch workspaceAgent.StartupScriptBehavior { - case codersdk.WorkspaceAgentStartupScriptBehaviorBlocking: - wait = true - case codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking: - wait = false - default: - return xerrors.Errorf("unknown startup script behavior %q", workspaceAgent.StartupScriptBehavior) + for _, script := range workspaceAgent.Scripts { + if script.StartBlocksLogin { + wait = true + break + } } default: return xerrors.Errorf("unknown wait value %q", waitEnum) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 05249d65986a4..613000f83d7c3 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1250,32 +1250,21 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. agentID := uuid.New() dbAgent, err := db.InsertWorkspaceAgent(ctx, database.InsertWorkspaceAgentParams{ - ID: agentID, - CreatedAt: dbtime.Now(), - UpdatedAt: dbtime.Now(), - ResourceID: resource.ID, - Name: prAgent.Name, - AuthToken: authToken, - AuthInstanceID: instanceID, - Architecture: prAgent.Architecture, - EnvironmentVariables: env, - Directory: prAgent.Directory, - OperatingSystem: prAgent.OperatingSystem, - StartupScript: sql.NullString{ - String: prAgent.StartupScript, - Valid: prAgent.StartupScript != "", - }, - ConnectionTimeoutSeconds: prAgent.GetConnectionTimeoutSeconds(), - TroubleshootingURL: prAgent.GetTroubleshootingUrl(), - MOTDFile: prAgent.GetMotdFile(), - StartupScriptBehavior: database.StartupScriptBehavior(prAgent.GetStartupScriptBehavior()), - StartupScriptTimeoutSeconds: prAgent.GetStartupScriptTimeoutSeconds(), - ShutdownScript: sql.NullString{ - String: prAgent.ShutdownScript, - Valid: prAgent.ShutdownScript != "", - }, - ShutdownScriptTimeoutSeconds: prAgent.GetShutdownScriptTimeoutSeconds(), - DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()), + ID: agentID, + CreatedAt: dbtime.Now(), + UpdatedAt: dbtime.Now(), + ResourceID: resource.ID, + Name: prAgent.Name, + AuthToken: authToken, + AuthInstanceID: instanceID, + Architecture: prAgent.Architecture, + EnvironmentVariables: env, + Directory: prAgent.Directory, + OperatingSystem: prAgent.OperatingSystem, + ConnectionTimeoutSeconds: prAgent.GetConnectionTimeoutSeconds(), + TroubleshootingURL: prAgent.GetTroubleshootingUrl(), + MOTDFile: prAgent.GetMotdFile(), + DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()), }) if err != nil { return xerrors.Errorf("insert agent: %w", err) diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index b023d051185c2..c6a1cc9db7673 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -190,11 +190,11 @@ type WorkspaceAgentLogSource struct { type WorkspaceAgentScript struct { LogSourceDisplayName string `json:"log_source_display_name"` LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` - Script string `json:"script"` - Schedule string `json:"schedule"` + Source string `json:"source"` + CRON string `json:"cron"` RunOnStart bool `json:"run_on_start"` RunOnStop bool `json:"run_on_stop"` - LoginBeforeReady bool `json:"login_before_ready"` + StartBlocksLogin bool `json:"start_blocks_login"` Timeout time.Duration `json:"timeout_seconds"` } @@ -767,10 +767,11 @@ const ( ) type WorkspaceAgentLog struct { - ID int64 `json:"id"` - CreatedAt time.Time `json:"created_at" format:"date-time"` - Output string `json:"output"` - Level LogLevel `json:"level"` + ID int64 `json:"id"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + Output string `json:"output"` + Level LogLevel `json:"level"` + LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` } type AgentSubsystem string diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 3382b7e7b7847..1a16ac2e3492a 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -29,25 +29,26 @@ type agentMetadata struct { // A mapping of attributes on the "coder_agent" resource. type agentAttributes struct { - Auth string `mapstructure:"auth"` - OperatingSystem string `mapstructure:"os"` - Architecture string `mapstructure:"arch"` - Directory string `mapstructure:"dir"` - ID string `mapstructure:"id"` - Token string `mapstructure:"token"` - Env map[string]string `mapstructure:"env"` - StartupScript string `mapstructure:"startup_script"` - ConnectionTimeoutSeconds int32 `mapstructure:"connection_timeout"` - TroubleshootingURL string `mapstructure:"troubleshooting_url"` - MOTDFile string `mapstructure:"motd_file"` + Auth string `mapstructure:"auth"` + OperatingSystem string `mapstructure:"os"` + Architecture string `mapstructure:"arch"` + Directory string `mapstructure:"dir"` + ID string `mapstructure:"id"` + Token string `mapstructure:"token"` + Env map[string]string `mapstructure:"env"` // Deprecated, but remains here for backwards compatibility. - LoginBeforeReady bool `mapstructure:"login_before_ready"` - StartupScriptBehavior string `mapstructure:"startup_script_behavior"` - StartupScriptTimeoutSeconds int32 `mapstructure:"startup_script_timeout"` - ShutdownScript string `mapstructure:"shutdown_script"` - ShutdownScriptTimeoutSeconds int32 `mapstructure:"shutdown_script_timeout"` - Metadata []agentMetadata `mapstructure:"metadata"` - DisplayApps []agentDisplayAppsAttributes `mapstructure:"display_apps"` + StartupScript string `mapstructure:"startup_script"` + StartupScriptBehavior string `mapstructure:"startup_script_behavior"` + StartupScriptTimeoutSeconds int32 `mapstructure:"startup_script_timeout"` + LoginBeforeReady bool `mapstructure:"login_before_ready"` + ShutdownScript string `mapstructure:"shutdown_script"` + ShutdownScriptTimeoutSeconds int32 `mapstructure:"shutdown_script_timeout"` + + ConnectionTimeoutSeconds int32 `mapstructure:"connection_timeout"` + TroubleshootingURL string `mapstructure:"troubleshooting_url"` + MOTDFile string `mapstructure:"motd_file"` + Metadata []agentMetadata `mapstructure:"metadata"` + DisplayApps []agentDisplayAppsAttributes `mapstructure:"display_apps"` } type agentDisplayAppsAttributes struct { @@ -76,6 +77,18 @@ type agentAppAttributes struct { Healthcheck []appHealthcheckAttributes `mapstructure:"healthcheck"` } +type agentScriptAttributes struct { + AgentID string `mapstructure:"agent_id"` + DisplayName string `mapstructure:"display_name"` + Icon string `mapstructure:"icon"` + Source string `mapstructure:"source"` + Cron string `mapstructure:"cron"` + StartBlocksLogin bool `mapstructure:"start_blocks_login"` + RunOnStart bool `mapstructure:"run_on_start"` + RunOnStop bool `mapstructure:"run_on_stop"` + TimeoutSeconds int32 `mapstructure:"timeout"` +} + // A mapping of attributes on the "healthcheck" resource. type appHealthcheckAttributes struct { URL string `mapstructure:"url"` @@ -206,22 +219,35 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error } agent := &proto.Agent{ - Name: tfResource.Name, - Id: attrs.ID, - Env: attrs.Env, - StartupScript: attrs.StartupScript, - OperatingSystem: attrs.OperatingSystem, - Architecture: attrs.Architecture, - Directory: attrs.Directory, - ConnectionTimeoutSeconds: attrs.ConnectionTimeoutSeconds, - TroubleshootingUrl: attrs.TroubleshootingURL, - MotdFile: attrs.MOTDFile, - StartupScriptBehavior: startupScriptBehavior, - StartupScriptTimeoutSeconds: attrs.StartupScriptTimeoutSeconds, - ShutdownScript: attrs.ShutdownScript, - ShutdownScriptTimeoutSeconds: attrs.ShutdownScriptTimeoutSeconds, - Metadata: metadata, - DisplayApps: displayApps, + Name: tfResource.Name, + Id: attrs.ID, + Env: attrs.Env, + OperatingSystem: attrs.OperatingSystem, + Architecture: attrs.Architecture, + Directory: attrs.Directory, + ConnectionTimeoutSeconds: attrs.ConnectionTimeoutSeconds, + TroubleshootingUrl: attrs.TroubleshootingURL, + MotdFile: attrs.MOTDFile, + Metadata: metadata, + DisplayApps: displayApps, + } + // Support the legacy script attributes in the agent! + if attrs.StartupScript != "" { + agent.Scripts = append(agent.Scripts, &proto.Script{ + DisplayName: "Startup Script", + Source: attrs.StartupScript, + StartBlocksLogin: startupScriptBehavior == string(codersdk.WorkspaceAgentStartupScriptBehaviorBlocking), + Timeout: attrs.StartupScriptTimeoutSeconds, + RunOnStart: true, + }) + } + if attrs.ShutdownScript != "" { + agent.Scripts = append(agent.Scripts, &proto.Script{ + DisplayName: "Shutdown Script", + Source: attrs.ShutdownScript, + Timeout: attrs.ShutdownScriptTimeoutSeconds, + RunOnStop: true, + }) } switch attrs.Auth { case "token": @@ -403,6 +429,38 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error } } + // Associate scripts with agents. + for _, resources := range tfResourcesByLabel { + for _, resource := range resources { + if resource.Type != "coder_script" { + continue + } + var attrs agentScriptAttributes + err = mapstructure.Decode(resource.AttributeValues, &attrs) + if err != nil { + return nil, xerrors.Errorf("decode app attributes: %w", err) + } + for _, agents := range resourceAgents { + for _, agent := range agents { + // Find agents with the matching ID and associate them! + if agent.Id != attrs.AgentID { + continue + } + agent.Scripts = append(agent.Scripts, &proto.Script{ + DisplayName: attrs.DisplayName, + Icon: attrs.Icon, + Source: attrs.Source, + Cron: attrs.Cron, + StartBlocksLogin: attrs.StartBlocksLogin, + RunOnStart: attrs.RunOnStart, + RunOnStop: attrs.RunOnStop, + Timeout: attrs.TimeoutSeconds, + }) + } + } + } + } + // Associate metadata blocks with resources. resourceMetadata := map[string][]*proto.Resource_Metadata{} resourceHidden := map[string]bool{} diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index 79d7a56ac4d16..4bbd001aa6ca9 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -54,7 +54,6 @@ func TestConvertResources(t *testing.T) { OperatingSystem: "linux", Architecture: "amd64", Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, }}, @@ -72,7 +71,6 @@ func TestConvertResources(t *testing.T) { OperatingSystem: "linux", Architecture: "amd64", Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, }}, @@ -91,7 +89,6 @@ func TestConvertResources(t *testing.T) { OperatingSystem: "linux", Architecture: "amd64", Auth: &proto.Agent_InstanceId{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, }}, @@ -108,7 +105,6 @@ func TestConvertResources(t *testing.T) { OperatingSystem: "linux", Architecture: "amd64", Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, }}, @@ -121,48 +117,41 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "dev1", - OperatingSystem: "linux", - Architecture: "amd64", - Auth: &proto.Agent_Token{}, - ConnectionTimeoutSeconds: 120, - StartupScriptBehavior: "non-blocking", - StartupScriptTimeoutSeconds: 300, - ShutdownScriptTimeoutSeconds: 300, - DisplayApps: &displayApps, + Name: "dev1", + OperatingSystem: "linux", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }, { - Name: "dev2", - OperatingSystem: "darwin", - Architecture: "amd64", - Auth: &proto.Agent_Token{}, - ConnectionTimeoutSeconds: 1, - MotdFile: "/etc/motd", - StartupScriptBehavior: "non-blocking", - StartupScriptTimeoutSeconds: 30, - ShutdownScript: "echo bye bye", - ShutdownScriptTimeoutSeconds: 30, - DisplayApps: &displayApps, + Name: "dev2", + OperatingSystem: "darwin", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 1, + MotdFile: "/etc/motd", + DisplayApps: &displayApps, + Scripts: []*proto.Script{{ + DisplayName: "Shutdown Script", + RunOnStop: true, + Source: "echo bye bye", + Timeout: 30, + }}, }, { - Name: "dev3", - OperatingSystem: "windows", - Architecture: "arm64", - Auth: &proto.Agent_Token{}, - ConnectionTimeoutSeconds: 120, - TroubleshootingUrl: "https://coder.com/troubleshoot", - StartupScriptBehavior: "blocking", - StartupScriptTimeoutSeconds: 300, - ShutdownScriptTimeoutSeconds: 300, - DisplayApps: &displayApps, + Name: "dev3", + OperatingSystem: "windows", + Architecture: "arm64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + TroubleshootingUrl: "https://coder.com/troubleshoot", + DisplayApps: &displayApps, }, { - Name: "dev4", - OperatingSystem: "linux", - Architecture: "amd64", - Auth: &proto.Agent_Token{}, - ConnectionTimeoutSeconds: 120, - StartupScriptBehavior: "blocking", - StartupScriptTimeoutSeconds: 300, - ShutdownScriptTimeoutSeconds: 300, - DisplayApps: &displayApps, + Name: "dev4", + OperatingSystem: "linux", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }}, }}, }, @@ -199,7 +188,6 @@ func TestConvertResources(t *testing.T) { }, }, Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, }}, @@ -224,7 +212,6 @@ func TestConvertResources(t *testing.T) { }, }, Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, }}, @@ -263,11 +250,8 @@ func TestConvertResources(t *testing.T) { Interval: 5, Timeout: 1, }}, - ShutdownScriptTimeoutSeconds: 300, - StartupScriptTimeoutSeconds: 300, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - DisplayApps: &displayApps, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }}, }}, }, @@ -307,7 +291,6 @@ func TestConvertResources(t *testing.T) { Name: "main", OperatingSystem: "linux", Architecture: "amd64", - StartupScript: " #!/bin/bash\n # home folder can be empty, so copying default bash settings\n if [ ! -f ~/.profile ]; then\n cp /etc/skel/.profile $HOME\n fi\n if [ ! -f ~/.bashrc ]; then\n cp /etc/skel/.bashrc $HOME\n fi\n # install and start code-server\n curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log\n code-server --auth none --port 13337 | tee code-server-install.log &\n", Apps: []*proto.App{ { Icon: "/icon/code.svg", @@ -317,9 +300,13 @@ func TestConvertResources(t *testing.T) { }, }, Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", ConnectionTimeoutSeconds: 120, DisplayApps: &displayApps, + Scripts: []*proto.Script{{ + DisplayName: "Startup Script", + RunOnStart: true, + Source: " #!/bin/bash\n # home folder can be empty, so copying default bash settings\n if [ ! -f ~/.profile ]; then\n cp /etc/skel/.profile $HOME\n fi\n if [ ! -f ~/.bashrc ]; then\n cp /etc/skel/.bashrc $HOME\n fi\n # install and start code-server\n curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log\n code-server --auth none --port 13337 | tee code-server-install.log &\n", + }}, }}, }, }, @@ -329,15 +316,12 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "dev", - OperatingSystem: "windows", - ShutdownScriptTimeoutSeconds: 300, - StartupScriptTimeoutSeconds: 300, - Architecture: "arm64", - Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - DisplayApps: &displayApps, + Name: "dev", + OperatingSystem: "windows", + Architecture: "arm64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }}, }}, parameters: []*proto.RichParameter{{ @@ -411,15 +395,12 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "dev", - OperatingSystem: "windows", - ShutdownScriptTimeoutSeconds: 300, - StartupScriptTimeoutSeconds: 300, - Architecture: "arm64", - Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - DisplayApps: &displayApps, + Name: "dev", + OperatingSystem: "windows", + Architecture: "arm64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }}, }}, parameters: []*proto.RichParameter{{ @@ -440,15 +421,12 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "dev", - OperatingSystem: "windows", - ShutdownScriptTimeoutSeconds: 300, - StartupScriptTimeoutSeconds: 300, - Architecture: "arm64", - Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - DisplayApps: &displayApps, + Name: "dev", + OperatingSystem: "windows", + Architecture: "arm64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }}, }}, parameters: []*proto.RichParameter{{ @@ -496,15 +474,12 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "main", - OperatingSystem: "linux", - Architecture: "amd64", - Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - StartupScriptTimeoutSeconds: 300, - ShutdownScriptTimeoutSeconds: 300, - DisplayApps: &displayApps, + Name: "main", + OperatingSystem: "linux", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &displayApps, }}, }}, gitAuthProviders: []string{"github", "gitlab"}, @@ -514,14 +489,11 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "main", - OperatingSystem: "linux", - Architecture: "amd64", - Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - StartupScriptTimeoutSeconds: 300, - ShutdownScriptTimeoutSeconds: 300, + Name: "main", + OperatingSystem: "linux", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, DisplayApps: &proto.DisplayApps{ VscodeInsiders: true, WebTerminal: true, @@ -534,15 +506,12 @@ func TestConvertResources(t *testing.T) { Name: "dev", Type: "null_resource", Agents: []*proto.Agent{{ - Name: "main", - OperatingSystem: "linux", - Architecture: "amd64", - Auth: &proto.Agent_Token{}, - StartupScriptBehavior: "non-blocking", - ConnectionTimeoutSeconds: 120, - StartupScriptTimeoutSeconds: 300, - ShutdownScriptTimeoutSeconds: 300, - DisplayApps: &proto.DisplayApps{}, + Name: "main", + OperatingSystem: "linux", + Architecture: "amd64", + Auth: &proto.Agent_Token{}, + ConnectionTimeoutSeconds: 120, + DisplayApps: &proto.DisplayApps{}, }}, }}, }, diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 1475fd2359549..9e7c4053742fd 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -827,14 +827,14 @@ type Agent struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Env map[string]string `protobuf:"bytes,3,rep,name=env,proto3" json:"env,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - StartupScript string `protobuf:"bytes,4,opt,name=startup_script,json=startupScript,proto3" json:"startup_script,omitempty"` - OperatingSystem string `protobuf:"bytes,5,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"` - Architecture string `protobuf:"bytes,6,opt,name=architecture,proto3" json:"architecture,omitempty"` - Directory string `protobuf:"bytes,7,opt,name=directory,proto3" json:"directory,omitempty"` - Apps []*App `protobuf:"bytes,8,rep,name=apps,proto3" json:"apps,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Env map[string]string `protobuf:"bytes,3,rep,name=env,proto3" json:"env,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // Field 4 was startup_script, now removed. + OperatingSystem string `protobuf:"bytes,5,opt,name=operating_system,json=operatingSystem,proto3" json:"operating_system,omitempty"` + Architecture string `protobuf:"bytes,6,opt,name=architecture,proto3" json:"architecture,omitempty"` + Directory string `protobuf:"bytes,7,opt,name=directory,proto3" json:"directory,omitempty"` + Apps []*App `protobuf:"bytes,8,rep,name=apps,proto3" json:"apps,omitempty"` // Types that are assignable to Auth: // // *Agent_Token @@ -844,12 +844,11 @@ type Agent struct { TroubleshootingUrl string `protobuf:"bytes,12,opt,name=troubleshooting_url,json=troubleshootingUrl,proto3" json:"troubleshooting_url,omitempty"` MotdFile string `protobuf:"bytes,13,opt,name=motd_file,json=motdFile,proto3" json:"motd_file,omitempty"` // Field 14 was bool login_before_ready = 14, now removed. - StartupScriptTimeoutSeconds int32 `protobuf:"varint,15,opt,name=startup_script_timeout_seconds,json=startupScriptTimeoutSeconds,proto3" json:"startup_script_timeout_seconds,omitempty"` - ShutdownScript string `protobuf:"bytes,16,opt,name=shutdown_script,json=shutdownScript,proto3" json:"shutdown_script,omitempty"` - ShutdownScriptTimeoutSeconds int32 `protobuf:"varint,17,opt,name=shutdown_script_timeout_seconds,json=shutdownScriptTimeoutSeconds,proto3" json:"shutdown_script_timeout_seconds,omitempty"` - Metadata []*Agent_Metadata `protobuf:"bytes,18,rep,name=metadata,proto3" json:"metadata,omitempty"` - StartupScriptBehavior string `protobuf:"bytes,19,opt,name=startup_script_behavior,json=startupScriptBehavior,proto3" json:"startup_script_behavior,omitempty"` - DisplayApps *DisplayApps `protobuf:"bytes,20,opt,name=display_apps,json=displayApps,proto3" json:"display_apps,omitempty"` + // Field 15, 16, 17 were related to scripts, which are now removed. + Metadata []*Agent_Metadata `protobuf:"bytes,18,rep,name=metadata,proto3" json:"metadata,omitempty"` + // Field 19 was startup_script_behavior, now removed. + DisplayApps *DisplayApps `protobuf:"bytes,20,opt,name=display_apps,json=displayApps,proto3" json:"display_apps,omitempty"` + Scripts []*Script `protobuf:"bytes,21,rep,name=scripts,proto3" json:"scripts,omitempty"` } func (x *Agent) Reset() { @@ -905,13 +904,6 @@ func (x *Agent) GetEnv() map[string]string { return nil } -func (x *Agent) GetStartupScript() string { - if x != nil { - return x.StartupScript - } - return "" -} - func (x *Agent) GetOperatingSystem() string { if x != nil { return x.OperatingSystem @@ -982,27 +974,6 @@ func (x *Agent) GetMotdFile() string { return "" } -func (x *Agent) GetStartupScriptTimeoutSeconds() int32 { - if x != nil { - return x.StartupScriptTimeoutSeconds - } - return 0 -} - -func (x *Agent) GetShutdownScript() string { - if x != nil { - return x.ShutdownScript - } - return "" -} - -func (x *Agent) GetShutdownScriptTimeoutSeconds() int32 { - if x != nil { - return x.ShutdownScriptTimeoutSeconds - } - return 0 -} - func (x *Agent) GetMetadata() []*Agent_Metadata { if x != nil { return x.Metadata @@ -1010,16 +981,16 @@ func (x *Agent) GetMetadata() []*Agent_Metadata { return nil } -func (x *Agent) GetStartupScriptBehavior() string { +func (x *Agent) GetDisplayApps() *DisplayApps { if x != nil { - return x.StartupScriptBehavior + return x.DisplayApps } - return "" + return nil } -func (x *Agent) GetDisplayApps() *DisplayApps { +func (x *Agent) GetScripts() []*Script { if x != nil { - return x.DisplayApps + return x.Scripts } return nil } @@ -1119,6 +1090,110 @@ func (x *DisplayApps) GetPortForwardingHelper() bool { return false } +// Script represents a script to be run on the workspace. +type Script struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + DisplayName string `protobuf:"bytes,1,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` + Icon string `protobuf:"bytes,2,opt,name=icon,proto3" json:"icon,omitempty"` + Source string `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` + Cron string `protobuf:"bytes,4,opt,name=cron,proto3" json:"cron,omitempty"` + StartBlocksLogin bool `protobuf:"varint,5,opt,name=start_blocks_login,json=startBlocksLogin,proto3" json:"start_blocks_login,omitempty"` + RunOnStart bool `protobuf:"varint,6,opt,name=run_on_start,json=runOnStart,proto3" json:"run_on_start,omitempty"` + RunOnStop bool `protobuf:"varint,7,opt,name=run_on_stop,json=runOnStop,proto3" json:"run_on_stop,omitempty"` + Timeout int32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"` +} + +func (x *Script) Reset() { + *x = Script{} + if protoimpl.UnsafeEnabled { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Script) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Script) ProtoMessage() {} + +func (x *Script) ProtoReflect() protoreflect.Message { + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Script.ProtoReflect.Descriptor instead. +func (*Script) Descriptor() ([]byte, []int) { + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{11} +} + +func (x *Script) GetDisplayName() string { + if x != nil { + return x.DisplayName + } + return "" +} + +func (x *Script) GetIcon() string { + if x != nil { + return x.Icon + } + return "" +} + +func (x *Script) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *Script) GetCron() string { + if x != nil { + return x.Cron + } + return "" +} + +func (x *Script) GetStartBlocksLogin() bool { + if x != nil { + return x.StartBlocksLogin + } + return false +} + +func (x *Script) GetRunOnStart() bool { + if x != nil { + return x.RunOnStart + } + return false +} + +func (x *Script) GetRunOnStop() bool { + if x != nil { + return x.RunOnStop + } + return false +} + +func (x *Script) GetTimeout() int32 { + if x != nil { + return x.Timeout + } + return 0 +} + // App represents a dev-accessible application on the workspace. type App struct { state protoimpl.MessageState @@ -1141,7 +1216,7 @@ type App struct { func (x *App) Reset() { *x = App{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1154,7 +1229,7 @@ func (x *App) String() string { func (*App) ProtoMessage() {} func (x *App) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[11] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1167,7 +1242,7 @@ func (x *App) ProtoReflect() protoreflect.Message { // Deprecated: Use App.ProtoReflect.Descriptor instead. func (*App) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{11} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{12} } func (x *App) GetSlug() string { @@ -1247,7 +1322,7 @@ type Healthcheck struct { func (x *Healthcheck) Reset() { *x = Healthcheck{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1260,7 +1335,7 @@ func (x *Healthcheck) String() string { func (*Healthcheck) ProtoMessage() {} func (x *Healthcheck) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[12] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1273,7 +1348,7 @@ func (x *Healthcheck) ProtoReflect() protoreflect.Message { // Deprecated: Use Healthcheck.ProtoReflect.Descriptor instead. func (*Healthcheck) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{12} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13} } func (x *Healthcheck) GetUrl() string { @@ -1316,7 +1391,7 @@ type Resource struct { func (x *Resource) Reset() { *x = Resource{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1329,7 +1404,7 @@ func (x *Resource) String() string { func (*Resource) ProtoMessage() {} func (x *Resource) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[13] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1342,7 +1417,7 @@ func (x *Resource) ProtoReflect() protoreflect.Message { // Deprecated: Use Resource.ProtoReflect.Descriptor instead. func (*Resource) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14} } func (x *Resource) GetName() string { @@ -1423,7 +1498,7 @@ type Metadata struct { func (x *Metadata) Reset() { *x = Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1436,7 +1511,7 @@ func (x *Metadata) String() string { func (*Metadata) ProtoMessage() {} func (x *Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[14] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1449,7 +1524,7 @@ func (x *Metadata) ProtoReflect() protoreflect.Message { // Deprecated: Use Metadata.ProtoReflect.Descriptor instead. func (*Metadata) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15} } func (x *Metadata) GetCoderUrl() string { @@ -1545,7 +1620,7 @@ type Config struct { func (x *Config) Reset() { *x = Config{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1558,7 +1633,7 @@ func (x *Config) String() string { func (*Config) ProtoMessage() {} func (x *Config) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[15] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1571,7 +1646,7 @@ func (x *Config) ProtoReflect() protoreflect.Message { // Deprecated: Use Config.ProtoReflect.Descriptor instead. func (*Config) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{15} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16} } func (x *Config) GetTemplateSourceArchive() []byte { @@ -1605,7 +1680,7 @@ type ParseRequest struct { func (x *ParseRequest) Reset() { *x = ParseRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1618,7 +1693,7 @@ func (x *ParseRequest) String() string { func (*ParseRequest) ProtoMessage() {} func (x *ParseRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[16] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1631,7 +1706,7 @@ func (x *ParseRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ParseRequest.ProtoReflect.Descriptor instead. func (*ParseRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{16} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17} } // ParseComplete indicates a request to parse completed. @@ -1648,7 +1723,7 @@ type ParseComplete struct { func (x *ParseComplete) Reset() { *x = ParseComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1661,7 +1736,7 @@ func (x *ParseComplete) String() string { func (*ParseComplete) ProtoMessage() {} func (x *ParseComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[17] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1674,7 +1749,7 @@ func (x *ParseComplete) ProtoReflect() protoreflect.Message { // Deprecated: Use ParseComplete.ProtoReflect.Descriptor instead. func (*ParseComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{17} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18} } func (x *ParseComplete) GetError() string { @@ -1713,7 +1788,7 @@ type PlanRequest struct { func (x *PlanRequest) Reset() { *x = PlanRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1726,7 +1801,7 @@ func (x *PlanRequest) String() string { func (*PlanRequest) ProtoMessage() {} func (x *PlanRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[18] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1739,7 +1814,7 @@ func (x *PlanRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanRequest.ProtoReflect.Descriptor instead. func (*PlanRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{18} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{19} } func (x *PlanRequest) GetMetadata() *Metadata { @@ -1785,7 +1860,7 @@ type PlanComplete struct { func (x *PlanComplete) Reset() { *x = PlanComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1798,7 +1873,7 @@ func (x *PlanComplete) String() string { func (*PlanComplete) ProtoMessage() {} func (x *PlanComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[19] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1811,7 +1886,7 @@ func (x *PlanComplete) ProtoReflect() protoreflect.Message { // Deprecated: Use PlanComplete.ProtoReflect.Descriptor instead. func (*PlanComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{19} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{20} } func (x *PlanComplete) GetError() string { @@ -1855,7 +1930,7 @@ type ApplyRequest struct { func (x *ApplyRequest) Reset() { *x = ApplyRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1868,7 +1943,7 @@ func (x *ApplyRequest) String() string { func (*ApplyRequest) ProtoMessage() {} func (x *ApplyRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[20] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1881,7 +1956,7 @@ func (x *ApplyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyRequest.ProtoReflect.Descriptor instead. func (*ApplyRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{20} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{21} } func (x *ApplyRequest) GetMetadata() *Metadata { @@ -1907,7 +1982,7 @@ type ApplyComplete struct { func (x *ApplyComplete) Reset() { *x = ApplyComplete{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1920,7 +1995,7 @@ func (x *ApplyComplete) String() string { func (*ApplyComplete) ProtoMessage() {} func (x *ApplyComplete) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[21] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1933,7 +2008,7 @@ func (x *ApplyComplete) ProtoReflect() protoreflect.Message { // Deprecated: Use ApplyComplete.ProtoReflect.Descriptor instead. func (*ApplyComplete) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{21} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{22} } func (x *ApplyComplete) GetState() []byte { @@ -1981,7 +2056,7 @@ type CancelRequest struct { func (x *CancelRequest) Reset() { *x = CancelRequest{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1994,7 +2069,7 @@ func (x *CancelRequest) String() string { func (*CancelRequest) ProtoMessage() {} func (x *CancelRequest) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[22] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2007,7 +2082,7 @@ func (x *CancelRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CancelRequest.ProtoReflect.Descriptor instead. func (*CancelRequest) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{22} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{23} } type Request struct { @@ -2028,7 +2103,7 @@ type Request struct { func (x *Request) Reset() { *x = Request{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2041,7 +2116,7 @@ func (x *Request) String() string { func (*Request) ProtoMessage() {} func (x *Request) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[23] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2054,7 +2129,7 @@ func (x *Request) ProtoReflect() protoreflect.Message { // Deprecated: Use Request.ProtoReflect.Descriptor instead. func (*Request) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{23} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{24} } func (m *Request) GetType() isRequest_Type { @@ -2150,7 +2225,7 @@ type Response struct { func (x *Response) Reset() { *x = Response{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2163,7 +2238,7 @@ func (x *Response) String() string { func (*Response) ProtoMessage() {} func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[24] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2176,7 +2251,7 @@ func (x *Response) ProtoReflect() protoreflect.Message { // Deprecated: Use Response.ProtoReflect.Descriptor instead. func (*Response) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{24} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{25} } func (m *Response) GetType() isResponse_Type { @@ -2257,7 +2332,7 @@ type Agent_Metadata struct { func (x *Agent_Metadata) Reset() { *x = Agent_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2270,7 +2345,7 @@ func (x *Agent_Metadata) String() string { func (*Agent_Metadata) ProtoMessage() {} func (x *Agent_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[25] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2335,7 +2410,7 @@ type Resource_Metadata struct { func (x *Resource_Metadata) Reset() { *x = Resource_Metadata{} if protoimpl.UnsafeEnabled { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2348,7 +2423,7 @@ func (x *Resource_Metadata) String() string { func (*Resource_Metadata) ProtoMessage() {} func (x *Resource_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[27] + mi := &file_provisionersdk_proto_provisioner_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2361,7 +2436,7 @@ func (x *Resource_Metadata) ProtoReflect() protoreflect.Message { // Deprecated: Use Resource_Metadata.ProtoReflect.Descriptor instead. func (*Resource_Metadata) Descriptor() ([]byte, []int) { - return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{13, 0} + return file_provisionersdk_proto_provisioner_proto_rawDescGZIP(), []int{14, 0} } func (x *Resource_Metadata) GetKey() string { @@ -2480,288 +2555,290 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xa8, 0x08, 0x0a, 0x05, 0x41, 0x67, 0x65, + 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xc3, 0x06, 0x0a, 0x05, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2d, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, - 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, - 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x29, 0x0a, 0x10, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, - 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, - 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, - 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, - 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x24, 0x0a, 0x04, 0x61, 0x70, 0x70, - 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, - 0x16, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, - 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, - 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x72, 0x6f, 0x75, - 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, 0x6c, 0x18, - 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, - 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x6f, 0x74, - 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, - 0x74, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x43, 0x0a, 0x1e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, - 0x70, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1b, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73, - 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x10, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x12, 0x45, 0x0a, 0x1f, 0x73, 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, - 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, - 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x11, 0x20, 0x01, 0x28, 0x05, 0x52, 0x1c, 0x73, - 0x68, 0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x37, 0x0a, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x36, 0x0a, 0x17, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x5f, - 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, - 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x73, 0x74, 0x61, 0x72, 0x74, 0x75, 0x70, 0x53, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x3b, 0x0a, 0x0c, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x52, 0x0b, 0x64, 0x69, - 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x1a, 0x8d, 0x01, 0x0a, 0x08, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, - 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, - 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x76, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x42, 0x06, 0x0a, 0x04, 0x61, 0x75, 0x74, 0x68, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x52, - 0x12, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, - 0x61, 0x64, 0x79, 0x22, 0xc6, 0x01, 0x0a, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, - 0x70, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x76, - 0x73, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x65, 0x62, 0x5f, 0x74, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x65, 0x62, 0x54, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x5f, 0x68, - 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x73, 0x68, - 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, - 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0xb5, 0x02, 0x0a, - 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, - 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, - 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, - 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, - 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, - 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, - 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, - 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, - 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, - 0xf1, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, - 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, - 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, - 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, - 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, - 0x75, 0x6c, 0x6c, 0x22, 0xae, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, - 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, - 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, - 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, - 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, - 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, - 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, - 0x6c, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, - 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, - 0xa6, 0x02, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, - 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, - 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x12, - 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, - 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0c, 0x50, 0x6c, 0x61, - 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x41, - 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x22, 0xda, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, - 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, - 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, - 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x0f, - 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x8c, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, - 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, - 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, - 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, - 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, - 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd1, - 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, - 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, - 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, - 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, - 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, - 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, - 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, - 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, - 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, - 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, - 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, - 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, - 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, - 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, - 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, - 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x28, 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, - 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, - 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x29, 0x0a, 0x10, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, + 0x6e, 0x67, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, + 0x12, 0x22, 0x0a, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, + 0x74, 0x75, 0x72, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, + 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x12, 0x24, 0x0a, 0x04, 0x61, 0x70, 0x70, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, + 0x70, 0x70, 0x52, 0x04, 0x61, 0x70, 0x70, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, + 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x21, 0x0a, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x49, 0x64, 0x12, 0x3c, 0x0a, 0x1a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x18, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, + 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, + 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, + 0x74, 0x72, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x73, 0x68, 0x6f, 0x6f, 0x74, 0x69, 0x6e, 0x67, 0x55, + 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x6f, 0x74, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, + 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x6f, 0x74, 0x64, 0x46, 0x69, 0x6c, 0x65, 0x12, + 0x37, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x3b, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, + 0x6c, 0x61, 0x79, 0x5f, 0x61, 0x70, 0x70, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x44, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x41, 0x70, 0x70, 0x73, 0x12, 0x2d, 0x0a, 0x07, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x73, + 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x52, 0x07, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x73, 0x1a, 0x8d, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, + 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x1a, 0x36, 0x0a, 0x08, 0x45, 0x6e, 0x76, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04, + 0x61, 0x75, 0x74, 0x68, 0x4a, 0x04, 0x08, 0x0e, 0x10, 0x0f, 0x52, 0x12, 0x6c, 0x6f, 0x67, 0x69, + 0x6e, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x79, 0x22, 0xc6, + 0x01, 0x0a, 0x0b, 0x44, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x41, 0x70, 0x70, 0x73, 0x12, 0x16, + 0x0a, 0x06, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x76, 0x73, 0x63, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x73, 0x12, + 0x21, 0x0a, 0x0c, 0x77, 0x65, 0x62, 0x5f, 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x65, 0x62, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x73, 0x68, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x73, 0x68, 0x48, 0x65, 0x6c, 0x70, 0x65, + 0x72, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x14, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, + 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0xf5, 0x01, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4c, 0x6f, + 0x67, 0x69, 0x6e, 0x12, 0x20, 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x4f, 0x6e, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, + 0x73, 0x74, 0x6f, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4f, + 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, + 0xb5, 0x02, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, + 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, + 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, + 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, + 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, + 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, + 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, + 0x6c, 0x64, 0x22, 0xf1, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, + 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, + 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, + 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, + 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, + 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0xae, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, + 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, + 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, + 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, + 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, + 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, + 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, + 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, + 0x61, 0x64, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, + 0x6d, 0x65, 0x22, 0xa6, 0x02, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, + 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, + 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, + 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, + 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, + 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0c, + 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x73, 0x22, 0x41, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x22, 0xda, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, + 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, + 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, + 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, + 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, + 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, + 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, + 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, + 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, + 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, + 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, + 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, + 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, + 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, + 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, + 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, + 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, + 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, + 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, + 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, + 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, + 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, + 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0x49, 0x0a, 0x0b, + 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, + 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -2777,7 +2854,7 @@ func file_provisionersdk_proto_provisioner_proto_rawDescGZIP() []byte { } var file_provisionersdk_proto_provisioner_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 28) +var file_provisionersdk_proto_provisioner_proto_msgTypes = make([]protoimpl.MessageInfo, 29) var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (LogLevel)(0), // 0: provisioner.LogLevel (AppSharingLevel)(0), // 1: provisioner.AppSharingLevel @@ -2793,62 +2870,64 @@ var file_provisionersdk_proto_provisioner_proto_goTypes = []interface{}{ (*GitAuthProvider)(nil), // 11: provisioner.GitAuthProvider (*Agent)(nil), // 12: provisioner.Agent (*DisplayApps)(nil), // 13: provisioner.DisplayApps - (*App)(nil), // 14: provisioner.App - (*Healthcheck)(nil), // 15: provisioner.Healthcheck - (*Resource)(nil), // 16: provisioner.Resource - (*Metadata)(nil), // 17: provisioner.Metadata - (*Config)(nil), // 18: provisioner.Config - (*ParseRequest)(nil), // 19: provisioner.ParseRequest - (*ParseComplete)(nil), // 20: provisioner.ParseComplete - (*PlanRequest)(nil), // 21: provisioner.PlanRequest - (*PlanComplete)(nil), // 22: provisioner.PlanComplete - (*ApplyRequest)(nil), // 23: provisioner.ApplyRequest - (*ApplyComplete)(nil), // 24: provisioner.ApplyComplete - (*CancelRequest)(nil), // 25: provisioner.CancelRequest - (*Request)(nil), // 26: provisioner.Request - (*Response)(nil), // 27: provisioner.Response - (*Agent_Metadata)(nil), // 28: provisioner.Agent.Metadata - nil, // 29: provisioner.Agent.EnvEntry - (*Resource_Metadata)(nil), // 30: provisioner.Resource.Metadata + (*Script)(nil), // 14: provisioner.Script + (*App)(nil), // 15: provisioner.App + (*Healthcheck)(nil), // 16: provisioner.Healthcheck + (*Resource)(nil), // 17: provisioner.Resource + (*Metadata)(nil), // 18: provisioner.Metadata + (*Config)(nil), // 19: provisioner.Config + (*ParseRequest)(nil), // 20: provisioner.ParseRequest + (*ParseComplete)(nil), // 21: provisioner.ParseComplete + (*PlanRequest)(nil), // 22: provisioner.PlanRequest + (*PlanComplete)(nil), // 23: provisioner.PlanComplete + (*ApplyRequest)(nil), // 24: provisioner.ApplyRequest + (*ApplyComplete)(nil), // 25: provisioner.ApplyComplete + (*CancelRequest)(nil), // 26: provisioner.CancelRequest + (*Request)(nil), // 27: provisioner.Request + (*Response)(nil), // 28: provisioner.Response + (*Agent_Metadata)(nil), // 29: provisioner.Agent.Metadata + nil, // 30: provisioner.Agent.EnvEntry + (*Resource_Metadata)(nil), // 31: provisioner.Resource.Metadata } var file_provisionersdk_proto_provisioner_proto_depIdxs = []int32{ 5, // 0: provisioner.RichParameter.options:type_name -> provisioner.RichParameterOption 0, // 1: provisioner.Log.level:type_name -> provisioner.LogLevel - 29, // 2: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry - 14, // 3: provisioner.Agent.apps:type_name -> provisioner.App - 28, // 4: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata + 30, // 2: provisioner.Agent.env:type_name -> provisioner.Agent.EnvEntry + 15, // 3: provisioner.Agent.apps:type_name -> provisioner.App + 29, // 4: provisioner.Agent.metadata:type_name -> provisioner.Agent.Metadata 13, // 5: provisioner.Agent.display_apps:type_name -> provisioner.DisplayApps - 15, // 6: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck - 1, // 7: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel - 12, // 8: provisioner.Resource.agents:type_name -> provisioner.Agent - 30, // 9: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata - 2, // 10: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition - 4, // 11: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable - 17, // 12: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata - 7, // 13: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue - 8, // 14: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue - 11, // 15: provisioner.PlanRequest.git_auth_providers:type_name -> provisioner.GitAuthProvider - 16, // 16: provisioner.PlanComplete.resources:type_name -> provisioner.Resource - 6, // 17: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter - 17, // 18: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata - 16, // 19: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource - 6, // 20: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter - 18, // 21: provisioner.Request.config:type_name -> provisioner.Config - 19, // 22: provisioner.Request.parse:type_name -> provisioner.ParseRequest - 21, // 23: provisioner.Request.plan:type_name -> provisioner.PlanRequest - 23, // 24: provisioner.Request.apply:type_name -> provisioner.ApplyRequest - 25, // 25: provisioner.Request.cancel:type_name -> provisioner.CancelRequest - 9, // 26: provisioner.Response.log:type_name -> provisioner.Log - 20, // 27: provisioner.Response.parse:type_name -> provisioner.ParseComplete - 22, // 28: provisioner.Response.plan:type_name -> provisioner.PlanComplete - 24, // 29: provisioner.Response.apply:type_name -> provisioner.ApplyComplete - 26, // 30: provisioner.Provisioner.Session:input_type -> provisioner.Request - 27, // 31: provisioner.Provisioner.Session:output_type -> provisioner.Response - 31, // [31:32] is the sub-list for method output_type - 30, // [30:31] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 14, // 6: provisioner.Agent.scripts:type_name -> provisioner.Script + 16, // 7: provisioner.App.healthcheck:type_name -> provisioner.Healthcheck + 1, // 8: provisioner.App.sharing_level:type_name -> provisioner.AppSharingLevel + 12, // 9: provisioner.Resource.agents:type_name -> provisioner.Agent + 31, // 10: provisioner.Resource.metadata:type_name -> provisioner.Resource.Metadata + 2, // 11: provisioner.Metadata.workspace_transition:type_name -> provisioner.WorkspaceTransition + 4, // 12: provisioner.ParseComplete.template_variables:type_name -> provisioner.TemplateVariable + 18, // 13: provisioner.PlanRequest.metadata:type_name -> provisioner.Metadata + 7, // 14: provisioner.PlanRequest.rich_parameter_values:type_name -> provisioner.RichParameterValue + 8, // 15: provisioner.PlanRequest.variable_values:type_name -> provisioner.VariableValue + 11, // 16: provisioner.PlanRequest.git_auth_providers:type_name -> provisioner.GitAuthProvider + 17, // 17: provisioner.PlanComplete.resources:type_name -> provisioner.Resource + 6, // 18: provisioner.PlanComplete.parameters:type_name -> provisioner.RichParameter + 18, // 19: provisioner.ApplyRequest.metadata:type_name -> provisioner.Metadata + 17, // 20: provisioner.ApplyComplete.resources:type_name -> provisioner.Resource + 6, // 21: provisioner.ApplyComplete.parameters:type_name -> provisioner.RichParameter + 19, // 22: provisioner.Request.config:type_name -> provisioner.Config + 20, // 23: provisioner.Request.parse:type_name -> provisioner.ParseRequest + 22, // 24: provisioner.Request.plan:type_name -> provisioner.PlanRequest + 24, // 25: provisioner.Request.apply:type_name -> provisioner.ApplyRequest + 26, // 26: provisioner.Request.cancel:type_name -> provisioner.CancelRequest + 9, // 27: provisioner.Response.log:type_name -> provisioner.Log + 21, // 28: provisioner.Response.parse:type_name -> provisioner.ParseComplete + 23, // 29: provisioner.Response.plan:type_name -> provisioner.PlanComplete + 25, // 30: provisioner.Response.apply:type_name -> provisioner.ApplyComplete + 27, // 31: provisioner.Provisioner.Session:input_type -> provisioner.Request + 28, // 32: provisioner.Provisioner.Session:output_type -> provisioner.Response + 32, // [32:33] is the sub-list for method output_type + 31, // [31:32] is the sub-list for method input_type + 31, // [31:31] is the sub-list for extension type_name + 31, // [31:31] is the sub-list for extension extendee + 0, // [0:31] is the sub-list for field type_name } func init() { file_provisionersdk_proto_provisioner_proto_init() } @@ -2990,7 +3069,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*App); i { + switch v := v.(*Script); i { case 0: return &v.state case 1: @@ -3002,7 +3081,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Healthcheck); i { + switch v := v.(*App); i { case 0: return &v.state case 1: @@ -3014,7 +3093,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Resource); i { + switch v := v.(*Healthcheck); i { case 0: return &v.state case 1: @@ -3026,7 +3105,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Metadata); i { + switch v := v.(*Resource); i { case 0: return &v.state case 1: @@ -3038,7 +3117,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Config); i { + switch v := v.(*Metadata); i { case 0: return &v.state case 1: @@ -3050,7 +3129,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ParseRequest); i { + switch v := v.(*Config); i { case 0: return &v.state case 1: @@ -3062,7 +3141,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ParseComplete); i { + switch v := v.(*ParseRequest); i { case 0: return &v.state case 1: @@ -3074,7 +3153,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanRequest); i { + switch v := v.(*ParseComplete); i { case 0: return &v.state case 1: @@ -3086,7 +3165,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PlanComplete); i { + switch v := v.(*PlanRequest); i { case 0: return &v.state case 1: @@ -3098,7 +3177,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyRequest); i { + switch v := v.(*PlanComplete); i { case 0: return &v.state case 1: @@ -3110,7 +3189,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ApplyComplete); i { + switch v := v.(*ApplyRequest); i { case 0: return &v.state case 1: @@ -3122,7 +3201,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CancelRequest); i { + switch v := v.(*ApplyComplete); i { case 0: return &v.state case 1: @@ -3134,7 +3213,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Request); i { + switch v := v.(*CancelRequest); i { case 0: return &v.state case 1: @@ -3146,7 +3225,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Response); i { + switch v := v.(*Request); i { case 0: return &v.state case 1: @@ -3158,6 +3237,18 @@ func file_provisionersdk_proto_provisioner_proto_init() { } } file_provisionersdk_proto_provisioner_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Response); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_provisionersdk_proto_provisioner_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Agent_Metadata); i { case 0: return &v.state @@ -3169,7 +3260,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { return nil } } - file_provisionersdk_proto_provisioner_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + file_provisionersdk_proto_provisioner_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Resource_Metadata); i { case 0: return &v.state @@ -3187,14 +3278,14 @@ func file_provisionersdk_proto_provisioner_proto_init() { (*Agent_Token)(nil), (*Agent_InstanceId)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[23].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[24].OneofWrappers = []interface{}{ (*Request_Config)(nil), (*Request_Parse)(nil), (*Request_Plan)(nil), (*Request_Apply)(nil), (*Request_Cancel)(nil), } - file_provisionersdk_proto_provisioner_proto_msgTypes[24].OneofWrappers = []interface{}{ + file_provisionersdk_proto_provisioner_proto_msgTypes[25].OneofWrappers = []interface{}{ (*Response_Log)(nil), (*Response_Parse)(nil), (*Response_Plan)(nil), @@ -3206,7 +3297,7 @@ func file_provisionersdk_proto_provisioner_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_provisionersdk_proto_provisioner_proto_rawDesc, NumEnums: 3, - NumMessages: 28, + NumMessages: 29, NumExtensions: 0, NumServices: 1, }, diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 38e44590bc7cd..d822563e420f7 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -101,7 +101,7 @@ message Agent { string id = 1; string name = 2; map env = 3; - string startup_script = 4; + // Field 4 was startup_script, now removed. string operating_system = 5; string architecture = 6; string directory = 7; @@ -114,12 +114,11 @@ message Agent { string troubleshooting_url = 12; string motd_file = 13; // Field 14 was bool login_before_ready = 14, now removed. - int32 startup_script_timeout_seconds = 15; - string shutdown_script = 16; - int32 shutdown_script_timeout_seconds = 17; + // Field 15, 16, 17 were related to scripts, which are now removed. repeated Metadata metadata = 18; - string startup_script_behavior = 19; + // Field 19 was startup_script_behavior, now removed. DisplayApps display_apps = 20; + repeated Script scripts = 21; } enum AppSharingLevel { @@ -136,6 +135,18 @@ message DisplayApps { bool port_forwarding_helper = 5; } +// Script represents a script to be run on the workspace. +message Script { + string display_name = 1; + string icon = 2; + string source = 3; + string cron = 4; + bool start_blocks_login = 5; + bool run_on_start = 6; + bool run_on_stop = 7; + int32 timeout = 8; +} + // App represents a dev-accessible application on the workspace. message App { // slug is the unique identifier for the app, usually the name from the From d5df133d3867736ca64b2cec9226bfe6bf056249 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 18:24:49 +0000 Subject: [PATCH 05/59] Pipe the scripts! --- coderd/database/dbauthz/dbauthz.go | 8 + coderd/database/dbauthz/dbauthz_test.go | 3 +- coderd/database/dbfake/dbfake.go | 13 ++ coderd/database/dbmetrics/dbmetrics.go | 14 ++ coderd/database/dbmock/dbmock.go | 45 +++++ coderd/database/dump.sql | 10 +- .../000154_workspace_agent_script.up.sql | 10 +- coderd/database/models.go | 20 +- coderd/database/querier.go | 3 + coderd/database/queries.sql.go | 113 +++++++++++ coderd/database/queries/workspacescripts.sql | 18 ++ .../provisionerdserver/provisionerdserver.go | 5 - .../provisionerdserver_test.go | 4 - coderd/provisionerjobs.go | 21 ++- coderd/telemetry/telemetry.go | 4 - coderd/workspaceagents.go | 178 ++++++++++-------- coderd/workspacebuilds.go | 22 ++- coderd/workspaces.go | 2 + codersdk/agentsdk/logs_test.go | 5 +- codersdk/workspaceagents.go | 18 +- site/src/api/typesGenerated.ts | 11 +- 21 files changed, 391 insertions(+), 136 deletions(-) create mode 100644 coderd/database/queries/workspacescripts.sql diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 7feb81bdf934b..5bccc02b9bd2e 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1564,6 +1564,10 @@ func (q *querier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentI return q.db.GetWorkspaceAgentMetadata(ctx, workspaceAgentID) } +func (q *querier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { + panic("not implemented") +} + func (q *querier) GetWorkspaceAgentStats(ctx context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) { return q.db.GetWorkspaceAgentStats(ctx, createdAfter) } @@ -2049,6 +2053,10 @@ func (q *querier) InsertWorkspaceAgentMetadata(ctx context.Context, arg database return q.db.InsertWorkspaceAgentMetadata(ctx, arg) } +func (q *querier) InsertWorkspaceAgentScripts(ctx context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { + panic("not implemented") +} + func (q *querier) InsertWorkspaceAgentStat(ctx context.Context, arg database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) { // TODO: This is a workspace agent operation. Should users be able to query this? // Not really sure what this is for. diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 5dab130857a39..851176baae29d 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -1487,8 +1487,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { })) s.Run("InsertWorkspaceAgent", s.Subtest(func(db database.Store, check *expects) { check.Args(database.InsertWorkspaceAgentParams{ - ID: uuid.New(), - StartupScriptBehavior: database.StartupScriptBehaviorNonBlocking, + ID: uuid.New(), }).Asserts(rbac.ResourceSystem, rbac.ActionCreate) })) s.Run("InsertWorkspaceApp", s.Subtest(func(db database.Store, check *expects) { diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index b814492184438..8e2b21d967d99 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -3103,6 +3103,10 @@ func (q *FakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, workspaceAgen return metadata, nil } +func (q *FakeQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { + panic("not implemented") +} + func (q *FakeQuerier) GetWorkspaceAgentStats(_ context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -4505,6 +4509,15 @@ func (q *FakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg databa return nil } +func (q *FakeQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { + err := validateDatabaseType(arg) + if err != nil { + return nil, err + } + + panic("not implemented") +} + func (q *FakeQuerier) InsertWorkspaceAgentStat(_ context.Context, p database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) { if err := validateDatabaseType(p); err != nil { return database.WorkspaceAgentStat{}, err diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 2850551904d84..a79e7318f888e 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -830,6 +830,13 @@ func (m metricsStore) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAg return metadata, err } +func (m metricsStore) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { + start := time.Now() + r0, r1 := m.s.GetWorkspaceAgentScriptsByAgentIDs(ctx, ids) + m.queryLatencies.WithLabelValues("GetWorkspaceAgentScriptsByAgentIDs").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]database.GetWorkspaceAgentStatsRow, error) { start := time.Now() stats, err := m.s.GetWorkspaceAgentStats(ctx, createdAt) @@ -1257,6 +1264,13 @@ func (m metricsStore) InsertWorkspaceAgentMetadata(ctx context.Context, arg data return err } +func (m metricsStore) InsertWorkspaceAgentScripts(ctx context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { + start := time.Now() + r0, r1 := m.s.InsertWorkspaceAgentScripts(ctx, arg) + m.queryLatencies.WithLabelValues("InsertWorkspaceAgentScripts").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) InsertWorkspaceAgentStat(ctx context.Context, arg database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) { start := time.Now() stat, err := m.s.InsertWorkspaceAgentStat(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index b0ae7955a458d..421e8c2569a8b 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -1721,6 +1721,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentMetadata(arg0, arg1 interface{ return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentMetadata", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentMetadata), arg0, arg1) } +// GetWorkspaceAgentScriptsByAgentIDs mocks base method. +func (m *MockStore) GetWorkspaceAgentScriptsByAgentIDs(arg0 context.Context, arg1 []uuid.UUID) ([]database.WorkspaceAgentScript, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetWorkspaceAgentScriptsByAgentIDs", arg0, arg1) + ret0, _ := ret[0].([]database.WorkspaceAgentScript) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetWorkspaceAgentScriptsByAgentIDs indicates an expected call of GetWorkspaceAgentScriptsByAgentIDs. +func (mr *MockStoreMockRecorder) GetWorkspaceAgentScriptsByAgentIDs(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentScriptsByAgentIDs", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentScriptsByAgentIDs), arg0, arg1) +} + // GetWorkspaceAgentStats mocks base method. func (m *MockStore) GetWorkspaceAgentStats(arg0 context.Context, arg1 time.Time) ([]database.GetWorkspaceAgentStatsRow, error) { m.ctrl.T.Helper() @@ -2599,6 +2614,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceAgent(arg0, arg1 interface{}) *g return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgent", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgent), arg0, arg1) } +// InsertWorkspaceAgentLogSources mocks base method. +func (m *MockStore) InsertWorkspaceAgentLogSources(arg0 context.Context, arg1 database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertWorkspaceAgentLogSources", arg0, arg1) + ret0, _ := ret[0].([]database.WorkspaceAgentLogSource) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertWorkspaceAgentLogSources indicates an expected call of InsertWorkspaceAgentLogSources. +func (mr *MockStoreMockRecorder) InsertWorkspaceAgentLogSources(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentLogSources", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentLogSources), arg0, arg1) +} + // InsertWorkspaceAgentLogs mocks base method. func (m *MockStore) InsertWorkspaceAgentLogs(arg0 context.Context, arg1 database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { m.ctrl.T.Helper() @@ -2628,6 +2658,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceAgentMetadata(arg0, arg1 interfa return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentMetadata", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentMetadata), arg0, arg1) } +// InsertWorkspaceAgentScripts mocks base method. +func (m *MockStore) InsertWorkspaceAgentScripts(arg0 context.Context, arg1 database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "InsertWorkspaceAgentScripts", arg0, arg1) + ret0, _ := ret[0].([]database.WorkspaceAgentScript) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// InsertWorkspaceAgentScripts indicates an expected call of InsertWorkspaceAgentScripts. +func (mr *MockStoreMockRecorder) InsertWorkspaceAgentScripts(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAgentScripts", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAgentScripts), arg0, arg1) +} + // InsertWorkspaceAgentStat mocks base method. func (m *MockStore) InsertWorkspaceAgentStat(arg0 context.Context, arg1 database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) { m.ctrl.T.Helper() diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index c0ff45acb9147..06f1cbb407bec 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -727,14 +727,14 @@ CREATE UNLOGGED TABLE workspace_agent_metadata ( CREATE TABLE workspace_agent_scripts ( workspace_agent_id uuid NOT NULL, log_source_id uuid NOT NULL, + log_source_display_name character varying(127) NOT NULL, created_at timestamp with time zone NOT NULL, - script text NOT NULL, - schedule text NOT NULL, - login_before_ready boolean NOT NULL, + source text NOT NULL, + cron text NOT NULL, + start_blocks_login boolean NOT NULL, run_on_start boolean NOT NULL, run_on_stop boolean NOT NULL, - name character varying(127) NOT NULL, - description text NOT NULL + timeout integer NOT NULL ); CREATE SEQUENCE workspace_agent_startup_logs_id_seq diff --git a/coderd/database/migrations/000154_workspace_agent_script.up.sql b/coderd/database/migrations/000154_workspace_agent_script.up.sql index 434389185150d..a9d605e505227 100644 --- a/coderd/database/migrations/000154_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000154_workspace_agent_script.up.sql @@ -11,14 +11,14 @@ CREATE TABLE workspace_agent_log_sources ( CREATE TABLE workspace_agent_scripts ( workspace_agent_id uuid NOT NULL, log_source_id uuid NOT NULL, + log_source_display_name varchar(127) NOT NULL, created_at timestamptz NOT NULL, - script text NOT NULL, - schedule text NOT NULL, - login_before_ready boolean NOT NULL, + source text NOT NULL, + cron text NOT NULL, + start_blocks_login boolean NOT NULL, run_on_start boolean NOT NULL, run_on_stop boolean NOT NULL, - name varchar(127) NOT NULL, - description text NOT NULL + timeout integer NOT NULL ); ALTER TABLE workspace_agent_logs ADD COLUMN log_source_id uuid NOT NULL; diff --git a/coderd/database/models.go b/coderd/database/models.go index 9ef86bc4d6c13..0ce7ced36528b 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1976,16 +1976,16 @@ type WorkspaceAgentMetadatum struct { } type WorkspaceAgentScript struct { - WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` - LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - Script string `db:"script" json:"script"` - Schedule string `db:"schedule" json:"schedule"` - LoginBeforeReady bool `db:"login_before_ready" json:"login_before_ready"` - RunOnStart bool `db:"run_on_start" json:"run_on_start"` - RunOnStop bool `db:"run_on_stop" json:"run_on_stop"` - Name string `db:"name" json:"name"` - Description string `db:"description" json:"description"` + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` + LogSourceDisplayName string `db:"log_source_display_name" json:"log_source_display_name"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Source string `db:"source" json:"source"` + Cron string `db:"cron" json:"cron"` + StartBlocksLogin bool `db:"start_blocks_login" json:"start_blocks_login"` + RunOnStart bool `db:"run_on_start" json:"run_on_start"` + RunOnStop bool `db:"run_on_stop" json:"run_on_stop"` + Timeout int32 `db:"timeout" json:"timeout"` } type WorkspaceAgentStat struct { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 520266bd1d25c..efae925a76d44 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -162,6 +162,7 @@ type sqlcQuerier interface { GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (GetWorkspaceAgentLifecycleStateByIDRow, error) GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWorkspaceAgentLogsAfterParams) ([]WorkspaceAgentLog, error) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentMetadatum, error) + GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsRow, error) GetWorkspaceAgentStatsAndLabels(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsAndLabelsRow, error) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgent, error) @@ -235,8 +236,10 @@ type sqlcQuerier interface { InsertUserLink(ctx context.Context, arg InsertUserLinkParams) (UserLink, error) InsertWorkspace(ctx context.Context, arg InsertWorkspaceParams) (Workspace, error) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) + InsertWorkspaceAgentLogSources(ctx context.Context, arg InsertWorkspaceAgentLogSourcesParams) ([]WorkspaceAgentLogSource, error) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error) InsertWorkspaceAgentMetadata(ctx context.Context, arg InsertWorkspaceAgentMetadataParams) error + InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) InsertWorkspaceAgentStat(ctx context.Context, arg InsertWorkspaceAgentStatParams) (WorkspaceAgentStat, error) InsertWorkspaceAgentStats(ctx context.Context, arg InsertWorkspaceAgentStatsParams) error InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index b153c88d8bfa2..3ca34c95bc2d3 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -9033,6 +9033,119 @@ func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg In return items, nil } +const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many +SELECT workspace_agent_id, log_source_id, log_source_display_name, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +` + +func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { + rows, err := q.db.QueryContext(ctx, getWorkspaceAgentScriptsByAgentIDs, pq.Array(ids)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentScript + for rows.Next() { + var i WorkspaceAgentScript + if err := rows.Scan( + &i.WorkspaceAgentID, + &i.LogSourceID, + &i.LogSourceDisplayName, + &i.CreatedAt, + &i.Source, + &i.Cron, + &i.StartBlocksLogin, + &i.RunOnStart, + &i.RunOnStop, + &i.Timeout, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many +INSERT INTO + workspace_agent_scripts (workspace_agent_id, log_source_id, log_source_display_name, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) +SELECT + $1 :: uuid AS workspace_agent_id, + unnest($2 :: uuid [ ]) AS log_source_id, + unnest($3 :: varchar(127) [ ]) AS log_source_display_name, + unnest($4 :: timestamptz [ ]) AS created_at, + unnest($5 :: text [ ]) AS source, + unnest($6 :: text [ ]) AS cron, + unnest($7 :: boolean [ ]) AS start_blocks_login, + unnest($8 :: boolean [ ]) AS run_on_start, + unnest($9 :: boolean [ ]) AS run_on_stop, + unnest($10 :: integer [ ]) AS timeout +RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_source_display_name, workspace_agent_scripts.created_at, workspace_agent_scripts.source, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout +` + +type InsertWorkspaceAgentScriptsParams struct { + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` + LogSourceDisplayName []string `db:"log_source_display_name" json:"log_source_display_name"` + CreatedAt []time.Time `db:"created_at" json:"created_at"` + Source []string `db:"source" json:"source"` + Cron []string `db:"cron" json:"cron"` + StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` + RunOnStart []bool `db:"run_on_start" json:"run_on_start"` + RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"` + Timeout []int32 `db:"timeout" json:"timeout"` +} + +func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) { + rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentScripts, + arg.WorkspaceAgentID, + pq.Array(arg.LogSourceID), + pq.Array(arg.LogSourceDisplayName), + pq.Array(arg.CreatedAt), + pq.Array(arg.Source), + pq.Array(arg.Cron), + pq.Array(arg.StartBlocksLogin), + pq.Array(arg.RunOnStart), + pq.Array(arg.RunOnStop), + pq.Array(arg.Timeout), + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentScript + for rows.Next() { + var i WorkspaceAgentScript + if err := rows.Scan( + &i.WorkspaceAgentID, + &i.LogSourceID, + &i.LogSourceDisplayName, + &i.CreatedAt, + &i.Source, + &i.Cron, + &i.StartBlocksLogin, + &i.RunOnStart, + &i.RunOnStop, + &i.Timeout, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getDeploymentWorkspaceStats = `-- name: GetDeploymentWorkspaceStats :one WITH workspaces_with_jobs AS ( SELECT diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql new file mode 100644 index 0000000000000..8488bba96b8b8 --- /dev/null +++ b/coderd/database/queries/workspacescripts.sql @@ -0,0 +1,18 @@ +-- name: InsertWorkspaceAgentScripts :many +INSERT INTO + workspace_agent_scripts (workspace_agent_id, log_source_id, log_source_display_name, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) +SELECT + @workspace_agent_id :: uuid AS workspace_agent_id, + unnest(@log_source_id :: uuid [ ]) AS log_source_id, + unnest(@log_source_display_name :: varchar(127) [ ]) AS log_source_display_name, + unnest(@created_at :: timestamptz [ ]) AS created_at, + unnest(@source :: text [ ]) AS source, + unnest(@cron :: text [ ]) AS cron, + unnest(@start_blocks_login :: boolean [ ]) AS start_blocks_login, + unnest(@run_on_start :: boolean [ ]) AS run_on_start, + unnest(@run_on_stop :: boolean [ ]) AS run_on_stop, + unnest(@timeout :: integer [ ]) AS timeout +RETURNING workspace_agent_scripts.*; + +-- name: GetWorkspaceAgentScriptsByAgentIDs :many +SELECT * FROM workspace_agent_scripts WHERE workspace_agent_id = ANY(@ids :: uuid [ ]); diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 613000f83d7c3..74239709355d7 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1243,11 +1243,6 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. } } - // Set the default in case it was not provided (e.g. echo provider). - if prAgent.GetStartupScriptBehavior() == "" { - prAgent.StartupScriptBehavior = string(codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking) - } - agentID := uuid.New() dbAgent, err := db.InsertWorkspaceAgent(ctx, database.InsertWorkspaceAgentParams{ ID: agentID, diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index 6502a2d79675f..f45d9815b77e8 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -1557,7 +1557,6 @@ func TestInsertWorkspaceResource(t *testing.T) { Env: map[string]string{ "something": "test", }, - StartupScript: "value", OperatingSystem: "linux", Architecture: "amd64", Auth: &sdkproto.Agent_Token{ @@ -1566,7 +1565,6 @@ func TestInsertWorkspaceResource(t *testing.T) { Apps: []*sdkproto.App{{ Slug: "a", }}, - ShutdownScript: "shutdown", DisplayApps: &sdkproto.DisplayApps{ Vscode: true, PortForwardingHelper: true, @@ -1585,8 +1583,6 @@ func TestInsertWorkspaceResource(t *testing.T) { agent := agents[0] require.Equal(t, "amd64", agent.Architecture) require.Equal(t, "linux", agent.OperatingSystem) - require.Equal(t, "value", agent.StartupScript.String) - require.Equal(t, "shutdown", agent.ShutdownScript.String) want, err := json.Marshal(map[string]string{ "something": "test", }) diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index 4b49c385c80f4..f83efb028c89d 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -124,6 +124,19 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, return } + // nolint:gocritic // GetWorkspaceAgentScriptsByAgentIDs is a system function. + scripts, err := api.Database.GetWorkspaceAgentScriptsByAgentIDs(dbauthz.AsSystemRestricted(ctx), resourceAgentIDs) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching workspace agent scripts.", + Detail: err.Error(), + }) + return + } + // nolint:gocritic // GetWorkspaceResourceMetadataByResourceIDs is a system function. resourceMetadata, err := api.Database.GetWorkspaceResourceMetadataByResourceIDs(dbauthz.AsSystemRestricted(ctx), resourceIDs) if err != nil { @@ -147,9 +160,15 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, dbApps = append(dbApps, app) } } + dbScripts := make([]database.WorkspaceAgentScript, 0) + for _, script := range scripts { + if script.WorkspaceAgentID == agent.ID { + dbScripts = append(dbScripts, script) + } + } apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), convertScripts(dbScripts), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index f37770f2fb234..e37a948a0c60c 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -559,10 +559,8 @@ func ConvertWorkspaceAgent(agent database.WorkspaceAgent) WorkspaceAgent { Architecture: agent.Architecture, OperatingSystem: agent.OperatingSystem, EnvironmentVariables: agent.EnvironmentVariables.Valid, - StartupScript: agent.StartupScript.Valid, Directory: agent.Directory != "", ConnectionTimeoutSeconds: agent.ConnectionTimeoutSeconds, - ShutdownScript: agent.ShutdownScript.Valid, Subsystems: subsystems, } if agent.FirstConnectedAt.Valid { @@ -792,13 +790,11 @@ type WorkspaceAgent struct { Architecture string `json:"architecture"` OperatingSystem string `json:"operating_system"` EnvironmentVariables bool `json:"environment_variables"` - StartupScript bool `json:"startup_script"` Directory bool `json:"directory"` FirstConnectedAt *time.Time `json:"first_connected_at"` LastConnectedAt *time.Time `json:"last_connected_at"` DisconnectedAt *time.Time `json:"disconnected_at"` ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"` - ShutdownScript bool `json:"shutdown_script"` Subsystems []string `json:"subsystems"` } diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index aca56b0e397b6..ff037dcffe64d 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -56,16 +56,22 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgentParam(r) - dbApps, err := api.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID) - if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace agent applications.", - Detail: err.Error(), - }) + var ( + dbApps []database.WorkspaceApp + scripts []database.WorkspaceAgentScript + ) + + var eg errgroup.Group + eg.Go(func() (err error) { + dbApps, err = api.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID) return - } + }) + eg.Go(func() (err error) { + scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) + return + }) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), convertScripts(scripts), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -90,7 +96,7 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -100,52 +106,51 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) }) return } - dbApps, err := api.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID) - if err != nil && !xerrors.Is(err, sql.ErrNoRows) { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace agent applications.", - Detail: err.Error(), - }) - return - } - metadata, err := api.Database.GetWorkspaceAgentMetadata(ctx, workspaceAgent.ID) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace agent metadata.", - Detail: err.Error(), - }) - return - } + var ( + dbApps []database.WorkspaceApp + metadata []database.WorkspaceAgentMetadatum + resource database.WorkspaceResource + build database.WorkspaceBuild + workspace database.Workspace + owner database.User + ) - resource, err := api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace resource.", - Detail: err.Error(), - }) - return - } - build, err := api.Database.GetWorkspaceBuildByJobID(ctx, resource.JobID) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace build.", - Detail: err.Error(), - }) + var eg errgroup.Group + eg.Go(func() (err error) { + dbApps, err = api.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID) + if err != nil && !xerrors.Is(err, sql.ErrNoRows) { + return err + } + return nil + }) + eg.Go(func() (err error) { + metadata, err = api.Database.GetWorkspaceAgentMetadata(ctx, workspaceAgent.ID) return - } - workspace, err := api.Database.GetWorkspaceByID(ctx, build.WorkspaceID) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace.", - Detail: err.Error(), - }) + }) + eg.Go(func() (err error) { + resource, err = api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID) + if err != nil { + return xerrors.Errorf("getting resource by id: %w", err) + } + build, err = api.Database.GetWorkspaceBuildByJobID(ctx, resource.JobID) + if err != nil { + return xerrors.Errorf("getting workspace build by job id: %w", err) + } + workspace, err = api.Database.GetWorkspaceByID(ctx, build.WorkspaceID) + if err != nil { + return xerrors.Errorf("getting workspace by id: %w", err) + } + owner, err = api.Database.GetUserByID(ctx, workspace.OwnerID) + if err != nil { + return xerrors.Errorf("getting workspace owner by id: %w", err) + } return - } - owner, err := api.Database.GetUserByID(ctx, workspace.OwnerID) + }) + err = eg.Wait() if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching workspace owner.", + Message: "Internal error fetching workspace agent manifest.", Detail: err.Error(), }) return @@ -169,13 +174,9 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) DERPForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), GitAuthConfigs: len(api.GitAuthConfigs), EnvironmentVariables: apiAgent.EnvironmentVariables, - StartupScript: apiAgent.StartupScript, Directory: apiAgent.Directory, VSCodePortProxyURI: vscodeProxyURI, MOTDFile: workspaceAgent.MOTDFile, - StartupScriptTimeout: time.Duration(apiAgent.StartupScriptTimeoutSeconds) * time.Second, - ShutdownScript: apiAgent.ShutdownScript, - ShutdownScriptTimeout: time.Duration(apiAgent.ShutdownScriptTimeoutSeconds) * time.Second, DisableDirectConnections: api.DeploymentValues.DERP.Config.BlockDirect.Value(), Metadata: convertWorkspaceAgentMetadataDesc(metadata), }) @@ -195,7 +196,7 @@ func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Reques ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -641,7 +642,7 @@ func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Req workspaceAgent := httpmw.WorkspaceAgentParam(r) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -1289,6 +1290,24 @@ func convertApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp { return apps } +func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.WorkspaceAgentScript { + scripts := make([]codersdk.WorkspaceAgentScript, 0) + for _, dbScript := range dbScripts { + scripts = append(scripts, codersdk.WorkspaceAgentScript{ + LogSourceDisplayName: dbScript.LogSourceDisplayName, + LogSourceID: dbScript.LogSourceID, + Source: dbScript.Source, + CRON: dbScript.Cron, + RunOnStart: dbScript.RunOnStart, + RunOnStop: dbScript.RunOnStop, + StartBlocksLogin: dbScript.StartBlocksLogin, + // In the database it's stored as seconds! + Timeout: time.Duration(dbScript.Timeout) * time.Second, + }) + } + return scripts +} + func convertWorkspaceAgentMetadataDesc(mds []database.WorkspaceAgentMetadatum) []codersdk.WorkspaceAgentMetadataDescription { metadata := make([]codersdk.WorkspaceAgentMetadataDescription, 0) for _, datum := range mds { @@ -1303,7 +1322,7 @@ func convertWorkspaceAgentMetadataDesc(mds []database.WorkspaceAgentMetadatum) [ return metadata } -func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) { +func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, scripts []codersdk.WorkspaceAgentScript, agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) { var envs map[string]string if dbAgent.EnvironmentVariables.Valid { err := json.Unmarshal(dbAgent.EnvironmentVariables.RawMessage, &envs) @@ -1321,32 +1340,27 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin } workspaceAgent := codersdk.WorkspaceAgent{ - ID: dbAgent.ID, - CreatedAt: dbAgent.CreatedAt, - UpdatedAt: dbAgent.UpdatedAt, - ResourceID: dbAgent.ResourceID, - InstanceID: dbAgent.AuthInstanceID.String, - Name: dbAgent.Name, - Architecture: dbAgent.Architecture, - OperatingSystem: dbAgent.OperatingSystem, - StartupScript: dbAgent.StartupScript.String, - StartupScriptBehavior: codersdk.WorkspaceAgentStartupScriptBehavior(dbAgent.StartupScriptBehavior), - StartupScriptTimeoutSeconds: dbAgent.StartupScriptTimeoutSeconds, - LogsLength: dbAgent.LogsLength, - LogsOverflowed: dbAgent.LogsOverflowed, - Version: dbAgent.Version, - EnvironmentVariables: envs, - Directory: dbAgent.Directory, - ExpandedDirectory: dbAgent.ExpandedDirectory, - Apps: apps, - ConnectionTimeoutSeconds: dbAgent.ConnectionTimeoutSeconds, - TroubleshootingURL: troubleshootingURL, - LifecycleState: codersdk.WorkspaceAgentLifecycle(dbAgent.LifecycleState), - LoginBeforeReady: dbAgent.StartupScriptBehavior != database.StartupScriptBehaviorBlocking, - ShutdownScript: dbAgent.ShutdownScript.String, - ShutdownScriptTimeoutSeconds: dbAgent.ShutdownScriptTimeoutSeconds, - Subsystems: subsystems, - DisplayApps: convertDisplayApps(dbAgent.DisplayApps), + ID: dbAgent.ID, + CreatedAt: dbAgent.CreatedAt, + UpdatedAt: dbAgent.UpdatedAt, + ResourceID: dbAgent.ResourceID, + InstanceID: dbAgent.AuthInstanceID.String, + Name: dbAgent.Name, + Architecture: dbAgent.Architecture, + OperatingSystem: dbAgent.OperatingSystem, + Scripts: scripts, + LogsLength: dbAgent.LogsLength, + LogsOverflowed: dbAgent.LogsOverflowed, + Version: dbAgent.Version, + EnvironmentVariables: envs, + Directory: dbAgent.Directory, + ExpandedDirectory: dbAgent.ExpandedDirectory, + Apps: apps, + ConnectionTimeoutSeconds: dbAgent.ConnectionTimeoutSeconds, + TroubleshootingURL: troubleshootingURL, + LifecycleState: codersdk.WorkspaceAgentLifecycle(dbAgent.LifecycleState), + Subsystems: subsystems, + DisplayApps: convertDisplayApps(dbAgent.DisplayApps), } node := coordinator.Node(dbAgent.ID) if node != nil { diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 99bf36a33313e..56144726bed68 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -76,6 +76,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { data.metadata, data.agents, data.apps, + data.scripts, data.templateVersions[0], ) if err != nil { @@ -190,6 +191,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { data.metadata, data.agents, data.apps, + data.scripts, data.templateVersions, ) if err != nil { @@ -278,6 +280,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ data.metadata, data.agents, data.apps, + data.scripts, data.templateVersions[0], ) if err != nil { @@ -398,6 +401,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { []database.WorkspaceResourceMetadatum{}, []database.WorkspaceAgent{}, []database.WorkspaceApp{}, + []database.WorkspaceAgentScript{}, database.TemplateVersion{}, ) if err != nil { @@ -631,6 +635,7 @@ type workspaceBuildsData struct { metadata []database.WorkspaceResourceMetadatum agents []database.WorkspaceAgent apps []database.WorkspaceApp + scripts []database.WorkspaceAgentScript } func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.Workspace, workspaceBuilds []database.WorkspaceBuild) (workspaceBuildsData, error) { @@ -715,6 +720,12 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W return workspaceBuildsData{}, xerrors.Errorf("fetching workspace apps: %w", err) } + // nolint:gocritic // Getting workspace scripts by agent IDs is a system function. + scripts, err := api.Database.GetWorkspaceAgentScriptsByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs) + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return workspaceBuildsData{}, xerrors.Errorf("fetching workspace agent scripts: %w", err) + } + return workspaceBuildsData{ users: users, jobs: jobs, @@ -723,6 +734,7 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W metadata: metadata, agents: agents, apps: apps, + scripts: scripts, }, nil } @@ -735,6 +747,7 @@ func (api *API) convertWorkspaceBuilds( resourceMetadata []database.WorkspaceResourceMetadatum, resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, + agentScripts []database.WorkspaceAgentScript, templateVersions []database.TemplateVersion, ) ([]codersdk.WorkspaceBuild, error) { workspaceByID := map[uuid.UUID]database.Workspace{} @@ -775,6 +788,7 @@ func (api *API) convertWorkspaceBuilds( resourceMetadata, resourceAgents, agentApps, + agentScripts, templateVersion, ) if err != nil { @@ -796,6 +810,7 @@ func (api *API) convertWorkspaceBuild( resourceMetadata []database.WorkspaceResourceMetadatum, resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, + agentScripts []database.WorkspaceAgentScript, templateVersion database.TemplateVersion, ) (codersdk.WorkspaceBuild, error) { userByID := map[uuid.UUID]database.User{} @@ -818,6 +833,10 @@ func (api *API) convertWorkspaceBuild( for _, app := range agentApps { appsByAgentID[app.AgentID] = append(appsByAgentID[app.AgentID], app) } + scriptsByAgentID := map[uuid.UUID][]database.WorkspaceAgentScript{} + for _, script := range agentScripts { + scriptsByAgentID[script.WorkspaceAgentID] = append(scriptsByAgentID[script.WorkspaceAgentID], script) + } owner, exists := userByID[workspace.OwnerID] if !exists { @@ -831,8 +850,9 @@ func (api *API) convertWorkspaceBuild( apiAgents := make([]codersdk.WorkspaceAgent, 0) for _, agent := range agents { apps := appsByAgentID[agent.ID] + scripts := scriptsByAgentID[agent.ID] apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(apps), api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(apps), convertScripts(scripts), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 7b6ad9f0b8c5d..cd264c2ba4d37 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -534,6 +534,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req []database.WorkspaceResourceMetadatum{}, []database.WorkspaceAgent{}, []database.WorkspaceApp{}, + []database.WorkspaceAgentScript{}, database.TemplateVersion{}, ) if err != nil { @@ -1118,6 +1119,7 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa data.metadata, data.agents, data.apps, + data.scripts, data.templateVersions, ) if err != nil { diff --git a/codersdk/agentsdk/logs_test.go b/codersdk/agentsdk/logs_test.go index 7c1d7b0bf814c..15c9888081d56 100644 --- a/codersdk/agentsdk/logs_test.go +++ b/codersdk/agentsdk/logs_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/exp/slices" @@ -39,12 +40,10 @@ func TestStartupLogsWriter_Write(t *testing.T) { ctx: context.Background(), level: codersdk.LogLevelInfo, writes: []string{"hello world\n"}, - source: codersdk.WorkspaceAgentLogSourceShutdownScript, want: []agentsdk.Log{ { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceShutdownScript, }, }, }, @@ -330,7 +329,7 @@ func TestStartupLogsSender(t *testing.T) { return nil } - sendLog, flushAndClose := agentsdk.LogsSender(patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug)) + sendLog, flushAndClose := agentsdk.LogsSender(uuid.New(), patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug)) defer func() { _ = flushAndClose(ctx) }() diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index c6a1cc9db7673..51786f0b06180 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -167,16 +167,14 @@ type WorkspaceAgent struct { Version string `json:"version"` Apps []WorkspaceApp `json:"apps"` // DERPLatency is mapped by region name (e.g. "New York City", "Seattle"). - DERPLatency map[string]DERPRegion `json:"latency,omitempty"` - ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"` - TroubleshootingURL string `json:"troubleshooting_url"` - // Deprecated: Use StartupScriptBehavior instead. - LoginBeforeReady bool `json:"login_before_ready"` - Subsystems []AgentSubsystem `json:"subsystems"` - Health WorkspaceAgentHealth `json:"health"` // Health reports the health of the agent. - DisplayApps []DisplayApp `json:"display_apps"` - LogSources []WorkspaceAgentLogSource `json:"log_sources"` - Scripts []WorkspaceAgentScript `json:"scripts"` + DERPLatency map[string]DERPRegion `json:"latency,omitempty"` + ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"` + TroubleshootingURL string `json:"troubleshooting_url"` + Subsystems []AgentSubsystem `json:"subsystems"` + Health WorkspaceAgentHealth `json:"health"` // Health reports the health of the agent. + DisplayApps []DisplayApp `json:"display_apps"` + LogSources []WorkspaceAgentLogSource `json:"log_sources"` + Scripts []WorkspaceAgentScript `json:"scripts"` } type WorkspaceAgentLogSource struct { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 928ffde5cead0..cdfc915e2452e 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1344,7 +1344,6 @@ export interface WorkspaceAgent { readonly latency?: Record readonly connection_timeout_seconds: number readonly troubleshooting_url: string - readonly login_before_ready: boolean readonly subsystems: AgentSubsystem[] readonly health: WorkspaceAgentHealth readonly display_apps: DisplayApp[] @@ -1376,6 +1375,7 @@ export interface WorkspaceAgentLog { readonly created_at: string readonly output: string readonly level: LogLevel + readonly log_source_id: string } // From codersdk/workspaceagents.go @@ -1412,10 +1412,13 @@ export interface WorkspaceAgentMetadataResult { // From codersdk/workspaceagents.go export interface WorkspaceAgentScript { + readonly log_source_display_name: string readonly log_source_id: string - readonly script: string - readonly schedule: string - readonly login_before_ready: boolean + readonly source: string + readonly cron: string + readonly run_on_start: boolean + readonly run_on_stop: boolean + readonly start_blocks_login: boolean readonly timeout_seconds: number } From 58964c9cf494075c036148def4a426a6b40def3a Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 19:03:36 +0000 Subject: [PATCH 06/59] Finish the piping --- agent/agent.go | 73 -- agent/agent_test.go | 12 +- agent/agentscripts/agentscripts.go | 28 +- agent/agentscripts/agentscripts_test.go | 3 +- coderd/apidoc/docs.go | 134 ++-- coderd/apidoc/swagger.json | 129 ++-- coderd/database/dbauthz/dbauthz.go | 21 +- coderd/database/dbfake/dbfake.go | 71 +- coderd/database/dbmetrics/dbmetrics.go | 7 + coderd/database/dbmock/dbmock.go | 15 + coderd/database/dump.sql | 2 +- .../000154_workspace_agent_script.up.sql | 2 +- coderd/database/models.go | 20 +- coderd/database/querier.go | 1 + coderd/database/queries.sql.go | 67 +- coderd/database/queries/workspaceagents.sql | 3 + coderd/database/queries/workspacescripts.sql | 4 +- coderd/provisionerjobs.go | 21 +- coderd/workspaceagents.go | 55 +- coderd/workspacebuilds.go | 15 +- coderd/workspaces.go | 2 + codersdk/workspaceagents.go | 16 +- docs/api/agents.md | 83 ++- docs/api/builds.md | 682 ++++++++++-------- docs/api/schemas.md | 321 ++++++--- docs/api/templates.md | 460 ++++++------ docs/api/workspaces.md | 135 +++- provisioner/terraform/resources.go | 2 + provisionersdk/proto/provisioner.pb.go | 415 +++++------ provisionersdk/proto/provisioner.proto | 1 + site/src/api/typesGenerated.ts | 2 +- 31 files changed, 1660 insertions(+), 1142 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 1c58d14673ee9..d93e6334fb5c8 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -12,7 +12,6 @@ import ( "net/http" "net/netip" "os" - "os/exec" "os/user" "path/filepath" "sort" @@ -975,78 +974,6 @@ func (a *agent) runDERPMapSubscriber(ctx context.Context, network *tailnet.Conn) } } -func (a *agent) runScript(ctx context.Context, script codersdk.WorkspaceAgentScript) (err error) { - if script.Source == "" { - return nil - } - - logger := a.logger.With(slog.F("log_source", script.LogSourceDisplayName)) - - logger.Info(ctx, "running script", slog.F("script", script.Source)) - fileWriter, err := a.filesystem.OpenFile(filepath.Join(a.logDir, fmt.Sprintf("coder-%s-script.log", script.LogSourceDisplayName)), os.O_CREATE|os.O_RDWR, 0o600) - if err != nil { - return xerrors.Errorf("open %s script log file: %w", script.LogSourceDisplayName, err) - } - defer func() { - err := fileWriter.Close() - if err != nil { - logger.Warn(ctx, fmt.Sprintf("close %s script log file", script.LogSourceDisplayName), slog.Error(err)) - } - }() - - cmdPty, err := a.sshServer.CreateCommand(ctx, script.Source, nil) - if err != nil { - return xerrors.Errorf("%s script: create command: %w", script.LogSourceDisplayName, err) - } - cmd := cmdPty.AsExec() - - send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, a.client.PatchLogs, logger) - // If ctx is canceled here (or in a writer below), we may be - // discarding logs, but that's okay because we're shutting down - // anyway. We could consider creating a new context here if we - // want better control over flush during shutdown. - defer func() { - if err := flushAndClose(ctx); err != nil { - logger.Warn(ctx, "flush startup logs failed", slog.Error(err)) - } - }() - - infoW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelInfo) - defer infoW.Close() - errW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelError) - defer errW.Close() - cmd.Stdout = io.MultiWriter(fileWriter, infoW) - cmd.Stderr = io.MultiWriter(fileWriter, errW) - - start := time.Now() - defer func() { - end := time.Now() - execTime := end.Sub(start) - exitCode := 0 - if err != nil { - exitCode = 255 // Unknown status. - var exitError *exec.ExitError - if xerrors.As(err, &exitError) { - exitCode = exitError.ExitCode() - } - logger.Warn(ctx, fmt.Sprintf("%s script failed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode), slog.Error(err)) - } else { - logger.Info(ctx, fmt.Sprintf("%s script completed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode)) - } - }() - - err = cmd.Run() - if err != nil { - // cmd.Run does not return a context canceled error, it returns "signal: killed". - if ctx.Err() != nil { - return ctx.Err() - } - - return xerrors.Errorf("%s script: run: %w", script.LogSourceDisplayName, err) - } - return nil -} - func (a *agent) handleReconnectingPTY(ctx context.Context, logger slog.Logger, msg codersdk.WorkspaceAgentReconnectingPTYInit, conn net.Conn) (retErr error) { defer conn.Close() a.metrics.connectionsTotal.Add(1) diff --git a/agent/agent_test.go b/agent/agent_test.go index 5cc3863fd3f05..1f7d5baa4158d 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1416,13 +1416,13 @@ func TestAgent_Lifecycle(t *testing.T) { agentsdk.Manifest{ DERPMap: derpMap, Scripts: []codersdk.WorkspaceAgentScript{{ - LogSourceDisplayName: "startup", - Source: "echo 1", - RunOnStart: true, + LogPath: "coder-startup-script.log", + Source: "echo 1", + RunOnStart: true, }, { - LogSourceDisplayName: "shutdown", - Source: "echo " + expected, - RunOnStop: true, + LogPath: "coder-shutdown-script.log", + Source: "echo " + expected, + RunOnStop: true, }}, }, make(chan *agentsdk.Stats, 50), diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index b03438716cb71..9654dd8daf7a5 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -99,7 +99,7 @@ func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) eg.Go(func() error { err := r.run(script) if err != nil { - return xerrors.Errorf("run agent script %q: %w", script.LogSourceDisplayName, err) + return xerrors.Errorf("run agent script %q: %w", script.LogPath, err) } return nil }) @@ -112,17 +112,25 @@ func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) // If the process does not exit after a few seconds, it is forcefully killed. // This function immediately returns after a timeout, and does not wait for the process to exit. func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { - logger := r.Logger.With(slog.F("log_source", script.LogSourceDisplayName)) + logger := r.Logger.With(slog.F("log_source", script.LogPath)) ctx := r.ctx logger.Info(ctx, "running agent script", slog.F("script", script.Source)) - fileWriter, err := r.Filesystem.OpenFile(filepath.Join(r.LogDir, fmt.Sprintf("coder-%s-script.log", script.LogSourceDisplayName)), os.O_CREATE|os.O_RDWR, 0o600) + + logPath := script.LogPath + if logPath == "" { + logPath = fmt.Sprintf("coder-%s-script.log", script.LogSourceID) + } + if !filepath.IsAbs(logPath) { + logPath = filepath.Join(r.LogDir, logPath) + } + fileWriter, err := r.Filesystem.OpenFile(logPath, os.O_CREATE|os.O_RDWR, 0o600) if err != nil { - return xerrors.Errorf("open %s script log file: %w", script.LogSourceDisplayName, err) + return xerrors.Errorf("open %s script log file: %w", logPath, err) } defer func() { err := fileWriter.Close() if err != nil { - logger.Warn(ctx, fmt.Sprintf("close %s script log file", script.LogSourceDisplayName), slog.Error(err)) + logger.Warn(ctx, fmt.Sprintf("close %s script log file", logPath), slog.Error(err)) } }() @@ -136,7 +144,7 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Source, nil) if err != nil { - return xerrors.Errorf("%s script: create command: %w", script.LogSourceDisplayName, err) + return xerrors.Errorf("%s script: create command: %w", logPath, err) } cmd = cmdPty.AsExec() @@ -169,15 +177,15 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { if xerrors.As(err, &exitError) { exitCode = exitError.ExitCode() } - logger.Warn(ctx, fmt.Sprintf("%s script failed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode), slog.Error(err)) + logger.Warn(ctx, fmt.Sprintf("%s script failed", logPath), slog.F("execution_time", execTime), slog.F("exit_code", exitCode), slog.Error(err)) } else { - logger.Info(ctx, fmt.Sprintf("%s script completed", script.LogSourceDisplayName), slog.F("execution_time", execTime), slog.F("exit_code", exitCode)) + logger.Info(ctx, fmt.Sprintf("%s script completed", logPath), slog.F("execution_time", execTime), slog.F("exit_code", exitCode)) } }() err = cmd.Start() if err != nil { - return xerrors.Errorf("%s script: start command: %w", script.LogSourceDisplayName, err) + return xerrors.Errorf("%s script: start command: %w", logPath, err) } // timeout stores whether the process timed out then was gracefully killed. @@ -199,7 +207,7 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { cmdDone <- cmd.Wait() }) if err != nil { - return xerrors.Errorf("%s script: track command goroutine: %w", script.LogSourceDisplayName, err) + return xerrors.Errorf("%s script: track command goroutine: %w", logPath, err) } select { case <-timeout: diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index 22450302df7a7..1b2643233c991 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -31,8 +31,7 @@ func TestExecuteBasic(t *testing.T) { }) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - LogSourceDisplayName: "test", - Source: "echo hello", + Source: "echo hello", }}) require.NoError(t, err) require.NoError(t, runner.Execute(func(script codersdk.WorkspaceAgentScript) bool { diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index bc382c82221e3..e8877ab06405d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6398,9 +6398,6 @@ const docTemplate = `{ }, "output": { "type": "string" - }, - "source": { - "$ref": "#/definitions/codersdk.WorkspaceAgentLogSource" } } }, @@ -6447,17 +6444,11 @@ const docTemplate = `{ "motd_file": { "type": "string" }, - "shutdown_script": { - "type": "string" - }, - "shutdown_script_timeout": { - "type": "integer" - }, - "startup_script": { - "type": "string" - }, - "startup_script_timeout": { - "type": "integer" + "scripts": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceAgentScript" + } }, "vscode_port_proxy_uri": { "type": "string" @@ -6466,7 +6457,13 @@ const docTemplate = `{ }, "agentsdk.PatchLogs": { "type": "object", + "required": [ + "log_source_id" + ], "properties": { + "log_source_id": { + "type": "string" + }, "logs": { "type": "array", "items": { @@ -10681,9 +10678,11 @@ const docTemplate = `{ "lifecycle_state": { "$ref": "#/definitions/codersdk.WorkspaceAgentLifecycle" }, - "login_before_ready": { - "description": "Deprecated: Use StartupScriptBehavior instead.", - "type": "boolean" + "log_sources": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceAgentLogSource" + } }, "logs_length": { "type": "integer" @@ -10705,26 +10704,16 @@ const docTemplate = `{ "type": "string", "format": "uuid" }, - "shutdown_script": { - "type": "string" - }, - "shutdown_script_timeout_seconds": { - "type": "integer" + "scripts": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceAgentScript" + } }, "started_at": { "type": "string", "format": "date-time" }, - "startup_script": { - "type": "string" - }, - "startup_script_behavior": { - "$ref": "#/definitions/codersdk.WorkspaceAgentStartupScriptBehavior" - }, - "startup_script_timeout_seconds": { - "description": "StartupScriptTimeoutSeconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout.", - "type": "integer" - }, "status": { "$ref": "#/definitions/codersdk.WorkspaceAgentStatus" }, @@ -10841,29 +10830,37 @@ const docTemplate = `{ "level": { "$ref": "#/definitions/codersdk.LogLevel" }, + "log_source_id": { + "type": "string", + "format": "uuid" + }, "output": { "type": "string" } } }, "codersdk.WorkspaceAgentLogSource": { - "type": "string", - "enum": [ - "startup_script", - "shutdown_script", - "kubernetes", - "envbox", - "envbuilder", - "external" - ], - "x-enum-varnames": [ - "WorkspaceAgentLogSourceStartupScript", - "WorkspaceAgentLogSourceShutdownScript", - "WorkspaceAgentLogSourceKubernetes", - "WorkspaceAgentLogSourceEnvbox", - "WorkspaceAgentLogSourceEnvbuilder", - "WorkspaceAgentLogSourceExternal" - ] + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "display_name": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "workspace_agent_id": { + "type": "string", + "format": "uuid" + } + } }, "codersdk.WorkspaceAgentMetadataDescription": { "type": "object", @@ -10885,16 +10882,35 @@ const docTemplate = `{ } } }, - "codersdk.WorkspaceAgentStartupScriptBehavior": { - "type": "string", - "enum": [ - "blocking", - "non-blocking" - ], - "x-enum-varnames": [ - "WorkspaceAgentStartupScriptBehaviorBlocking", - "WorkspaceAgentStartupScriptBehaviorNonBlocking" - ] + "codersdk.WorkspaceAgentScript": { + "type": "object", + "properties": { + "cron": { + "type": "string" + }, + "log_path": { + "type": "string" + }, + "log_source_id": { + "type": "string", + "format": "uuid" + }, + "run_on_start": { + "type": "boolean" + }, + "run_on_stop": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "start_blocks_login": { + "type": "boolean" + }, + "timeout_seconds": { + "type": "integer" + } + } }, "codersdk.WorkspaceAgentStatus": { "type": "string", diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 50c68490d8415..8686f94b46a86 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5638,9 +5638,6 @@ }, "output": { "type": "string" - }, - "source": { - "$ref": "#/definitions/codersdk.WorkspaceAgentLogSource" } } }, @@ -5687,17 +5684,11 @@ "motd_file": { "type": "string" }, - "shutdown_script": { - "type": "string" - }, - "shutdown_script_timeout": { - "type": "integer" - }, - "startup_script": { - "type": "string" - }, - "startup_script_timeout": { - "type": "integer" + "scripts": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceAgentScript" + } }, "vscode_port_proxy_uri": { "type": "string" @@ -5706,7 +5697,11 @@ }, "agentsdk.PatchLogs": { "type": "object", + "required": ["log_source_id"], "properties": { + "log_source_id": { + "type": "string" + }, "logs": { "type": "array", "items": { @@ -9684,9 +9679,11 @@ "lifecycle_state": { "$ref": "#/definitions/codersdk.WorkspaceAgentLifecycle" }, - "login_before_ready": { - "description": "Deprecated: Use StartupScriptBehavior instead.", - "type": "boolean" + "log_sources": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceAgentLogSource" + } }, "logs_length": { "type": "integer" @@ -9708,26 +9705,16 @@ "type": "string", "format": "uuid" }, - "shutdown_script": { - "type": "string" - }, - "shutdown_script_timeout_seconds": { - "type": "integer" + "scripts": { + "type": "array", + "items": { + "$ref": "#/definitions/codersdk.WorkspaceAgentScript" + } }, "started_at": { "type": "string", "format": "date-time" }, - "startup_script": { - "type": "string" - }, - "startup_script_behavior": { - "$ref": "#/definitions/codersdk.WorkspaceAgentStartupScriptBehavior" - }, - "startup_script_timeout_seconds": { - "description": "StartupScriptTimeoutSeconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout.", - "type": "integer" - }, "status": { "$ref": "#/definitions/codersdk.WorkspaceAgentStatus" }, @@ -9844,29 +9831,37 @@ "level": { "$ref": "#/definitions/codersdk.LogLevel" }, + "log_source_id": { + "type": "string", + "format": "uuid" + }, "output": { "type": "string" } } }, "codersdk.WorkspaceAgentLogSource": { - "type": "string", - "enum": [ - "startup_script", - "shutdown_script", - "kubernetes", - "envbox", - "envbuilder", - "external" - ], - "x-enum-varnames": [ - "WorkspaceAgentLogSourceStartupScript", - "WorkspaceAgentLogSourceShutdownScript", - "WorkspaceAgentLogSourceKubernetes", - "WorkspaceAgentLogSourceEnvbox", - "WorkspaceAgentLogSourceEnvbuilder", - "WorkspaceAgentLogSourceExternal" - ] + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time" + }, + "display_name": { + "type": "string" + }, + "icon": { + "type": "string" + }, + "id": { + "type": "string", + "format": "uuid" + }, + "workspace_agent_id": { + "type": "string", + "format": "uuid" + } + } }, "codersdk.WorkspaceAgentMetadataDescription": { "type": "object", @@ -9888,13 +9883,35 @@ } } }, - "codersdk.WorkspaceAgentStartupScriptBehavior": { - "type": "string", - "enum": ["blocking", "non-blocking"], - "x-enum-varnames": [ - "WorkspaceAgentStartupScriptBehaviorBlocking", - "WorkspaceAgentStartupScriptBehaviorNonBlocking" - ] + "codersdk.WorkspaceAgentScript": { + "type": "object", + "properties": { + "cron": { + "type": "string" + }, + "log_path": { + "type": "string" + }, + "log_source_id": { + "type": "string", + "format": "uuid" + }, + "run_on_start": { + "type": "boolean" + }, + "run_on_stop": { + "type": "boolean" + }, + "source": { + "type": "string" + }, + "start_blocks_login": { + "type": "boolean" + }, + "timeout_seconds": { + "type": "integer" + } + } }, "codersdk.WorkspaceAgentStatus": { "type": "string", diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 5bccc02b9bd2e..5154e5d6bfe3b 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -1542,6 +1542,13 @@ func (q *querier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uu return q.db.GetWorkspaceAgentLifecycleStateByID(ctx, id) } +func (q *querier) GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) { + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil { + return nil, err + } + return q.db.GetWorkspaceAgentLogSourcesByAgentIDs(ctx, ids) +} + func (q *querier) GetWorkspaceAgentLogsAfter(ctx context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) { _, err := q.GetWorkspaceAgentByID(ctx, arg.AgentID) if err != nil { @@ -1565,7 +1572,10 @@ func (q *querier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentI } func (q *querier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { - panic("not implemented") + if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceSystem); err != nil { + return nil, err + } + return q.db.GetWorkspaceAgentScriptsByAgentIDs(ctx, ids) } func (q *querier) GetWorkspaceAgentStats(ctx context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) { @@ -2026,8 +2036,6 @@ func (q *querier) InsertWorkspace(ctx context.Context, arg database.InsertWorksp return insert(q.log, q.auth, obj, q.db.InsertWorkspace)(ctx, arg) } -// Provisionerd server functions - func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertWorkspaceAgentParams) (database.WorkspaceAgent, error) { if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil { return database.WorkspaceAgent{}, err @@ -2036,7 +2044,7 @@ func (q *querier) InsertWorkspaceAgent(ctx context.Context, arg database.InsertW } func (q *querier) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { - panic("not implemented") + return q.db.InsertWorkspaceAgentLogSources(ctx, arg) } func (q *querier) InsertWorkspaceAgentLogs(ctx context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { @@ -2054,7 +2062,10 @@ func (q *querier) InsertWorkspaceAgentMetadata(ctx context.Context, arg database } func (q *querier) InsertWorkspaceAgentScripts(ctx context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { - panic("not implemented") + if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceSystem); err != nil { + return []database.WorkspaceAgentScript{}, err + } + return q.db.InsertWorkspaceAgentScripts(ctx, arg) } func (q *querier) InsertWorkspaceAgentStat(ctx context.Context, arg database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) { diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 8e2b21d967d99..8e2a276ccbcec 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -135,6 +135,8 @@ type data struct { workspaceAgents []database.WorkspaceAgent workspaceAgentMetadata []database.WorkspaceAgentMetadatum workspaceAgentLogs []database.WorkspaceAgentLog + workspaceAgentLogSources []database.WorkspaceAgentLogSource + workspaceAgentScripts []database.WorkspaceAgentScript workspaceApps []database.WorkspaceApp workspaceAppStatsLastInsertID int64 workspaceAppStats []database.WorkspaceAppStat @@ -3069,6 +3071,22 @@ func (q *FakeQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, i }, nil } +func (q *FakeQuerier) GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) { + q.mutex.RLock() + defer q.mutex.RUnlock() + + logSources := make([]database.WorkspaceAgentLogSource, 0) + for _, logSource := range q.workspaceAgentLogSources { + for _, id := range ids { + if logSource.WorkspaceAgentID == id { + logSources = append(logSources, logSource) + break + } + } + } + return logSources, nil +} + func (q *FakeQuerier) GetWorkspaceAgentLogsAfter(_ context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) { if err := validateDatabaseType(arg); err != nil { return nil, err @@ -3104,7 +3122,19 @@ func (q *FakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, workspaceAgen } func (q *FakeQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { - panic("not implemented") + q.mutex.RLock() + defer q.mutex.RUnlock() + + scripts := make([]database.WorkspaceAgentScript, 0) + for _, script := range q.workspaceAgentScripts { + for _, id := range ids { + if script.WorkspaceAgentID == id { + scripts = append(scripts, script) + break + } + } + } + return scripts, nil } func (q *FakeQuerier) GetWorkspaceAgentStats(_ context.Context, createdAfter time.Time) ([]database.GetWorkspaceAgentStatsRow, error) { @@ -4443,7 +4473,22 @@ func (q *FakeQuerier) InsertWorkspaceAgentLogSources(ctx context.Context, arg da return nil, err } - panic("not implemented") + q.mutex.Lock() + defer q.mutex.Unlock() + + logSources := make([]database.WorkspaceAgentLogSource, 0) + for index, source := range arg.ID { + logSource := database.WorkspaceAgentLogSource{ + ID: source, + WorkspaceAgentID: arg.WorkspaceAgentID, + CreatedAt: arg.CreatedAt[index], + DisplayName: arg.DisplayName[index], + Icon: arg.Icon[index], + } + logSources = append(logSources, logSource) + } + q.workspaceAgentLogSources = append(q.workspaceAgentLogSources, logSources...) + return logSources, nil } func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.InsertWorkspaceAgentLogsParams) ([]database.WorkspaceAgentLog, error) { @@ -4515,7 +4560,27 @@ func (q *FakeQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg datab return nil, err } - panic("not implemented") + q.mutex.Lock() + defer q.mutex.Unlock() + + scripts := make([]database.WorkspaceAgentScript, 0) + for index, source := range arg.LogSourceID { + script := database.WorkspaceAgentScript{ + LogSourceID: source, + WorkspaceAgentID: arg.WorkspaceAgentID, + LogPath: arg.LogPath[index], + Source: arg.Source[index], + Cron: arg.Cron[index], + StartBlocksLogin: arg.StartBlocksLogin[index], + RunOnStart: arg.RunOnStart[index], + RunOnStop: arg.RunOnStop[index], + Timeout: arg.Timeout[index], + CreatedAt: arg.CreatedAt[index], + } + scripts = append(scripts, script) + } + q.workspaceAgentScripts = append(q.workspaceAgentScripts, scripts...) + return scripts, nil } func (q *FakeQuerier) InsertWorkspaceAgentStat(_ context.Context, p database.InsertWorkspaceAgentStatParams) (database.WorkspaceAgentStat, error) { diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index a79e7318f888e..64e443268563b 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -816,6 +816,13 @@ func (m metricsStore) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, i return r0, r1 } +func (m metricsStore) GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) { + start := time.Now() + r0, r1 := m.s.GetWorkspaceAgentLogSourcesByAgentIDs(ctx, ids) + m.queryLatencies.WithLabelValues("GetWorkspaceAgentLogSourcesByAgentIDs").Observe(time.Since(start).Seconds()) + return r0, r1 +} + func (m metricsStore) GetWorkspaceAgentLogsAfter(ctx context.Context, arg database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) { start := time.Now() r0, r1 := m.s.GetWorkspaceAgentLogsAfter(ctx, arg) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index 421e8c2569a8b..0b3b2842f32a8 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -1691,6 +1691,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentLifecycleStateByID(arg0, arg1 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentLifecycleStateByID", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentLifecycleStateByID), arg0, arg1) } +// GetWorkspaceAgentLogSourcesByAgentIDs mocks base method. +func (m *MockStore) GetWorkspaceAgentLogSourcesByAgentIDs(arg0 context.Context, arg1 []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetWorkspaceAgentLogSourcesByAgentIDs", arg0, arg1) + ret0, _ := ret[0].([]database.WorkspaceAgentLogSource) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetWorkspaceAgentLogSourcesByAgentIDs indicates an expected call of GetWorkspaceAgentLogSourcesByAgentIDs. +func (mr *MockStoreMockRecorder) GetWorkspaceAgentLogSourcesByAgentIDs(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAgentLogSourcesByAgentIDs", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAgentLogSourcesByAgentIDs), arg0, arg1) +} + // GetWorkspaceAgentLogsAfter mocks base method. func (m *MockStore) GetWorkspaceAgentLogsAfter(arg0 context.Context, arg1 database.GetWorkspaceAgentLogsAfterParams) ([]database.WorkspaceAgentLog, error) { m.ctrl.T.Helper() diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 06f1cbb407bec..92a6f8c0dc5f6 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -727,7 +727,7 @@ CREATE UNLOGGED TABLE workspace_agent_metadata ( CREATE TABLE workspace_agent_scripts ( workspace_agent_id uuid NOT NULL, log_source_id uuid NOT NULL, - log_source_display_name character varying(127) NOT NULL, + log_path text NOT NULL, created_at timestamp with time zone NOT NULL, source text NOT NULL, cron text NOT NULL, diff --git a/coderd/database/migrations/000154_workspace_agent_script.up.sql b/coderd/database/migrations/000154_workspace_agent_script.up.sql index a9d605e505227..005d4d88a3ff2 100644 --- a/coderd/database/migrations/000154_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000154_workspace_agent_script.up.sql @@ -11,7 +11,7 @@ CREATE TABLE workspace_agent_log_sources ( CREATE TABLE workspace_agent_scripts ( workspace_agent_id uuid NOT NULL, log_source_id uuid NOT NULL, - log_source_display_name varchar(127) NOT NULL, + log_path text NOT NULL, created_at timestamptz NOT NULL, source text NOT NULL, cron text NOT NULL, diff --git a/coderd/database/models.go b/coderd/database/models.go index 0ce7ced36528b..b9f1a064fb376 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -1976,16 +1976,16 @@ type WorkspaceAgentMetadatum struct { } type WorkspaceAgentScript struct { - WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` - LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` - LogSourceDisplayName string `db:"log_source_display_name" json:"log_source_display_name"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - Source string `db:"source" json:"source"` - Cron string `db:"cron" json:"cron"` - StartBlocksLogin bool `db:"start_blocks_login" json:"start_blocks_login"` - RunOnStart bool `db:"run_on_start" json:"run_on_start"` - RunOnStop bool `db:"run_on_stop" json:"run_on_stop"` - Timeout int32 `db:"timeout" json:"timeout"` + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` + LogPath string `db:"log_path" json:"log_path"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Source string `db:"source" json:"source"` + Cron string `db:"cron" json:"cron"` + StartBlocksLogin bool `db:"start_blocks_login" json:"start_blocks_login"` + RunOnStart bool `db:"run_on_start" json:"run_on_start"` + RunOnStop bool `db:"run_on_stop" json:"run_on_stop"` + Timeout int32 `db:"timeout" json:"timeout"` } type WorkspaceAgentStat struct { diff --git a/coderd/database/querier.go b/coderd/database/querier.go index efae925a76d44..e30a38f73679e 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -160,6 +160,7 @@ type sqlcQuerier interface { GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (WorkspaceAgent, error) GetWorkspaceAgentByInstanceID(ctx context.Context, authInstanceID string) (WorkspaceAgent, error) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id uuid.UUID) (GetWorkspaceAgentLifecycleStateByIDRow, error) + GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentLogSource, error) GetWorkspaceAgentLogsAfter(ctx context.Context, arg GetWorkspaceAgentLogsAfterParams) ([]WorkspaceAgentLog, error) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAgentID uuid.UUID) ([]WorkspaceAgentMetadatum, error) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 3ca34c95bc2d3..d6a61b1acf25a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -6585,6 +6585,39 @@ func (q *sqlQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, id return i, err } +const getWorkspaceAgentLogSourcesByAgentIDs = `-- name: GetWorkspaceAgentLogSourcesByAgentIDs :many +SELECT workspace_agent_id, id, created_at, display_name, icon FROM workspace_agent_log_sources WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +` + +func (q *sqlQuerier) GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentLogSource, error) { + rows, err := q.db.QueryContext(ctx, getWorkspaceAgentLogSourcesByAgentIDs, pq.Array(ids)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentLogSource + for rows.Next() { + var i WorkspaceAgentLogSource + if err := rows.Scan( + &i.WorkspaceAgentID, + &i.ID, + &i.CreatedAt, + &i.DisplayName, + &i.Icon, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const getWorkspaceAgentLogsAfter = `-- name: GetWorkspaceAgentLogsAfter :many SELECT agent_id, created_at, output, id, level, log_source_id @@ -9034,7 +9067,7 @@ func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg In } const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many -SELECT workspace_agent_id, log_source_id, log_source_display_name, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +SELECT workspace_agent_id, log_source_id, log_path, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) ` func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { @@ -9049,7 +9082,7 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids if err := rows.Scan( &i.WorkspaceAgentID, &i.LogSourceID, - &i.LogSourceDisplayName, + &i.LogPath, &i.CreatedAt, &i.Source, &i.Cron, @@ -9073,11 +9106,11 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_source_display_name, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) SELECT $1 :: uuid AS workspace_agent_id, unnest($2 :: uuid [ ]) AS log_source_id, - unnest($3 :: varchar(127) [ ]) AS log_source_display_name, + unnest($3 :: text [ ]) AS log_path, unnest($4 :: timestamptz [ ]) AS created_at, unnest($5 :: text [ ]) AS source, unnest($6 :: text [ ]) AS cron, @@ -9085,27 +9118,27 @@ SELECT unnest($8 :: boolean [ ]) AS run_on_start, unnest($9 :: boolean [ ]) AS run_on_stop, unnest($10 :: integer [ ]) AS timeout -RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_source_display_name, workspace_agent_scripts.created_at, workspace_agent_scripts.source, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout +RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.source, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout ` type InsertWorkspaceAgentScriptsParams struct { - WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` - LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` - LogSourceDisplayName []string `db:"log_source_display_name" json:"log_source_display_name"` - CreatedAt []time.Time `db:"created_at" json:"created_at"` - Source []string `db:"source" json:"source"` - Cron []string `db:"cron" json:"cron"` - StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` - RunOnStart []bool `db:"run_on_start" json:"run_on_start"` - RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"` - Timeout []int32 `db:"timeout" json:"timeout"` + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` + LogPath []string `db:"log_path" json:"log_path"` + CreatedAt []time.Time `db:"created_at" json:"created_at"` + Source []string `db:"source" json:"source"` + Cron []string `db:"cron" json:"cron"` + StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` + RunOnStart []bool `db:"run_on_start" json:"run_on_start"` + RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"` + Timeout []int32 `db:"timeout" json:"timeout"` } func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) { rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentScripts, arg.WorkspaceAgentID, pq.Array(arg.LogSourceID), - pq.Array(arg.LogSourceDisplayName), + pq.Array(arg.LogPath), pq.Array(arg.CreatedAt), pq.Array(arg.Source), pq.Array(arg.Cron), @@ -9124,7 +9157,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg Insert if err := rows.Scan( &i.WorkspaceAgentID, &i.LogSourceID, - &i.LogSourceDisplayName, + &i.LogPath, &i.CreatedAt, &i.Source, &i.Cron, diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index 55459ea3a7cc6..5afc597a4394d 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -171,6 +171,9 @@ INSERT INTO unnest(@icon :: text [ ]) AS icon RETURNING workspace_agent_log_sources.*; +-- name: GetWorkspaceAgentLogSourcesByAgentIDs :many +SELECT * FROM workspace_agent_log_sources WHERE workspace_agent_id = ANY(@ids :: uuid [ ]); + -- If an agent hasn't connected in the last 7 days, we purge it's logs. -- Logs can take up a lot of space, so it's important we clean up frequently. -- name: DeleteOldWorkspaceAgentLogs :exec diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql index 8488bba96b8b8..4b6503f4cca2a 100644 --- a/coderd/database/queries/workspacescripts.sql +++ b/coderd/database/queries/workspacescripts.sql @@ -1,10 +1,10 @@ -- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_source_display_name, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, unnest(@log_source_id :: uuid [ ]) AS log_source_id, - unnest(@log_source_display_name :: varchar(127) [ ]) AS log_source_display_name, + unnest(@log_path :: text [ ]) AS log_path, unnest(@created_at :: timestamptz [ ]) AS created_at, unnest(@source :: text [ ]) AS source, unnest(@cron :: text [ ]) AS cron, diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index f83efb028c89d..70c2dfae09a3a 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -137,6 +137,19 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, return } + // nolint:gocritic // GetWorkspaceAgentLogSourcesByAgentIDs is a system function. + logSources, err := api.Database.GetWorkspaceAgentLogSourcesByAgentIDs(dbauthz.AsSystemRestricted(ctx), resourceAgentIDs) + if errors.Is(err, sql.ErrNoRows) { + err = nil + } + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching workspace agent log sources.", + Detail: err.Error(), + }) + return + } + // nolint:gocritic // GetWorkspaceResourceMetadataByResourceIDs is a system function. resourceMetadata, err := api.Database.GetWorkspaceResourceMetadataByResourceIDs(dbauthz.AsSystemRestricted(ctx), resourceIDs) if err != nil { @@ -166,9 +179,15 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, dbScripts = append(dbScripts, script) } } + dbLogSources := make([]database.WorkspaceAgentLogSource, 0) + for _, logSource := range logSources { + if logSource.WorkspaceAgentID == agent.ID { + dbLogSources = append(dbLogSources, logSource) + } + } apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), convertScripts(dbScripts), api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(dbApps), convertScripts(dbScripts), convertLogSources(dbLogSources), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index ff037dcffe64d..18d191307cf4e 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -57,8 +57,9 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { workspaceAgent := httpmw.WorkspaceAgentParam(r) var ( - dbApps []database.WorkspaceApp - scripts []database.WorkspaceAgentScript + dbApps []database.WorkspaceApp + scripts []database.WorkspaceAgentScript + logSources []database.WorkspaceAgentLogSource ) var eg errgroup.Group @@ -70,8 +71,12 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) return }) + eg.Go(func() (err error) { + logSources, err = api.Database.GetWorkspaceAgentLogSourcesByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) + return + }) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), convertScripts(scripts), api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -96,7 +101,7 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -109,6 +114,7 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) var ( dbApps []database.WorkspaceApp + scripts []database.WorkspaceAgentScript metadata []database.WorkspaceAgentMetadatum resource database.WorkspaceResource build database.WorkspaceBuild @@ -124,6 +130,10 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) } return nil }) + eg.Go(func() (err error) { + scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) + return + }) eg.Go(func() (err error) { metadata, err = api.Database.GetWorkspaceAgentMetadata(ctx, workspaceAgent.ID) return @@ -170,6 +180,7 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) httpapi.Write(ctx, rw, http.StatusOK, agentsdk.Manifest{ AgentID: apiAgent.ID, Apps: convertApps(dbApps), + Scripts: convertScripts(scripts), DERPMap: api.DERPMap(), DERPForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), GitAuthConfigs: len(api.GitAuthConfigs), @@ -196,7 +207,7 @@ func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Reques ctx := r.Context() workspaceAgent := httpmw.WorkspaceAgent(r) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -642,7 +653,7 @@ func (api *API) workspaceAgentListeningPorts(rw http.ResponseWriter, r *http.Req workspaceAgent := httpmw.WorkspaceAgentParam(r) apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, nil, nil, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { @@ -1290,17 +1301,31 @@ func convertApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp { return apps } +func convertLogSources(dbLogSources []database.WorkspaceAgentLogSource) []codersdk.WorkspaceAgentLogSource { + logSources := make([]codersdk.WorkspaceAgentLogSource, 0) + for _, dbLogSource := range dbLogSources { + logSources = append(logSources, codersdk.WorkspaceAgentLogSource{ + ID: dbLogSource.ID, + DisplayName: dbLogSource.DisplayName, + WorkspaceAgentID: dbLogSource.WorkspaceAgentID, + CreatedAt: dbLogSource.CreatedAt, + Icon: dbLogSource.Icon, + }) + } + return logSources +} + func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.WorkspaceAgentScript { scripts := make([]codersdk.WorkspaceAgentScript, 0) for _, dbScript := range dbScripts { scripts = append(scripts, codersdk.WorkspaceAgentScript{ - LogSourceDisplayName: dbScript.LogSourceDisplayName, - LogSourceID: dbScript.LogSourceID, - Source: dbScript.Source, - CRON: dbScript.Cron, - RunOnStart: dbScript.RunOnStart, - RunOnStop: dbScript.RunOnStop, - StartBlocksLogin: dbScript.StartBlocksLogin, + LogPath: dbScript.LogPath, + LogSourceID: dbScript.LogSourceID, + Source: dbScript.Source, + CRON: dbScript.Cron, + RunOnStart: dbScript.RunOnStart, + RunOnStop: dbScript.RunOnStop, + StartBlocksLogin: dbScript.StartBlocksLogin, // In the database it's stored as seconds! Timeout: time.Duration(dbScript.Timeout) * time.Second, }) @@ -1322,7 +1347,9 @@ func convertWorkspaceAgentMetadataDesc(mds []database.WorkspaceAgentMetadatum) [ return metadata } -func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, scripts []codersdk.WorkspaceAgentScript, agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) { +func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, + dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, scripts []codersdk.WorkspaceAgentScript, logSources []codersdk.WorkspaceAgentLogSource, + agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) { var envs map[string]string if dbAgent.EnvironmentVariables.Valid { err := json.Unmarshal(dbAgent.EnvironmentVariables.RawMessage, &envs) diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 56144726bed68..0905fd821248b 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -77,6 +77,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) { data.agents, data.apps, data.scripts, + data.logSources, data.templateVersions[0], ) if err != nil { @@ -192,6 +193,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) { data.agents, data.apps, data.scripts, + data.logSources, data.templateVersions, ) if err != nil { @@ -281,6 +283,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ data.agents, data.apps, data.scripts, + data.logSources, data.templateVersions[0], ) if err != nil { @@ -402,6 +405,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) { []database.WorkspaceAgent{}, []database.WorkspaceApp{}, []database.WorkspaceAgentScript{}, + []database.WorkspaceAgentLogSource{}, database.TemplateVersion{}, ) if err != nil { @@ -636,6 +640,7 @@ type workspaceBuildsData struct { agents []database.WorkspaceAgent apps []database.WorkspaceApp scripts []database.WorkspaceAgentScript + logSources []database.WorkspaceAgentLogSource } func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.Workspace, workspaceBuilds []database.WorkspaceBuild) (workspaceBuildsData, error) { @@ -748,6 +753,7 @@ func (api *API) convertWorkspaceBuilds( resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, agentScripts []database.WorkspaceAgentScript, + agentLogSources []database.WorkspaceAgentLogSource, templateVersions []database.TemplateVersion, ) ([]codersdk.WorkspaceBuild, error) { workspaceByID := map[uuid.UUID]database.Workspace{} @@ -789,6 +795,7 @@ func (api *API) convertWorkspaceBuilds( resourceAgents, agentApps, agentScripts, + agentLogSources, templateVersion, ) if err != nil { @@ -811,6 +818,7 @@ func (api *API) convertWorkspaceBuild( resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, agentScripts []database.WorkspaceAgentScript, + agentLogSources []database.WorkspaceAgentLogSource, templateVersion database.TemplateVersion, ) (codersdk.WorkspaceBuild, error) { userByID := map[uuid.UUID]database.User{} @@ -837,6 +845,10 @@ func (api *API) convertWorkspaceBuild( for _, script := range agentScripts { scriptsByAgentID[script.WorkspaceAgentID] = append(scriptsByAgentID[script.WorkspaceAgentID], script) } + logSourcesByAgentID := map[uuid.UUID][]database.WorkspaceAgentLogSource{} + for _, logSource := range agentLogSources { + logSourcesByAgentID[logSource.WorkspaceAgentID] = append(logSourcesByAgentID[logSource.WorkspaceAgentID], logSource) + } owner, exists := userByID[workspace.OwnerID] if !exists { @@ -851,8 +863,9 @@ func (api *API) convertWorkspaceBuild( for _, agent := range agents { apps := appsByAgentID[agent.ID] scripts := scriptsByAgentID[agent.ID] + logSources := logSourcesByAgentID[agent.ID] apiAgent, err := convertWorkspaceAgent( - api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(apps), convertScripts(scripts), api.AgentInactiveDisconnectTimeout, + api.DERPMap(), *api.TailnetCoordinator.Load(), agent, convertApps(apps), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), ) if err != nil { diff --git a/coderd/workspaces.go b/coderd/workspaces.go index cd264c2ba4d37..647201745298b 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -535,6 +535,7 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req []database.WorkspaceAgent{}, []database.WorkspaceApp{}, []database.WorkspaceAgentScript{}, + []database.WorkspaceAgentLogSource{}, database.TemplateVersion{}, ) if err != nil { @@ -1120,6 +1121,7 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa data.agents, data.apps, data.scripts, + data.logSources, data.templateVersions, ) if err != nil { diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 51786f0b06180..9c6a7842567a3 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -186,14 +186,14 @@ type WorkspaceAgentLogSource struct { } type WorkspaceAgentScript struct { - LogSourceDisplayName string `json:"log_source_display_name"` - LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` - Source string `json:"source"` - CRON string `json:"cron"` - RunOnStart bool `json:"run_on_start"` - RunOnStop bool `json:"run_on_stop"` - StartBlocksLogin bool `json:"start_blocks_login"` - Timeout time.Duration `json:"timeout_seconds"` + LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` + LogPath string `json:"log_path"` + Source string `json:"source"` + CRON string `json:"cron"` + RunOnStart bool `json:"run_on_start"` + RunOnStop bool `json:"run_on_stop"` + StartBlocksLogin bool `json:"start_blocks_login"` + Timeout time.Duration `json:"timeout_seconds"` } type WorkspaceAgentHealth struct { diff --git a/docs/api/agents.md b/docs/api/agents.md index 7bd27794bb03f..6240b92fd5b6e 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -311,12 +311,12 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/logs \ ```json { + "log_source_id": "string", "logs": [ { "created_at": "string", "level": "trace", - "output": "string", - "source": "startup_script" + "output": "string" } ] } @@ -469,10 +469,18 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/manifest \ } ], "motd_file": "string", - "shutdown_script": "string", - "shutdown_script_timeout": 0, - "startup_script": "string", - "startup_script_timeout": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "vscode_port_proxy_uri": "string" } ``` @@ -575,12 +583,12 @@ curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/startup-logs \ ```json { + "log_source_id": "string", "logs": [ { "created_at": "string", "level": "trace", - "output": "string", - "source": "startup_script" + "output": "string" } ] } @@ -691,19 +699,34 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -919,6 +942,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/log "created_at": "2019-08-24T14:15:22Z", "id": 0, "level": "trace", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "output": "string" } ] @@ -934,13 +958,14 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/log Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------- | ------------------------------------------------ | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | false | | | -| `» id` | integer | false | | | -| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | -| `» output` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------------------------------------------------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | integer | false | | | +| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | +| `» log_source_id` | string(uuid) | false | | | +| `» output` | string | false | | | #### Enumerated Values @@ -1013,6 +1038,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta "created_at": "2019-08-24T14:15:22Z", "id": 0, "level": "trace", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "output": "string" } ] @@ -1028,13 +1054,14 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------- | ------------------------------------------------ | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | false | | | -| `» id` | integer | false | | | -| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | -| `» output` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ----------------- | ------------------------------------------------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | integer | false | | | +| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | +| `» log_source_id` | string(uuid) | false | | | +| `» output` | string | false | | | #### Enumerated Values diff --git a/docs/api/builds.md b/docs/api/builds.md index 864b1e8775b0a..2cfc532a02540 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -107,19 +107,34 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -270,19 +285,34 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -572,19 +602,34 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -622,107 +667,114 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» display_apps` | array | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» expanded_directory` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. | -| `»» logs_length` | integer | false | | | -| `»» logs_overflowed` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» ready_at` | string(date-time) | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» shutdown_script` | string | false | | | -| `»» shutdown_script_timeout_seconds` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» startup_script` | string | false | | | -| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | | -| `»» startup_script_timeout_seconds` | integer | false | | »startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. | -| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»» subsystems` | array | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» display_apps` | array | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» expanded_directory` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»» log_sources` | array | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» display_name` | string | false | | | +| `»»» icon` | string | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» workspace_agent_id` | string(uuid) | false | | | +| `»» logs_length` | integer | false | | | +| `»» logs_overflowed` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» ready_at` | string(date-time) | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» scripts` | array | false | | | +| `»»» cron` | string | false | | | +| `»»» log_path` | string | false | | | +| `»»» log_source_id` | string(uuid) | false | | | +| `»»» run_on_start` | boolean | false | | | +| `»»» run_on_stop` | boolean | false | | | +| `»»» source` | string | false | | | +| `»»» start_blocks_login` | boolean | false | | | +| `»»» timeout_seconds` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»» subsystems` | array | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | #### Enumerated Values -| Property | Value | -| ------------------------- | ------------------ | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `startup_script_behavior` | `blocking` | -| `startup_script_behavior` | `non-blocking` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ---------------------- | ------------------ | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -831,19 +883,34 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -999,19 +1066,34 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -1060,164 +1142,171 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------------- | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» build_number` | integer | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» deadline` | string(date-time) | false | | | -| `» id` | string(uuid) | false | | | -| `» initiator_id` | string(uuid) | false | | | -| `» initiator_name` | string | false | | | -| `» job` | [codersdk.ProvisionerJob](schemas.md#codersdkprovisionerjob) | false | | | -| `»» canceled_at` | string(date-time) | false | | | -| `»» completed_at` | string(date-time) | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» error` | string | false | | | -| `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | -| `»» file_id` | string(uuid) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» queue_position` | integer | false | | | -| `»» queue_size` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | -| `»» tags` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» worker_id` | string(uuid) | false | | | -| `» max_deadline` | string(date-time) | false | | | -| `» reason` | [codersdk.BuildReason](schemas.md#codersdkbuildreason) | false | | | -| `» resources` | array | false | | | -| `»» agents` | array | false | | | -| `»»» apps` | array | false | | | -| `»»»» command` | string | false | | | -| `»»»» display_name` | string | false | | »»»display name is a friendly name for the app. | -| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»»» url` | string | false | | »»»»url specifies the endpoint to check for the app health. | -| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»»» id` | string(uuid) | false | | | -| `»»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»»» url` | string | false | | »»»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»»» architecture` | string | false | | | -| `»»» connection_timeout_seconds` | integer | false | | | -| `»»» created_at` | string(date-time) | false | | | -| `»»» directory` | string | false | | | -| `»»» disconnected_at` | string(date-time) | false | | | -| `»»» display_apps` | array | false | | | -| `»»» environment_variables` | object | false | | | -| `»»»» [any property]` | string | false | | | -| `»»» expanded_directory` | string | false | | | -| `»»» first_connected_at` | string(date-time) | false | | | -| `»»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»»» id` | string(uuid) | false | | | -| `»»» instance_id` | string | false | | | -| `»»» last_connected_at` | string(date-time) | false | | | -| `»»» latency` | object | false | | »»latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»»» latency_ms` | number | false | | | -| `»»»»» preferred` | boolean | false | | | -| `»»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. | -| `»»» logs_length` | integer | false | | | -| `»»» logs_overflowed` | boolean | false | | | -| `»»» name` | string | false | | | -| `»»» operating_system` | string | false | | | -| `»»» ready_at` | string(date-time) | false | | | -| `»»» resource_id` | string(uuid) | false | | | -| `»»» shutdown_script` | string | false | | | -| `»»» shutdown_script_timeout_seconds` | integer | false | | | -| `»»» started_at` | string(date-time) | false | | | -| `»»» startup_script` | string | false | | | -| `»»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | | -| `»»» startup_script_timeout_seconds` | integer | false | | »»startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. | -| `»»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»»» subsystems` | array | false | | | -| `»»» troubleshooting_url` | string | false | | | -| `»»» updated_at` | string(date-time) | false | | | -| `»»» version` | string | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» daily_cost` | integer | false | | | -| `»» hide` | boolean | false | | | -| `»» icon` | string | false | | | -| `»» id` | string(uuid) | false | | | -| `»» job_id` | string(uuid) | false | | | -| `»» metadata` | array | false | | | -| `»»» key` | string | false | | | -| `»»» sensitive` | boolean | false | | | -| `»»» value` | string | false | | | -| `»» name` | string | false | | | -| `»» type` | string | false | | | -| `»» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | -| `» status` | [codersdk.WorkspaceStatus](schemas.md#codersdkworkspacestatus) | false | | | -| `» template_version_id` | string(uuid) | false | | | -| `» template_version_name` | string | false | | | -| `» transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | -| `» updated_at` | string(date-time) | false | | | -| `» workspace_id` | string(uuid) | false | | | -| `» workspace_name` | string | false | | | -| `» workspace_owner_id` | string(uuid) | false | | | -| `» workspace_owner_name` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» build_number` | integer | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» deadline` | string(date-time) | false | | | +| `» id` | string(uuid) | false | | | +| `» initiator_id` | string(uuid) | false | | | +| `» initiator_name` | string | false | | | +| `» job` | [codersdk.ProvisionerJob](schemas.md#codersdkprovisionerjob) | false | | | +| `»» canceled_at` | string(date-time) | false | | | +| `»» completed_at` | string(date-time) | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» error` | string | false | | | +| `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | +| `»» file_id` | string(uuid) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» queue_position` | integer | false | | | +| `»» queue_size` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» worker_id` | string(uuid) | false | | | +| `» max_deadline` | string(date-time) | false | | | +| `» reason` | [codersdk.BuildReason](schemas.md#codersdkbuildreason) | false | | | +| `» resources` | array | false | | | +| `»» agents` | array | false | | | +| `»»» apps` | array | false | | | +| `»»»» command` | string | false | | | +| `»»»» display_name` | string | false | | »»»display name is a friendly name for the app. | +| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»»» url` | string | false | | »»»»url specifies the endpoint to check for the app health. | +| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»»» id` | string(uuid) | false | | | +| `»»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»»» url` | string | false | | »»»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»»» architecture` | string | false | | | +| `»»» connection_timeout_seconds` | integer | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» directory` | string | false | | | +| `»»» disconnected_at` | string(date-time) | false | | | +| `»»» display_apps` | array | false | | | +| `»»» environment_variables` | object | false | | | +| `»»»» [any property]` | string | false | | | +| `»»» expanded_directory` | string | false | | | +| `»»» first_connected_at` | string(date-time) | false | | | +| `»»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»»» id` | string(uuid) | false | | | +| `»»» instance_id` | string | false | | | +| `»»» last_connected_at` | string(date-time) | false | | | +| `»»» latency` | object | false | | »»latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»»» latency_ms` | number | false | | | +| `»»»»» preferred` | boolean | false | | | +| `»»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»»» log_sources` | array | false | | | +| `»»»» created_at` | string(date-time) | false | | | +| `»»»» display_name` | string | false | | | +| `»»»» icon` | string | false | | | +| `»»»» id` | string(uuid) | false | | | +| `»»»» workspace_agent_id` | string(uuid) | false | | | +| `»»» logs_length` | integer | false | | | +| `»»» logs_overflowed` | boolean | false | | | +| `»»» name` | string | false | | | +| `»»» operating_system` | string | false | | | +| `»»» ready_at` | string(date-time) | false | | | +| `»»» resource_id` | string(uuid) | false | | | +| `»»» scripts` | array | false | | | +| `»»»» cron` | string | false | | | +| `»»»» log_path` | string | false | | | +| `»»»» log_source_id` | string(uuid) | false | | | +| `»»»» run_on_start` | boolean | false | | | +| `»»»» run_on_stop` | boolean | false | | | +| `»»»» source` | string | false | | | +| `»»»» start_blocks_login` | boolean | false | | | +| `»»»» timeout_seconds` | integer | false | | | +| `»»» started_at` | string(date-time) | false | | | +| `»»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»»» subsystems` | array | false | | | +| `»»» troubleshooting_url` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | +| `»»» version` | string | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» daily_cost` | integer | false | | | +| `»» hide` | boolean | false | | | +| `»» icon` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» job_id` | string(uuid) | false | | | +| `»» metadata` | array | false | | | +| `»»» key` | string | false | | | +| `»»» sensitive` | boolean | false | | | +| `»»» value` | string | false | | | +| `»» name` | string | false | | | +| `»» type` | string | false | | | +| `»» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| `» status` | [codersdk.WorkspaceStatus](schemas.md#codersdkworkspacestatus) | false | | | +| `» template_version_id` | string(uuid) | false | | | +| `» template_version_name` | string | false | | | +| `» transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| `» updated_at` | string(date-time) | false | | | +| `» workspace_id` | string(uuid) | false | | | +| `» workspace_name` | string | false | | | +| `» workspace_owner_id` | string(uuid) | false | | | +| `» workspace_owner_name` | string | false | | | #### Enumerated Values -| Property | Value | -| ------------------------- | ----------------------------- | -| `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | -| `status` | `pending` | -| `status` | `running` | -| `status` | `succeeded` | -| `status` | `canceling` | -| `status` | `canceled` | -| `status` | `failed` | -| `reason` | `initiator` | -| `reason` | `autostart` | -| `reason` | `autostop` | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `startup_script_behavior` | `blocking` | -| `startup_script_behavior` | `non-blocking` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | -| `status` | `pending` | -| `status` | `starting` | -| `status` | `running` | -| `status` | `stopping` | -| `status` | `stopped` | -| `status` | `failed` | -| `status` | `canceling` | -| `status` | `canceled` | -| `status` | `deleting` | -| `status` | `deleted` | -| `transition` | `start` | -| `transition` | `stop` | -| `transition` | `delete` | +| Property | Value | +| ---------------------- | ----------------------------- | +| `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | +| `status` | `pending` | +| `status` | `running` | +| `status` | `succeeded` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `failed` | +| `reason` | `initiator` | +| `reason` | `autostart` | +| `reason` | `autostop` | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | +| `status` | `pending` | +| `status` | `starting` | +| `status` | `running` | +| `status` | `stopping` | +| `status` | `stopped` | +| `status` | `failed` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `deleting` | +| `status` | `deleted` | +| `transition` | `start` | +| `transition` | `stop` | +| `transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -1347,19 +1436,34 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 31d23faad36f6..b904fa4fd62c9 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -163,19 +163,17 @@ { "created_at": "string", "level": "trace", - "output": "string", - "source": "startup_script" + "output": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------ | -------------------------------------------------------------------- | -------- | ------------ | ----------- | -| `created_at` | string | false | | | -| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | | -| `output` | string | false | | | -| `source` | [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------ | -------------------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | | +| `output` | string | false | | | ## agentsdk.Manifest @@ -278,10 +276,18 @@ } ], "motd_file": "string", - "shutdown_script": "string", - "shutdown_script_timeout": 0, - "startup_script": "string", - "startup_script_timeout": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "vscode_port_proxy_uri": "string" } ``` @@ -301,22 +307,19 @@ | `git_auth_configs` | integer | false | | Git auth configs stores the number of Git configurations the Coder deployment has. If this number is >0, we set up special configuration in the workspace. | | `metadata` | array of [codersdk.WorkspaceAgentMetadataDescription](#codersdkworkspaceagentmetadatadescription) | false | | | | `motd_file` | string | false | | | -| `shutdown_script` | string | false | | | -| `shutdown_script_timeout` | integer | false | | | -| `startup_script` | string | false | | | -| `startup_script_timeout` | integer | false | | | +| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | | | `vscode_port_proxy_uri` | string | false | | | ## agentsdk.PatchLogs ```json { + "log_source_id": "string", "logs": [ { "created_at": "string", "level": "trace", - "output": "string", - "source": "startup_script" + "output": "string" } ] } @@ -324,9 +327,10 @@ ### Properties -| Name | Type | Required | Restrictions | Description | -| ------ | ------------------------------------- | -------- | ------------ | ----------- | -| `logs` | array of [agentsdk.Log](#agentsdklog) | false | | | +| Name | Type | Required | Restrictions | Description | +| --------------- | ------------------------------------- | -------- | ------------ | ----------- | +| `log_source_id` | string | true | | | +| `logs` | array of [agentsdk.Log](#agentsdklog) | false | | | ## agentsdk.PostAppHealthsRequest @@ -5468,19 +5472,34 @@ If the schedule is empty, the user will be updated to use the default schedule.| } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -5611,19 +5630,34 @@ If the schedule is empty, the user will be updated to use the default schedule.| } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -5634,44 +5668,40 @@ If the schedule is empty, the user will be updated to use the default schedule.| ### Properties -| Name | Type | Required | Restrictions | Description | -| --------------------------------- | -------------------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | | -| `architecture` | string | false | | | -| `connection_timeout_seconds` | integer | false | | | -| `created_at` | string | false | | | -| `directory` | string | false | | | -| `disconnected_at` | string | false | | | -| `display_apps` | array of [codersdk.DisplayApp](#codersdkdisplayapp) | false | | | -| `environment_variables` | object | false | | | -| » `[any property]` | string | false | | | -| `expanded_directory` | string | false | | | -| `first_connected_at` | string | false | | | -| `health` | [codersdk.WorkspaceAgentHealth](#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `id` | string | false | | | -| `instance_id` | string | false | | | -| `last_connected_at` | string | false | | | -| `latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | -| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | | -| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | | -| `login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. | -| `logs_length` | integer | false | | | -| `logs_overflowed` | boolean | false | | | -| `name` | string | false | | | -| `operating_system` | string | false | | | -| `ready_at` | string | false | | | -| `resource_id` | string | false | | | -| `shutdown_script` | string | false | | | -| `shutdown_script_timeout_seconds` | integer | false | | | -| `started_at` | string | false | | | -| `startup_script` | string | false | | | -| `startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](#codersdkworkspaceagentstartupscriptbehavior) | false | | | -| `startup_script_timeout_seconds` | integer | false | | Startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. | -| `status` | [codersdk.WorkspaceAgentStatus](#codersdkworkspaceagentstatus) | false | | | -| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | | -| `troubleshooting_url` | string | false | | | -| `updated_at` | string | false | | | -| `version` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ---------------------------- | ----------------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------- | +| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | | +| `architecture` | string | false | | | +| `connection_timeout_seconds` | integer | false | | | +| `created_at` | string | false | | | +| `directory` | string | false | | | +| `disconnected_at` | string | false | | | +| `display_apps` | array of [codersdk.DisplayApp](#codersdkdisplayapp) | false | | | +| `environment_variables` | object | false | | | +| » `[any property]` | string | false | | | +| `expanded_directory` | string | false | | | +| `first_connected_at` | string | false | | | +| `health` | [codersdk.WorkspaceAgentHealth](#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `id` | string | false | | | +| `instance_id` | string | false | | | +| `last_connected_at` | string | false | | | +| `latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | +| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | | +| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | | +| `log_sources` | array of [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | | +| `logs_length` | integer | false | | | +| `logs_overflowed` | boolean | false | | | +| `name` | string | false | | | +| `operating_system` | string | false | | | +| `ready_at` | string | false | | | +| `resource_id` | string | false | | | +| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | | +| `started_at` | string | false | | | +| `status` | [codersdk.WorkspaceAgentStatus](#codersdkworkspaceagentstatus) | false | | | +| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | | +| `troubleshooting_url` | string | false | | | +| `updated_at` | string | false | | | +| `version` | string | false | | | ## codersdk.WorkspaceAgentConnectionInfo @@ -5832,37 +5862,42 @@ If the schedule is empty, the user will be updated to use the default schedule.| "created_at": "2019-08-24T14:15:22Z", "id": 0, "level": "trace", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "output": "string" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| ------------ | -------------------------------------- | -------- | ------------ | ----------- | -| `created_at` | string | false | | | -| `id` | integer | false | | | -| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | | -| `output` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| --------------- | -------------------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `id` | integer | false | | | +| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | | +| `log_source_id` | string | false | | | +| `output` | string | false | | | ## codersdk.WorkspaceAgentLogSource ```json -"startup_script" +{ + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" +} ``` ### Properties -#### Enumerated Values - -| Value | -| ----------------- | -| `startup_script` | -| `shutdown_script` | -| `kubernetes` | -| `envbox` | -| `envbuilder` | -| `external` | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------ | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `display_name` | string | false | | | +| `icon` | string | false | | | +| `id` | string | false | | | +| `workspace_agent_id` | string | false | | | ## codersdk.WorkspaceAgentMetadataDescription @@ -5886,20 +5921,33 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `script` | string | false | | | | `timeout` | integer | false | | | -## codersdk.WorkspaceAgentStartupScriptBehavior +## codersdk.WorkspaceAgentScript ```json -"blocking" +{ + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 +} ``` ### Properties -#### Enumerated Values - -| Value | -| -------------- | -| `blocking` | -| `non-blocking` | +| Name | Type | Required | Restrictions | Description | +| -------------------- | ------- | -------- | ------------ | ----------- | +| `cron` | string | false | | | +| `log_path` | string | false | | | +| `log_source_id` | string | false | | | +| `run_on_start` | boolean | false | | | +| `run_on_stop` | boolean | false | | | +| `source` | string | false | | | +| `start_blocks_login` | boolean | false | | | +| `timeout_seconds` | integer | false | | | ## codersdk.WorkspaceAgentStatus @@ -6081,19 +6129,34 @@ If the schedule is empty, the user will be updated to use the default schedule.| } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -6393,19 +6456,34 @@ If the schedule is empty, the user will be updated to use the default schedule.| } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -6607,19 +6685,34 @@ If the schedule is empty, the user will be updated to use the default schedule.| } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/docs/api/templates.md b/docs/api/templates.md index 723cc9039d9eb..b5817ae59092f 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1618,19 +1618,34 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -1668,107 +1683,114 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» display_apps` | array | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» expanded_directory` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. | -| `»» logs_length` | integer | false | | | -| `»» logs_overflowed` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» ready_at` | string(date-time) | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» shutdown_script` | string | false | | | -| `»» shutdown_script_timeout_seconds` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» startup_script` | string | false | | | -| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | | -| `»» startup_script_timeout_seconds` | integer | false | | »startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. | -| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»» subsystems` | array | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» display_apps` | array | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» expanded_directory` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»» log_sources` | array | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» display_name` | string | false | | | +| `»»» icon` | string | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» workspace_agent_id` | string(uuid) | false | | | +| `»» logs_length` | integer | false | | | +| `»» logs_overflowed` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» ready_at` | string(date-time) | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» scripts` | array | false | | | +| `»»» cron` | string | false | | | +| `»»» log_path` | string | false | | | +| `»»» log_source_id` | string(uuid) | false | | | +| `»»» run_on_start` | boolean | false | | | +| `»»» run_on_stop` | boolean | false | | | +| `»»» source` | string | false | | | +| `»»» start_blocks_login` | boolean | false | | | +| `»»» timeout_seconds` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»» subsystems` | array | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | #### Enumerated Values -| Property | Value | -| ------------------------- | ------------------ | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `startup_script_behavior` | `blocking` | -| `startup_script_behavior` | `non-blocking` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ---------------------- | ------------------ | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -2011,19 +2033,34 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -2061,107 +2098,114 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» display_apps` | array | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» expanded_directory` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»» login_before_ready` | boolean | false | | Deprecated: Use StartupScriptBehavior instead. | -| `»» logs_length` | integer | false | | | -| `»» logs_overflowed` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» ready_at` | string(date-time) | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» shutdown_script` | string | false | | | -| `»» shutdown_script_timeout_seconds` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» startup_script` | string | false | | | -| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | | -| `»» startup_script_timeout_seconds` | integer | false | | »startup script timeout seconds is the number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout. | -| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»» subsystems` | array | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | »»display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | »»»url specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» url` | string | false | | »»url is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» display_apps` | array | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» expanded_directory` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | »latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»» log_sources` | array | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» display_name` | string | false | | | +| `»»» icon` | string | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» workspace_agent_id` | string(uuid) | false | | | +| `»» logs_length` | integer | false | | | +| `»» logs_overflowed` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» ready_at` | string(date-time) | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» scripts` | array | false | | | +| `»»» cron` | string | false | | | +| `»»» log_path` | string | false | | | +| `»»» log_source_id` | string(uuid) | false | | | +| `»»» run_on_start` | boolean | false | | | +| `»»» run_on_stop` | boolean | false | | | +| `»»» source` | string | false | | | +| `»»» start_blocks_login` | boolean | false | | | +| `»»» timeout_seconds` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»» subsystems` | array | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | #### Enumerated Values -| Property | Value | -| ------------------------- | ------------------ | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `startup_script_behavior` | `blocking` | -| `startup_script_behavior` | `non-blocking` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ---------------------- | ------------------ | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index fd9bb1c817a83..f3502d807d4be 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -137,19 +137,34 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -327,19 +342,34 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -516,19 +546,34 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -707,19 +752,34 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -977,19 +1037,34 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ } }, "lifecycle_state": "created", - "login_before_ready": true, + "log_sources": [ + { + "created_at": "2019-08-24T14:15:22Z", + "display_name": "string", + "icon": "string", + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "workspace_agent_id": "7ad2e618-fea7-4c1a-b70a-f501566a72f1" + } + ], "logs_length": 0, "logs_overflowed": true, "name": "string", "operating_system": "string", "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", - "shutdown_script": "string", - "shutdown_script_timeout_seconds": 0, + "scripts": [ + { + "cron": "string", + "log_path": "string", + "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", + "run_on_start": true, + "run_on_stop": true, + "source": "string", + "start_blocks_login": true, + "timeout_seconds": 0 + } + ], "started_at": "2019-08-24T14:15:22Z", - "startup_script": "string", - "startup_script_behavior": "blocking", - "startup_script_timeout_seconds": 0, "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 1a16ac2e3492a..cf3eef1df6c78 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -234,6 +234,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error // Support the legacy script attributes in the agent! if attrs.StartupScript != "" { agent.Scripts = append(agent.Scripts, &proto.Script{ + LogPath: "coder-startup-script.log", DisplayName: "Startup Script", Source: attrs.StartupScript, StartBlocksLogin: startupScriptBehavior == string(codersdk.WorkspaceAgentStartupScriptBehaviorBlocking), @@ -243,6 +244,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error } if attrs.ShutdownScript != "" { agent.Scripts = append(agent.Scripts, &proto.Script{ + LogPath: "coder-shutdown-script.log", DisplayName: "Shutdown Script", Source: attrs.ShutdownScript, Timeout: attrs.ShutdownScriptTimeoutSeconds, diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 9e7c4053742fd..84c6d37dae05a 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -1104,6 +1104,7 @@ type Script struct { RunOnStart bool `protobuf:"varint,6,opt,name=run_on_start,json=runOnStart,proto3" json:"run_on_start,omitempty"` RunOnStop bool `protobuf:"varint,7,opt,name=run_on_stop,json=runOnStop,proto3" json:"run_on_stop,omitempty"` Timeout int32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"` + LogPath string `protobuf:"bytes,9,opt,name=log_path,json=logPath,proto3" json:"log_path,omitempty"` } func (x *Script) Reset() { @@ -1194,6 +1195,13 @@ func (x *Script) GetTimeout() int32 { return 0 } +func (x *Script) GetLogPath() string { + if x != nil { + return x.LogPath + } + return "" +} + // App represents a dev-accessible application on the workspace. type App struct { state protoimpl.MessageState @@ -2620,7 +2628,7 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x72, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0xf5, 0x01, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x69, + 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0x90, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x02, 0x20, @@ -2635,210 +2643,211 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4f, 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, - 0xb5, 0x02, 0x0a, 0x03, 0x41, 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, - 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, - 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, - 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x09, 0x73, 0x75, 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, - 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, - 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, - 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, - 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, - 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, - 0x6c, 0x64, 0x22, 0xf1, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, - 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, - 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, - 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, - 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, 0x22, 0xae, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, - 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x03, 0x41, + 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, + 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, + 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, + 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, + 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, + 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, + 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, + 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, + 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xf1, 0x02, + 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, + 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, + 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, + 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, + 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, + 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, + 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, + 0x6c, 0x22, 0xae, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, + 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, + 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, - 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, - 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, - 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, - 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, - 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, - 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x22, 0x8b, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, - 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, - 0x61, 0x64, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, - 0x6d, 0x65, 0x22, 0xa6, 0x02, 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, - 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, - 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, - 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, - 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, - 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0c, - 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x22, 0x41, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x22, 0xda, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, - 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, - 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, - 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, - 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, - 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, - 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, - 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, - 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, - 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, - 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, - 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, - 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, - 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, - 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, - 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, + 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, + 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, + 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, + 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, + 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, + 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, + 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, + 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, + 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, + 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, + 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, + 0x8b, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, + 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, + 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, 0xa6, 0x02, + 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, + 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, + 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x12, 0x67, 0x69, + 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, + 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x6e, 0x43, + 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, + 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, + 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x0c, + 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, + 0xda, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, + 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, + 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, + 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, + 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x0f, 0x0a, 0x0d, + 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x02, + 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, + 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, 0x05, 0x61, + 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, + 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x34, + 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, + 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd1, 0x01, 0x0a, + 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, + 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, + 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, + 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, + 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, - 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, - 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, - 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, - 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, - 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, - 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, - 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, - 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, - 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, - 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, - 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, - 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, - 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0x49, 0x0a, 0x0b, - 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, - 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, + 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, + 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, + 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, + 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, + 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, + 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, + 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, + 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, + 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, + 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, + 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index d822563e420f7..751bfc8836bd7 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -145,6 +145,7 @@ message Script { bool run_on_start = 6; bool run_on_stop = 7; int32 timeout = 8; + string log_path = 9; } // App represents a dev-accessible application on the workspace. diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index cdfc915e2452e..77464c5d128fc 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1412,8 +1412,8 @@ export interface WorkspaceAgentMetadataResult { // From codersdk/workspaceagents.go export interface WorkspaceAgentScript { - readonly log_source_display_name: string readonly log_source_id: string + readonly log_path: string readonly source: string readonly cron: string readonly run_on_start: boolean From 00a4e733cbf91817d8094f0d7517dc64401d846a Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 19:04:28 +0000 Subject: [PATCH 07/59] Fix context usage --- coderd/database/dbfake/dbfake.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 8e2a276ccbcec..81a02e34b0bb0 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -3071,7 +3071,7 @@ func (q *FakeQuerier) GetWorkspaceAgentLifecycleStateByID(ctx context.Context, i }, nil } -func (q *FakeQuerier) GetWorkspaceAgentLogSourcesByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) { +func (q *FakeQuerier) GetWorkspaceAgentLogSourcesByAgentIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentLogSource, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -3121,7 +3121,7 @@ func (q *FakeQuerier) GetWorkspaceAgentMetadata(_ context.Context, workspaceAgen return metadata, nil } -func (q *FakeQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { +func (q *FakeQuerier) GetWorkspaceAgentScriptsByAgentIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { q.mutex.RLock() defer q.mutex.RUnlock() @@ -4467,7 +4467,7 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser return agent, nil } -func (q *FakeQuerier) InsertWorkspaceAgentLogSources(ctx context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { +func (q *FakeQuerier) InsertWorkspaceAgentLogSources(_ context.Context, arg database.InsertWorkspaceAgentLogSourcesParams) ([]database.WorkspaceAgentLogSource, error) { err := validateDatabaseType(arg) if err != nil { return nil, err @@ -4554,7 +4554,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentMetadata(_ context.Context, arg databa return nil } -func (q *FakeQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { +func (q *FakeQuerier) InsertWorkspaceAgentScripts(_ context.Context, arg database.InsertWorkspaceAgentScriptsParams) ([]database.WorkspaceAgentScript, error) { err := validateDatabaseType(arg) if err != nil { return nil, err From 942fde614db5212e21c7963ef98bce6dcb892dfd Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 19:29:45 +0000 Subject: [PATCH 08/59] It works! --- .../provisionerdserver/provisionerdserver.go | 53 +++++++++++++++++++ .../provisionerdserver_test.go | 4 ++ coderd/workspaceagents.go | 9 ++-- coderd/workspacebuilds.go | 35 ++++++++---- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 74239709355d7..54adbafc67112 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1281,6 +1281,59 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. } } + logSourceIDs := make([]uuid.UUID, 0, len(prAgent.Scripts)) + logSourceCreatedAt := make([]time.Time, 0, len(prAgent.Scripts)) + logSourceDisplayNames := make([]string, 0, len(prAgent.Scripts)) + logSourceIcons := make([]string, 0, len(prAgent.Scripts)) + scriptLogPaths := make([]string, 0, len(prAgent.Scripts)) + scriptSources := make([]string, 0, len(prAgent.Scripts)) + scriptCron := make([]string, 0, len(prAgent.Scripts)) + scriptTimeout := make([]int32, 0, len(prAgent.Scripts)) + scriptStartBlocksLogin := make([]bool, 0, len(prAgent.Scripts)) + scriptRunOnStart := make([]bool, 0, len(prAgent.Scripts)) + scriptRunOnStop := make([]bool, 0, len(prAgent.Scripts)) + + for _, script := range prAgent.Scripts { + logSourceIDs = append(logSourceIDs, uuid.New()) + logSourceCreatedAt = append(logSourceCreatedAt, dbtime.Now()) + logSourceDisplayNames = append(logSourceDisplayNames, script.DisplayName) + logSourceIcons = append(logSourceIcons, script.Icon) + scriptLogPaths = append(scriptLogPaths, script.LogPath) + scriptSources = append(scriptSources, script.Source) + scriptCron = append(scriptCron, script.Cron) + scriptTimeout = append(scriptTimeout, script.Timeout) + scriptStartBlocksLogin = append(scriptStartBlocksLogin, script.StartBlocksLogin) + scriptRunOnStart = append(scriptRunOnStart, script.RunOnStart) + scriptRunOnStop = append(scriptRunOnStop, script.RunOnStop) + } + + _, err = db.InsertWorkspaceAgentLogSources(ctx, database.InsertWorkspaceAgentLogSourcesParams{ + WorkspaceAgentID: agentID, + ID: logSourceIDs, + CreatedAt: logSourceCreatedAt, + DisplayName: logSourceDisplayNames, + Icon: logSourceIcons, + }) + if err != nil { + return xerrors.Errorf("insert agent log sources: %w", err) + } + + _, err = db.InsertWorkspaceAgentScripts(ctx, database.InsertWorkspaceAgentScriptsParams{ + WorkspaceAgentID: agentID, + LogSourceID: logSourceIDs, + LogPath: scriptLogPaths, + CreatedAt: logSourceCreatedAt, + Source: scriptSources, + Cron: scriptCron, + Timeout: scriptTimeout, + StartBlocksLogin: scriptStartBlocksLogin, + RunOnStart: scriptRunOnStart, + RunOnStop: scriptRunOnStop, + }) + if err != nil { + return xerrors.Errorf("insert agent scripts: %w", err) + } + for _, app := range prAgent.Apps { slug := app.Slug if slug == "" { diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index f45d9815b77e8..709a021ac1817 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -1565,6 +1565,10 @@ func TestInsertWorkspaceResource(t *testing.T) { Apps: []*sdkproto.App{{ Slug: "a", }}, + Scripts: []*sdkproto.Script{{ + DisplayName: "Startup", + Icon: "/test.png", + }}, DisplayApps: &sdkproto.DisplayApps{ Vscode: true, PortForwardingHelper: true, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 18d191307cf4e..6a4eb21b3571c 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -131,12 +131,13 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) return nil }) eg.Go(func() (err error) { - scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) - return + // nolint:gocritic // This is necessary to fetch agent scripts! + scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(dbauthz.AsSystemRestricted(ctx), []uuid.UUID{workspaceAgent.ID}) + return err }) eg.Go(func() (err error) { metadata, err = api.Database.GetWorkspaceAgentMetadata(ctx, workspaceAgent.ID) - return + return err }) eg.Go(func() (err error) { resource, err = api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID) @@ -155,7 +156,7 @@ func (api *API) workspaceAgentManifest(rw http.ResponseWriter, r *http.Request) if err != nil { return xerrors.Errorf("getting workspace owner by id: %w", err) } - return + return err }) err = eg.Wait() if err != nil { diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 0905fd821248b..52ebe4353229e 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -12,6 +12,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/uuid" "golang.org/x/exp/slices" + "golang.org/x/sync/errgroup" "golang.org/x/xerrors" "cdr.dev/slog" @@ -719,16 +720,31 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W agentIDs = append(agentIDs, agent.ID) } - // nolint:gocritic // Getting workspace apps by agent IDs is a system function. - apps, err := api.Database.GetWorkspaceAppsByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - return workspaceBuildsData{}, xerrors.Errorf("fetching workspace apps: %w", err) - } + var ( + apps []database.WorkspaceApp + scripts []database.WorkspaceAgentScript + logSources []database.WorkspaceAgentLogSource + ) - // nolint:gocritic // Getting workspace scripts by agent IDs is a system function. - scripts, err := api.Database.GetWorkspaceAgentScriptsByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs) - if err != nil && !errors.Is(err, sql.ErrNoRows) { - return workspaceBuildsData{}, xerrors.Errorf("fetching workspace agent scripts: %w", err) + var eg errgroup.Group + eg.Go(func() (err error) { + // nolint:gocritic // Getting workspace apps by agent IDs is a system function. + apps, err = api.Database.GetWorkspaceAppsByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs) + return err + }) + eg.Go(func() (err error) { + // nolint:gocritic // Getting workspace scripts by agent IDs is a system function. + scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs) + return err + }) + eg.Go(func() error { + // nolint:gocritic // Getting workspace agent log sources by agent IDs is a system function. + logSources, err = api.Database.GetWorkspaceAgentLogSourcesByAgentIDs(dbauthz.AsSystemRestricted(ctx), agentIDs) + return err + }) + err = eg.Wait() + if err != nil { + return workspaceBuildsData{}, err } return workspaceBuildsData{ @@ -740,6 +756,7 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaces []database.W agents: agents, apps: apps, scripts: scripts, + logSources: logSources, }, nil } From 92dedadc6893118265a808f07471f7cfc1823bde Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 19:34:25 +0000 Subject: [PATCH 09/59] Fix sql query --- coderd/database/queries.sql.go | 2 +- coderd/database/queries/workspacescripts.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index d6a61b1acf25a..ae4929173077a 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -9106,7 +9106,7 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) SELECT $1 :: uuid AS workspace_agent_id, unnest($2 :: uuid [ ]) AS log_source_id, diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql index 4b6503f4cca2a..3f687c810e000 100644 --- a/coderd/database/queries/workspacescripts.sql +++ b/coderd/database/queries/workspacescripts.sql @@ -1,6 +1,6 @@ -- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, unnest(@log_source_id :: uuid [ ]) AS log_source_id, From 7cf6f0cd8936537a69730c967c9f3a3e602c6909 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 20:07:12 +0000 Subject: [PATCH 10/59] Fix SQL query --- coderd/database/queries.sql.go | 2 +- coderd/database/queries/workspaceagents.sql | 2 +- coderd/workspaceagents.go | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index ae4929173077a..3df6d717712d2 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -7061,7 +7061,7 @@ WITH new_length AS ( logs_length = logs_length + $6 WHERE workspace_agents.id = $1 ) INSERT INTO - workspace_agent_logs (agent_id, created_at, output, level, source) + workspace_agent_logs (agent_id, created_at, output, level, log_source_id) SELECT $1 :: uuid AS agent_id, unnest($2 :: timestamptz [ ]) AS created_at, diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index 5afc597a4394d..0680a755fa2b9 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -151,7 +151,7 @@ WITH new_length AS ( logs_length = logs_length + @output_length WHERE workspace_agents.id = @agent_id ) INSERT INTO - workspace_agent_logs (agent_id, created_at, output, level, source) + workspace_agent_logs (agent_id, created_at, output, level, log_source_id) SELECT @agent_id :: uuid AS agent_id, unnest(@created_at :: timestamptz [ ]) AS created_at, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 6a4eb21b3571c..5593e272a14e2 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1379,6 +1379,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin Scripts: scripts, LogsLength: dbAgent.LogsLength, LogsOverflowed: dbAgent.LogsOverflowed, + LogSources: logSources, Version: dbAgent.Version, EnvironmentVariables: envs, Directory: dbAgent.Directory, From 5b6f264ebe3f048fdc1759e5af018b081b017916 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 20:28:10 +0000 Subject: [PATCH 11/59] Rename `LogSourceID` -> `SourceID` --- coderd/apidoc/docs.go | 8 +-- coderd/apidoc/swagger.json | 8 +-- coderd/workspaceagents.go | 1 + codersdk/workspaceagents.go | 10 +-- docs/api/agents.md | 40 ++++++------ docs/api/schemas.md | 18 +++--- site/src/api/typesGenerated.ts | 2 +- site/src/components/Logs/Logs.stories.tsx | 1 + site/src/components/Logs/Logs.tsx | 1 + site/src/components/Resources/AgentStatus.tsx | 61 +++++++++---------- .../WorkspaceBuildLogs/WorkspaceBuildLogs.tsx | 1 + site/src/testHelpers/entities.ts | 29 +++++++-- .../workspaceAgentLogsXService.ts | 2 + 13 files changed, 102 insertions(+), 80 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index e8877ab06405d..fa7e8b0246347 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -10830,12 +10830,12 @@ const docTemplate = `{ "level": { "$ref": "#/definitions/codersdk.LogLevel" }, - "log_source_id": { - "type": "string", - "format": "uuid" - }, "output": { "type": "string" + }, + "source_id": { + "type": "string", + "format": "uuid" } } }, diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 8686f94b46a86..aac04752204d1 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9831,12 +9831,12 @@ "level": { "$ref": "#/definitions/codersdk.LogLevel" }, - "log_source_id": { - "type": "string", - "format": "uuid" - }, "output": { "type": "string" + }, + "source_id": { + "type": "string", + "format": "uuid" } } }, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 5593e272a14e2..c139112360910 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -2256,6 +2256,7 @@ func convertWorkspaceAgentLog(logEntry database.WorkspaceAgentLog) codersdk.Work CreatedAt: logEntry.CreatedAt, Output: logEntry.Output, Level: codersdk.LogLevel(logEntry.Level), + SourceID: logEntry.LogSourceID, } } diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 9c6a7842567a3..3bd078bfe4dea 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -765,11 +765,11 @@ const ( ) type WorkspaceAgentLog struct { - ID int64 `json:"id"` - CreatedAt time.Time `json:"created_at" format:"date-time"` - Output string `json:"output"` - Level LogLevel `json:"level"` - LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` + ID int64 `json:"id"` + CreatedAt time.Time `json:"created_at" format:"date-time"` + Output string `json:"output"` + Level LogLevel `json:"level"` + SourceID uuid.UUID `json:"source_id" format:"uuid"` } type AgentSubsystem string diff --git a/docs/api/agents.md b/docs/api/agents.md index 6240b92fd5b6e..efd7c47f26395 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -942,8 +942,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/log "created_at": "2019-08-24T14:15:22Z", "id": 0, "level": "trace", - "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", - "output": "string" + "output": "string", + "source_id": "ae50a35c-df42-4eff-ba26-f8bc28d2af81" } ] ``` @@ -958,14 +958,14 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/log Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ----------------- | ------------------------------------------------ | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | false | | | -| `» id` | integer | false | | | -| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | -| `» log_source_id` | string(uuid) | false | | | -| `» output` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ------------------------------------------------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | integer | false | | | +| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | +| `» output` | string | false | | | +| `» source_id` | string(uuid) | false | | | #### Enumerated Values @@ -1038,8 +1038,8 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta "created_at": "2019-08-24T14:15:22Z", "id": 0, "level": "trace", - "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", - "output": "string" + "output": "string", + "source_id": "ae50a35c-df42-4eff-ba26-f8bc28d2af81" } ] ``` @@ -1054,14 +1054,14 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent}/sta Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ----------------- | ------------------------------------------------ | -------- | ------------ | ----------- | -| `[array item]` | array | false | | | -| `» created_at` | string(date-time) | false | | | -| `» id` | integer | false | | | -| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | -| `» log_source_id` | string(uuid) | false | | | -| `» output` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------- | ------------------------------------------------ | -------- | ------------ | ----------- | +| `[array item]` | array | false | | | +| `» created_at` | string(date-time) | false | | | +| `» id` | integer | false | | | +| `» level` | [codersdk.LogLevel](schemas.md#codersdkloglevel) | false | | | +| `» output` | string | false | | | +| `» source_id` | string(uuid) | false | | | #### Enumerated Values diff --git a/docs/api/schemas.md b/docs/api/schemas.md index b904fa4fd62c9..468f0dc6a5660 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -5862,20 +5862,20 @@ If the schedule is empty, the user will be updated to use the default schedule.| "created_at": "2019-08-24T14:15:22Z", "id": 0, "level": "trace", - "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", - "output": "string" + "output": "string", + "source_id": "ae50a35c-df42-4eff-ba26-f8bc28d2af81" } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -| --------------- | -------------------------------------- | -------- | ------------ | ----------- | -| `created_at` | string | false | | | -| `id` | integer | false | | | -| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | | -| `log_source_id` | string | false | | | -| `output` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------ | -------------------------------------- | -------- | ------------ | ----------- | +| `created_at` | string | false | | | +| `id` | integer | false | | | +| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | | +| `output` | string | false | | | +| `source_id` | string | false | | | ## codersdk.WorkspaceAgentLogSource diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 77464c5d128fc..ce6e01f5c6428 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1375,7 +1375,7 @@ export interface WorkspaceAgentLog { readonly created_at: string readonly output: string readonly level: LogLevel - readonly log_source_id: string + readonly source_id: string } // From codersdk/workspaceagents.go diff --git a/site/src/components/Logs/Logs.stories.tsx b/site/src/components/Logs/Logs.stories.tsx index 7f211cc9381e1..6a2149ba54ad4 100644 --- a/site/src/components/Logs/Logs.stories.tsx +++ b/site/src/components/Logs/Logs.stories.tsx @@ -14,6 +14,7 @@ const lines = MockWorkspaceBuildLogs.map((log) => ({ time: log.created_at, output: log.output, level: "info" as LogLevel, + source_id: log.log_source, })) export const Example = Template.bind({}) Example.args = { diff --git a/site/src/components/Logs/Logs.tsx b/site/src/components/Logs/Logs.tsx index 6ad2e6ddf34df..d9b4efa38142d 100644 --- a/site/src/components/Logs/Logs.tsx +++ b/site/src/components/Logs/Logs.tsx @@ -11,6 +11,7 @@ export interface Line { time: string output: string level: LogLevel + source_id: string } export interface LogsProps { diff --git a/site/src/components/Resources/AgentStatus.tsx b/site/src/components/Resources/AgentStatus.tsx index 7a8b80a3a57f1..84b30bd16c2d6 100644 --- a/site/src/components/Resources/AgentStatus.tsx +++ b/site/src/components/Resources/AgentStatus.tsx @@ -250,39 +250,34 @@ const OffLifecycle: React.FC = () => { const ConnectedStatus: React.FC<{ agent: WorkspaceAgent }> = ({ agent }) => { - switch (agent.startup_script_behavior) { - case "non-blocking": - return - case "blocking": - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - ) - } + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + ) } const DisconnectedStatus: React.FC = () => { diff --git a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx index e0682ce907c5c..0d3759d7379af 100644 --- a/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx +++ b/site/src/components/WorkspaceBuildLogs/WorkspaceBuildLogs.tsx @@ -72,6 +72,7 @@ export const WorkspaceBuildLogs: FC = ({ time: log.created_at, output: log.output, level: log.log_level, + source_id: log.log_source, })) const duration = getStageDurationInSeconds(logs) const shouldDisplayDuration = duration !== undefined diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index c5b272c0e4dc6..0924d55941d24 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -536,6 +536,25 @@ export const MockWorkspaceApp: TypesGen.WorkspaceApp = { }, } +export const MockWorkspaceAgentLogSource: TypesGen.WorkspaceAgentLogSource = { + created_at: "2023-05-04T11:30:41.402072Z", + id: "dc790496-eaec-4f88-a53f-8ce1f61a1fff", + display_name: "Startup Script", + icon: "", + workspace_agent_id: "", +}; + +export const MockWorkspaceAgentScript: TypesGen.WorkspaceAgentScript = { + log_source_id: MockWorkspaceAgentLogSource.id, + cron: "", + log_path: "", + run_on_start: true, + run_on_stop: false, + source: "echo 'hello world'", + start_blocks_login: false, + timeout_seconds: 0, +}; + export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { apps: [MockWorkspaceApp], architecture: "amd64", @@ -557,12 +576,10 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { connection_timeout_seconds: 120, troubleshooting_url: "https://coder.com/troubleshoot", lifecycle_state: "starting", - login_before_ready: false, // Deprecated. - startup_script_behavior: "blocking", logs_length: 0, logs_overflowed: false, - startup_script_timeout_seconds: 120, - shutdown_script_timeout_seconds: 120, + log_sources: [MockWorkspaceAgentLogSource], + scripts: [MockWorkspaceAgentScript], subsystems: ["envbox", "exectrace"], health: { healthy: true, @@ -2131,6 +2148,7 @@ export const MockWorkspaceAgentLogs: TypesGen.WorkspaceAgentLog[] = [ created_at: "2023-05-04T11:30:41.402072Z", output: "+ curl -fsSL https://code-server.dev/install.sh", level: "info", + source_id: MockWorkspaceAgentLogSource.id, }, { id: 166664, @@ -2138,18 +2156,21 @@ export const MockWorkspaceAgentLogs: TypesGen.WorkspaceAgentLog[] = [ output: "+ sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.8.3", level: "info", + source_id: MockWorkspaceAgentLogSource.id, }, { id: 166665, created_at: "2023-05-04T11:30:42.590731Z", output: "Ubuntu 22.04.2 LTS", level: "info", + source_id: MockWorkspaceAgentLogSource.id, }, { id: 166666, created_at: "2023-05-04T11:30:42.593686Z", output: "Installing v4.8.3 of the amd64 release from GitHub.", level: "info", + source_id: MockWorkspaceAgentLogSource.id, }, ] diff --git a/site/src/xServices/workspaceAgentLogs/workspaceAgentLogsXService.ts b/site/src/xServices/workspaceAgentLogs/workspaceAgentLogsXService.ts index 9f2277dc8b38d..71eea6f345888 100644 --- a/site/src/xServices/workspaceAgentLogs/workspaceAgentLogsXService.ts +++ b/site/src/xServices/workspaceAgentLogs/workspaceAgentLogsXService.ts @@ -81,6 +81,7 @@ export const workspaceAgentLogsMachine = createMachine( level: log.level || "info", output: log.output, time: log.created_at, + source_id: log.source_id, })), ), streamLogs: (ctx) => async (callback) => { @@ -99,6 +100,7 @@ export const workspaceAgentLogsMachine = createMachine( level: log.level || "info", output: log.output, time: log.created_at, + source_id: log.source_id, })), }) }, From e2c9f91441cfe32c28e25e817870f20997d89e53 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 20:40:47 +0000 Subject: [PATCH 12/59] Fix the FE --- .../components/Resources/AgentRow.stories.tsx | 2 - site/src/components/Resources/AgentRow.tsx | 70 ++----------------- 2 files changed, 6 insertions(+), 66 deletions(-) diff --git a/site/src/components/Resources/AgentRow.stories.tsx b/site/src/components/Resources/AgentRow.stories.tsx index 5e4adf5e1b4f0..b2594623ec589 100644 --- a/site/src/components/Resources/AgentRow.stories.tsx +++ b/site/src/components/Resources/AgentRow.stories.tsx @@ -145,8 +145,6 @@ export const Example = Template.bind({}) Example.args = { agent: { ...MockWorkspaceAgent, - startup_script: - 'set -eux -o pipefail\n\n# install and start code-server\ncurl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.8.3\n/tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &\n\n\nif [ ! -d ~/coder ]; then\n mkdir -p ~/coder\n\n git clone https://github.com/coder/coder ~/coder\nfi\n\nsudo service docker start\nDOTFILES_URI=" "\nrm -f ~/.personalize.log\nif [ -n "${DOTFILES_URI// }" ]; then\n coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee -a ~/.personalize.log\nfi\nif [ -x ~/personalize ]; then\n ~/personalize 2>&1 | tee -a ~/.personalize.log\nelif [ -f ~/personalize ]; then\n echo "~/personalize is not executable, skipping..." | tee -a ~/.personalize.log\nfi\n', }, workspace: MockWorkspace, showApps: true, diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/components/Resources/AgentRow.tsx index 508156e5739b0..740c9bc92fd97 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/components/Resources/AgentRow.tsx @@ -1,15 +1,14 @@ -import Popover from "@mui/material/Popover" -import { makeStyles, useTheme } from "@mui/styles" +import Collapse from "@mui/material/Collapse" import Skeleton from "@mui/material/Skeleton" +import { makeStyles } from "@mui/styles" import { useMachine } from "@xstate/react" -import CodeOutlined from "@mui/icons-material/CodeOutlined" import { CloseDropdown, OpenDropdown, } from "components/DropdownArrows/DropdownArrows" import { LogLine, logLineHeight } from "components/Logs/Logs" -import { PortForwardButton } from "./PortForwardButton" import { VSCodeDesktopButton } from "components/VSCodeDesktopButton/VSCodeDesktopButton" +import { useProxy } from "contexts/ProxyContext" import { FC, useCallback, @@ -19,8 +18,6 @@ import { useRef, useState, } from "react" -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" -import { darcula } from "react-syntax-highlighter/dist/cjs/styles/prism" import AutoSizer from "react-virtualized-auto-sizer" import { FixedSizeList as List, ListOnScrollProps } from "react-window" import { colors } from "theme/colors" @@ -40,10 +37,9 @@ import { Stack } from "../Stack/Stack" import { TerminalLink } from "../TerminalLink/TerminalLink" import { AgentLatency } from "./AgentLatency" import { AgentMetadata } from "./AgentMetadata" -import { AgentVersion } from "./AgentVersion" import { AgentStatus } from "./AgentStatus" -import Collapse from "@mui/material/Collapse" -import { useProxy } from "contexts/ProxyContext" +import { AgentVersion } from "./AgentVersion" +import { PortForwardButton } from "./PortForwardButton" export interface AgentRowProps { agent: WorkspaceAgent @@ -86,9 +82,6 @@ export const AgentRow: FC = ({ } : undefined, }) - const theme = useTheme() - const startupScriptAnchorRef = useRef(null) - const [startupScriptOpen, setStartupScriptOpen] = useState(false) const hasAppsToDisplay = !hideVSCodeDesktopButton || agent.apps.length > 0 const shouldDisplayApps = showApps && @@ -132,6 +125,7 @@ export const AgentRow: FC = ({ level: "error", output: "Startup logs exceeded the max size of 1MB!", time: new Date().toISOString(), + source_id: "", }) } return logs @@ -348,47 +342,6 @@ export const AgentRow: FC = ({ Show startup logs )} - - - - setStartupScriptOpen(false)} - anchorEl={startupScriptAnchorRef.current} - > -
- - {agent.startup_script || ""} - -
-
)} @@ -510,10 +463,6 @@ const useStyles = makeStyles((theme) => ({ }, }, - startupScriptPopover: { - backgroundColor: theme.palette.background.default, - }, - agentNameAndStatus: { display: "flex", alignItems: "center", @@ -602,13 +551,6 @@ const useStyles = makeStyles((theme) => ({ color: theme.palette.warning.light, }, - scriptButton: { - "& svg": { - width: theme.spacing(2), - height: theme.spacing(2), - }, - }, - agentOS: { textTransform: "capitalize", }, From 51e08f445af67952618604ea42f6b52ece3f0064 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 20:49:55 +0000 Subject: [PATCH 13/59] fmt --- site/src/api/typesGenerated.ts | 16 ++++++++-------- site/src/components/WorkspaceBuildLogs/Logs.tsx | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 3378bb67c089b..90c5aca0800ba 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1408,14 +1408,14 @@ export interface WorkspaceAgentMetadataResult { // From codersdk/workspaceagents.go export interface WorkspaceAgentScript { - readonly log_source_id: string - readonly log_path: string - readonly source: string - readonly cron: string - readonly run_on_start: boolean - readonly run_on_stop: boolean - readonly start_blocks_login: boolean - readonly timeout_seconds: number + readonly log_source_id: string; + readonly log_path: string; + readonly source: string; + readonly cron: string; + readonly run_on_start: boolean; + readonly run_on_stop: boolean; + readonly start_blocks_login: boolean; + readonly timeout_seconds: number; } // From codersdk/workspaceapps.go diff --git a/site/src/components/WorkspaceBuildLogs/Logs.tsx b/site/src/components/WorkspaceBuildLogs/Logs.tsx index cff3171984f76..76dd480202570 100644 --- a/site/src/components/WorkspaceBuildLogs/Logs.tsx +++ b/site/src/components/WorkspaceBuildLogs/Logs.tsx @@ -7,10 +7,10 @@ import { combineClasses } from "utils/combineClasses"; import AnsiToHTML from "ansi-to-html"; export interface Line { - time: string - output: string - level: LogLevel - source_id: string + time: string; + output: string; + level: LogLevel; + source_id: string; } export interface LogsProps { From 9a38131e45e6ca330c8a966a162ef4c8c00adeb4 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 7 Sep 2023 20:50:29 +0000 Subject: [PATCH 14/59] Rename migrations --- ...ent_script.down.sql => 000155_workspace_agent_script.down.sql} | 0 ...e_agent_script.up.sql => 000155_workspace_agent_script.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000154_workspace_agent_script.down.sql => 000155_workspace_agent_script.down.sql} (100%) rename coderd/database/migrations/{000154_workspace_agent_script.up.sql => 000155_workspace_agent_script.up.sql} (100%) diff --git a/coderd/database/migrations/000154_workspace_agent_script.down.sql b/coderd/database/migrations/000155_workspace_agent_script.down.sql similarity index 100% rename from coderd/database/migrations/000154_workspace_agent_script.down.sql rename to coderd/database/migrations/000155_workspace_agent_script.down.sql diff --git a/coderd/database/migrations/000154_workspace_agent_script.up.sql b/coderd/database/migrations/000155_workspace_agent_script.up.sql similarity index 100% rename from coderd/database/migrations/000154_workspace_agent_script.up.sql rename to coderd/database/migrations/000155_workspace_agent_script.up.sql From c0fac6b1661f4f10c871dc703f4ac5a06633f043 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 8 Sep 2023 13:51:43 +0000 Subject: [PATCH 15/59] Fix log tests --- codersdk/agentsdk/logs_test.go | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/codersdk/agentsdk/logs_test.go b/codersdk/agentsdk/logs_test.go index 15c9888081d56..eb2299092efca 100644 --- a/codersdk/agentsdk/logs_test.go +++ b/codersdk/agentsdk/logs_test.go @@ -56,12 +56,10 @@ func TestStartupLogsWriter_Write(t *testing.T) { { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "goodbye world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, }, }, @@ -74,32 +72,26 @@ func TestStartupLogsWriter_Write(t *testing.T) { { Level: codersdk.LogLevelInfo, Output: "", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "goodbye world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, }, }, @@ -112,7 +104,6 @@ func TestStartupLogsWriter_Write(t *testing.T) { { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, }, }, @@ -126,12 +117,10 @@ func TestStartupLogsWriter_Write(t *testing.T) { { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "goodbye world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, }, }, @@ -144,12 +133,10 @@ func TestStartupLogsWriter_Write(t *testing.T) { { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "goodbye world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, }, }, @@ -162,17 +149,14 @@ func TestStartupLogsWriter_Write(t *testing.T) { { Level: codersdk.LogLevelInfo, Output: "hello world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "\r", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, { Level: codersdk.LogLevelInfo, Output: "goodbye world", - Source: codersdk.WorkspaceAgentLogSourceExternal, }, }, }, @@ -200,7 +184,7 @@ func TestStartupLogsWriter_Write(t *testing.T) { got = append(got, log...) return nil } - w := agentsdk.StartupLogsWriter(tt.ctx, send, tt.source, tt.level) + w := agentsdk.StartupLogsWriter(tt.ctx, send, uuid.New(), tt.level) for _, s := range tt.writes { _, err := w.Write([]byte(s)) if err != nil { @@ -290,7 +274,7 @@ func TestStartupLogsSender(t *testing.T) { return nil } - sendLog, flushAndClose := agentsdk.LogsSender(patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug)) + sendLog, flushAndClose := agentsdk.LogsSender(uuid.New(), patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug)) defer func() { err := flushAndClose(ctx) require.NoError(t, err) @@ -360,7 +344,7 @@ func TestStartupLogsSender(t *testing.T) { return nil } - sendLog, flushAndClose := agentsdk.LogsSender(patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug)) + sendLog, flushAndClose := agentsdk.LogsSender(uuid.New(), patchLogs, slogtest.Make(t, nil).Leveled(slog.LevelDebug)) defer func() { _ = flushAndClose(ctx) }() From f7f1c7aa487ccffc75919ccedacb47eec0222d36 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 8 Sep 2023 13:52:43 +0000 Subject: [PATCH 16/59] Fix lint err --- coderd/workspaceagents.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index c139112360910..deffe63be91ba 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -65,15 +65,15 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { var eg errgroup.Group eg.Go(func() (err error) { dbApps, err = api.Database.GetWorkspaceAppsByAgentID(ctx, workspaceAgent.ID) - return + return err }) eg.Go(func() (err error) { scripts, err = api.Database.GetWorkspaceAgentScriptsByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) - return + return err }) eg.Go(func() (err error) { logSources, err = api.Database.GetWorkspaceAgentLogSourcesByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) - return + return err }) apiAgent, err := convertWorkspaceAgent( api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout, From f0a8f5359af75fcb86ec3f759d5cd243c854ba25 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 8 Sep 2023 14:26:24 +0000 Subject: [PATCH 17/59] Fix gen --- coderd/database/dump.sql | 10 +++++- site/src/api/typesGenerated.ts | 35 +++++++------------ .../components/Resources/AgentRow.stories.tsx | 2 -- 3 files changed, 21 insertions(+), 26 deletions(-) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 70874b3e710d9..34dcacdcaca3a 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -730,7 +730,15 @@ COMMENT ON COLUMN user_links.oauth_access_token_key_id IS 'The ID of the key use COMMENT ON COLUMN user_links.oauth_refresh_token_key_id IS 'The ID of the key used to encrypt the OAuth refresh token. If this is NULL, the refresh token is not encrypted'; -CREATE TABLE workspace_agent_logs ( +CREATE TABLE workspace_agent_log_sources ( + workspace_agent_id uuid NOT NULL, + id uuid NOT NULL, + created_at timestamp with time zone NOT NULL, + display_name character varying(127) NOT NULL, + icon text NOT NULL +); + +CREATE UNLOGGED TABLE workspace_agent_logs ( agent_id uuid NOT NULL, created_at timestamp with time zone NOT NULL, output character varying(1024) NOT NULL, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 90c5aca0800ba..9bc43d0540d4f 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1337,9 +1337,6 @@ export interface WorkspaceAgent { readonly architecture: string; readonly environment_variables: Record; readonly operating_system: string; - readonly startup_script?: string; - readonly startup_script_behavior: WorkspaceAgentStartupScriptBehavior; - readonly startup_script_timeout_seconds: number; readonly logs_length: number; readonly logs_overflowed: boolean; readonly directory?: string; @@ -1349,12 +1346,11 @@ export interface WorkspaceAgent { readonly latency?: Record; readonly connection_timeout_seconds: number; readonly troubleshooting_url: string; - readonly login_before_ready: boolean; - readonly shutdown_script?: string; - readonly shutdown_script_timeout_seconds: number; readonly subsystems: AgentSubsystem[]; readonly health: WorkspaceAgentHealth; readonly display_apps: DisplayApp[]; + readonly log_sources: WorkspaceAgentLogSource[]; + readonly scripts: WorkspaceAgentScript[]; } // From codersdk/workspaceagents.go @@ -1381,6 +1377,16 @@ export interface WorkspaceAgentLog { readonly created_at: string; readonly output: string; readonly level: LogLevel; + readonly source_id: string; +} + +// From codersdk/workspaceagents.go +export interface WorkspaceAgentLogSource { + readonly workspace_agent_id: string; + readonly id: string; + readonly created_at: string; + readonly display_name: string; + readonly icon: string; } // From codersdk/workspaceagents.go @@ -1903,23 +1909,6 @@ export const WorkspaceAgentLifecycles: WorkspaceAgentLifecycle[] = [ "starting", ]; -// From codersdk/workspaceagents.go -export type WorkspaceAgentLogSource = - | "envbox" - | "envbuilder" - | "external" - | "kubernetes" - | "shutdown_script" - | "startup_script"; -export const WorkspaceAgentLogSources: WorkspaceAgentLogSource[] = [ - "envbox", - "envbuilder", - "external", - "kubernetes", - "shutdown_script", - "startup_script", -]; - // From codersdk/workspaceagents.go export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking"; export const WorkspaceAgentStartupScriptBehaviors: WorkspaceAgentStartupScriptBehavior[] = diff --git a/site/src/components/Resources/AgentRow.stories.tsx b/site/src/components/Resources/AgentRow.stories.tsx index 08a49a2fc0141..0bef8f544ce9a 100644 --- a/site/src/components/Resources/AgentRow.stories.tsx +++ b/site/src/components/Resources/AgentRow.stories.tsx @@ -102,8 +102,6 @@ const meta: Meta = { })), agent: { ...MockWorkspaceAgent, - startup_script: - 'set -eux -o pipefail\n\n# install and start code-server\ncurl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.8.3\n/tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &\n\n\nif [ ! -d ~/coder ]; then\n mkdir -p ~/coder\n\n git clone https://github.com/coder/coder ~/coder\nfi\n\nsudo service docker start\nDOTFILES_URI=" "\nrm -f ~/.personalize.log\nif [ -n "${DOTFILES_URI// }" ]; then\n coder dotfiles "$DOTFILES_URI" -y 2>&1 | tee -a ~/.personalize.log\nfi\nif [ -x ~/personalize ]; then\n ~/personalize 2>&1 | tee -a ~/.personalize.log\nelif [ -f ~/personalize ]; then\n echo "~/personalize is not executable, skipping..." | tee -a ~/.personalize.log\nfi\n', }, workspace: MockWorkspace, showApps: true, From 66f9185861543d1abdb7b2cc3be3b41a6e138104 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Fri, 8 Sep 2023 14:29:17 +0000 Subject: [PATCH 18/59] Fix story type --- site/src/components/Resources/AgentRow.stories.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/components/Resources/AgentRow.stories.tsx b/site/src/components/Resources/AgentRow.stories.tsx index 0bef8f544ce9a..f30cfd1357b2a 100644 --- a/site/src/components/Resources/AgentRow.stories.tsx +++ b/site/src/components/Resources/AgentRow.stories.tsx @@ -99,6 +99,7 @@ const meta: Meta = { level: "info", output: line, time: "", + source_id: "", })), agent: { ...MockWorkspaceAgent, From 78f01d1032b38bf377e6b13186c6f99b5b6e84e4 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 13 Sep 2023 17:47:19 +0000 Subject: [PATCH 19/59] Rename source to script --- agent/agent.go | 1 + agent/agent_test.go | 16 +-- agent/agentscripts/agentscripts.go | 12 ++- agent/agentscripts/agentscripts_test.go | 4 +- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 2 +- coderd/database/dbfake/dbfake.go | 2 +- coderd/database/dump.sql | 2 +- .../000155_workspace_agent_script.up.sql | 2 +- coderd/database/models.go | 2 +- coderd/database/queries.sql.go | 16 +-- coderd/database/queries/workspacescripts.sql | 4 +- .../provisionerdserver/provisionerdserver.go | 4 +- coderd/workspaceagents.go | 2 +- codersdk/workspaceagents.go | 2 +- docs/api/agents.md | 4 +- docs/api/builds.md | 16 +-- docs/api/schemas.md | 16 +-- docs/api/templates.md | 8 +- docs/api/workspaces.md | 10 +- provisioner/terraform/resources.go | 8 +- provisionersdk/proto/provisioner.pb.go | 12 +-- provisionersdk/proto/provisioner.proto | 2 +- site/src/api/typesGenerated.ts | 2 +- site/src/components/Resources/AgentRow.tsx | 102 ++++++++++++++++-- .../components/WorkspaceBuildLogs/Logs.tsx | 11 +- site/src/testHelpers/entities.ts | 2 +- 27 files changed, 181 insertions(+), 85 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index d93e6334fb5c8..cb95fbc2ff88b 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -656,6 +656,7 @@ func (a *agent) run(ctx context.Context) error { } else { a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleReady) } + a.scriptRunner.StartCRON() }) if err != nil { return xerrors.Errorf("track conn goroutine: %w", err) diff --git a/agent/agent_test.go b/agent/agent_test.go index 1f7d5baa4158d..96cc239e01697 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1209,7 +1209,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Source: "sleep 3", + Script: "sleep 3", Timeout: time.Nanosecond, RunOnStart: true, }}, @@ -1234,7 +1234,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Source: "false", + Script: "false", Timeout: 30 * time.Second, RunOnStart: true, }}, @@ -1259,7 +1259,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Source: "true", + Script: "true", Timeout: 30 * time.Second, RunOnStart: true, }}, @@ -1284,7 +1284,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Source: "sleep 3", + Script: "sleep 3", Timeout: 30 * time.Second, RunOnStop: true, }}, @@ -1325,7 +1325,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Source: "sleep 3", + Script: "sleep 3", Timeout: time.Nanosecond, RunOnStop: true, }}, @@ -1367,7 +1367,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Source: "false", + Script: "false", Timeout: 30 * time.Second, RunOnStop: true, }}, @@ -1417,11 +1417,11 @@ func TestAgent_Lifecycle(t *testing.T) { DERPMap: derpMap, Scripts: []codersdk.WorkspaceAgentScript{{ LogPath: "coder-startup-script.log", - Source: "echo 1", + Script: "echo 1", RunOnStart: true, }, { LogPath: "coder-shutdown-script.log", - Source: "echo " + expected, + Script: "echo " + expected, RunOnStop: true, }}, }, diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 9654dd8daf7a5..7ef609466c6c2 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -25,7 +25,7 @@ var ( // ErrTimeout is returned when a script times out. ErrTimeout = xerrors.New("script timed out") - parser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow) + parser = cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.DowOptional) ) // Options are a set of options for the runner. @@ -82,6 +82,12 @@ func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { return nil } +// StartCRON starts the cron scheduler. +// This is done async to allow for the caller to execute scripts prior. +func (r *Runner) StartCRON() { + r.cron.Start() +} + // Execute runs a set of scripts according to a filter. func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) error { if filter == nil { @@ -114,7 +120,7 @@ func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { logger := r.Logger.With(slog.F("log_source", script.LogPath)) ctx := r.ctx - logger.Info(ctx, "running agent script", slog.F("script", script.Source)) + logger.Info(ctx, "running agent script", slog.F("script", script.Script)) logPath := script.LogPath if logPath == "" { @@ -142,7 +148,7 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { defer cancel() } - cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Source, nil) + cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Script, nil) if err != nil { return xerrors.Errorf("%s script: create command: %w", logPath, err) } diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index 1b2643233c991..af3b734839285 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -31,7 +31,7 @@ func TestExecuteBasic(t *testing.T) { }) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - Source: "echo hello", + Script: "echo hello", }}) require.NoError(t, err) require.NoError(t, runner.Execute(func(script codersdk.WorkspaceAgentScript) bool { @@ -46,7 +46,7 @@ func TestTimeout(t *testing.T) { runner := setup(t, nil) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - Source: "sleep 3", + Script: "sleep 3", Timeout: time.Nanosecond, }}) require.NoError(t, err) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index aba4faa995774..aa4ffe60d7cae 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -10907,7 +10907,7 @@ const docTemplate = `{ "run_on_stop": { "type": "boolean" }, - "source": { + "script": { "type": "string" }, "start_blocks_login": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 27a3e2c998d43..f108a55b5e113 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9908,7 +9908,7 @@ "run_on_stop": { "type": "boolean" }, - "source": { + "script": { "type": "string" }, "start_blocks_login": { diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 1895ef91a18b1..288312aded8c9 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -4645,7 +4645,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentScripts(_ context.Context, arg databas LogSourceID: source, WorkspaceAgentID: arg.WorkspaceAgentID, LogPath: arg.LogPath[index], - Source: arg.Source[index], + Script: arg.Script[index], Cron: arg.Cron[index], StartBlocksLogin: arg.StartBlocksLogin[index], RunOnStart: arg.RunOnStart[index], diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 34dcacdcaca3a..5cbd809090670 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -764,7 +764,7 @@ CREATE TABLE workspace_agent_scripts ( log_source_id uuid NOT NULL, log_path text NOT NULL, created_at timestamp with time zone NOT NULL, - source text NOT NULL, + script text NOT NULL, cron text NOT NULL, start_blocks_login boolean NOT NULL, run_on_start boolean NOT NULL, diff --git a/coderd/database/migrations/000155_workspace_agent_script.up.sql b/coderd/database/migrations/000155_workspace_agent_script.up.sql index 005d4d88a3ff2..5673e5588e1f9 100644 --- a/coderd/database/migrations/000155_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000155_workspace_agent_script.up.sql @@ -13,7 +13,7 @@ CREATE TABLE workspace_agent_scripts ( log_source_id uuid NOT NULL, log_path text NOT NULL, created_at timestamptz NOT NULL, - source text NOT NULL, + script text NOT NULL, cron text NOT NULL, start_blocks_login boolean NOT NULL, run_on_start boolean NOT NULL, diff --git a/coderd/database/models.go b/coderd/database/models.go index 6e58f833fed06..08a87774cc7db 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -2004,7 +2004,7 @@ type WorkspaceAgentScript struct { LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` LogPath string `db:"log_path" json:"log_path"` CreatedAt time.Time `db:"created_at" json:"created_at"` - Source string `db:"source" json:"source"` + Script string `db:"script" json:"script"` Cron string `db:"cron" json:"cron"` StartBlocksLogin bool `db:"start_blocks_login" json:"start_blocks_login"` RunOnStart bool `db:"run_on_start" json:"run_on_start"` diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index c0f0778782285..e435a34d166a1 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -9250,7 +9250,7 @@ func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg In } const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many -SELECT workspace_agent_id, log_source_id, log_path, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +SELECT workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) ` func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { @@ -9267,7 +9267,7 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids &i.LogSourceID, &i.LogPath, &i.CreatedAt, - &i.Source, + &i.Script, &i.Cron, &i.StartBlocksLogin, &i.RunOnStart, @@ -9289,19 +9289,19 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout) SELECT $1 :: uuid AS workspace_agent_id, unnest($2 :: uuid [ ]) AS log_source_id, unnest($3 :: text [ ]) AS log_path, unnest($4 :: timestamptz [ ]) AS created_at, - unnest($5 :: text [ ]) AS source, + unnest($5 :: text [ ]) AS script, unnest($6 :: text [ ]) AS cron, unnest($7 :: boolean [ ]) AS start_blocks_login, unnest($8 :: boolean [ ]) AS run_on_start, unnest($9 :: boolean [ ]) AS run_on_stop, unnest($10 :: integer [ ]) AS timeout -RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.source, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout +RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.script, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout ` type InsertWorkspaceAgentScriptsParams struct { @@ -9309,7 +9309,7 @@ type InsertWorkspaceAgentScriptsParams struct { LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` LogPath []string `db:"log_path" json:"log_path"` CreatedAt []time.Time `db:"created_at" json:"created_at"` - Source []string `db:"source" json:"source"` + Script []string `db:"script" json:"script"` Cron []string `db:"cron" json:"cron"` StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` RunOnStart []bool `db:"run_on_start" json:"run_on_start"` @@ -9323,7 +9323,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg Insert pq.Array(arg.LogSourceID), pq.Array(arg.LogPath), pq.Array(arg.CreatedAt), - pq.Array(arg.Source), + pq.Array(arg.Script), pq.Array(arg.Cron), pq.Array(arg.StartBlocksLogin), pq.Array(arg.RunOnStart), @@ -9342,7 +9342,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg Insert &i.LogSourceID, &i.LogPath, &i.CreatedAt, - &i.Source, + &i.Script, &i.Cron, &i.StartBlocksLogin, &i.RunOnStart, diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql index 3f687c810e000..bdfae1c5be66b 100644 --- a/coderd/database/queries/workspacescripts.sql +++ b/coderd/database/queries/workspacescripts.sql @@ -1,12 +1,12 @@ -- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, source, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, unnest(@log_source_id :: uuid [ ]) AS log_source_id, unnest(@log_path :: text [ ]) AS log_path, unnest(@created_at :: timestamptz [ ]) AS created_at, - unnest(@source :: text [ ]) AS source, + unnest(@script :: text [ ]) AS script, unnest(@cron :: text [ ]) AS cron, unnest(@start_blocks_login :: boolean [ ]) AS start_blocks_login, unnest(@run_on_start :: boolean [ ]) AS run_on_start, diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 54adbafc67112..c5b41dd6799bd 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1299,7 +1299,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. logSourceDisplayNames = append(logSourceDisplayNames, script.DisplayName) logSourceIcons = append(logSourceIcons, script.Icon) scriptLogPaths = append(scriptLogPaths, script.LogPath) - scriptSources = append(scriptSources, script.Source) + scriptSources = append(scriptSources, script.Script) scriptCron = append(scriptCron, script.Cron) scriptTimeout = append(scriptTimeout, script.Timeout) scriptStartBlocksLogin = append(scriptStartBlocksLogin, script.StartBlocksLogin) @@ -1323,7 +1323,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. LogSourceID: logSourceIDs, LogPath: scriptLogPaths, CreatedAt: logSourceCreatedAt, - Source: scriptSources, + Script: scriptSources, Cron: scriptCron, Timeout: scriptTimeout, StartBlocksLogin: scriptStartBlocksLogin, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index deffe63be91ba..285dd15aff0b9 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1322,7 +1322,7 @@ func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.Worksp scripts = append(scripts, codersdk.WorkspaceAgentScript{ LogPath: dbScript.LogPath, LogSourceID: dbScript.LogSourceID, - Source: dbScript.Source, + Script: dbScript.Script, CRON: dbScript.Cron, RunOnStart: dbScript.RunOnStart, RunOnStop: dbScript.RunOnStop, diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 3bd078bfe4dea..a81fe14a0a435 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -188,7 +188,7 @@ type WorkspaceAgentLogSource struct { type WorkspaceAgentScript struct { LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` LogPath string `json:"log_path"` - Source string `json:"source"` + Script string `json:"script"` CRON string `json:"cron"` RunOnStart bool `json:"run_on_start"` RunOnStop bool `json:"run_on_stop"` diff --git a/docs/api/agents.md b/docs/api/agents.md index efd7c47f26395..407b143ce9d20 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -476,7 +476,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/manifest \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -721,7 +721,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } diff --git a/docs/api/builds.md b/docs/api/builds.md index 2cfc532a02540..57946909714a4 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -129,7 +129,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -307,7 +307,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -624,7 +624,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -725,7 +725,7 @@ Status Code **200** | `»»» log_source_id` | string(uuid) | false | | | | `»»» run_on_start` | boolean | false | | | | `»»» run_on_stop` | boolean | false | | | -| `»»» source` | string | false | | | +| `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | | `»»» timeout_seconds` | integer | false | | | | `»» started_at` | string(date-time) | false | | | @@ -905,7 +905,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -1088,7 +1088,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -1225,7 +1225,7 @@ Status Code **200** | `»»»» log_source_id` | string(uuid) | false | | | | `»»»» run_on_start` | boolean | false | | | | `»»»» run_on_stop` | boolean | false | | | -| `»»»» source` | string | false | | | +| `»»»» script` | string | false | | | | `»»»» start_blocks_login` | boolean | false | | | | `»»»» timeout_seconds` | integer | false | | | | `»»» started_at` | string(date-time) | false | | | @@ -1458,7 +1458,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 19a7278749260..f8a810cbe8c63 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -283,7 +283,7 @@ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -5497,7 +5497,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -5655,7 +5655,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -5933,7 +5933,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -5948,7 +5948,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `log_source_id` | string | false | | | | `run_on_start` | boolean | false | | | | `run_on_stop` | boolean | false | | | -| `source` | string | false | | | +| `script` | string | false | | | | `start_blocks_login` | boolean | false | | | | `timeout_seconds` | integer | false | | | @@ -6154,7 +6154,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -6481,7 +6481,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -6710,7 +6710,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } diff --git a/docs/api/templates.md b/docs/api/templates.md index b5817ae59092f..7e56404972679 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1640,7 +1640,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -1741,7 +1741,7 @@ Status Code **200** | `»»» log_source_id` | string(uuid) | false | | | | `»»» run_on_start` | boolean | false | | | | `»»» run_on_stop` | boolean | false | | | -| `»»» source` | string | false | | | +| `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | | `»»» timeout_seconds` | integer | false | | | | `»» started_at` | string(date-time) | false | | | @@ -2055,7 +2055,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -2156,7 +2156,7 @@ Status Code **200** | `»»» log_source_id` | string(uuid) | false | | | | `»»» run_on_start` | boolean | false | | | | `»»» run_on_stop` | boolean | false | | | -| `»»» source` | string | false | | | +| `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | | `»»» timeout_seconds` | integer | false | | | | `»» started_at` | string(date-time) | false | | | diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index f3502d807d4be..096f1392bd176 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -159,7 +159,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -364,7 +364,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -568,7 +568,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -774,7 +774,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } @@ -1059,7 +1059,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", "run_on_start": true, "run_on_stop": true, - "source": "string", + "script": "string", "start_blocks_login": true, "timeout_seconds": 0 } diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index cf3eef1df6c78..45953f90151cf 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -81,7 +81,7 @@ type agentScriptAttributes struct { AgentID string `mapstructure:"agent_id"` DisplayName string `mapstructure:"display_name"` Icon string `mapstructure:"icon"` - Source string `mapstructure:"source"` + Script string `mapstructure:"script"` Cron string `mapstructure:"cron"` StartBlocksLogin bool `mapstructure:"start_blocks_login"` RunOnStart bool `mapstructure:"run_on_start"` @@ -236,7 +236,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error agent.Scripts = append(agent.Scripts, &proto.Script{ LogPath: "coder-startup-script.log", DisplayName: "Startup Script", - Source: attrs.StartupScript, + Script: attrs.StartupScript, StartBlocksLogin: startupScriptBehavior == string(codersdk.WorkspaceAgentStartupScriptBehaviorBlocking), Timeout: attrs.StartupScriptTimeoutSeconds, RunOnStart: true, @@ -246,7 +246,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error agent.Scripts = append(agent.Scripts, &proto.Script{ LogPath: "coder-shutdown-script.log", DisplayName: "Shutdown Script", - Source: attrs.ShutdownScript, + Script: attrs.ShutdownScript, Timeout: attrs.ShutdownScriptTimeoutSeconds, RunOnStop: true, }) @@ -451,7 +451,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error agent.Scripts = append(agent.Scripts, &proto.Script{ DisplayName: attrs.DisplayName, Icon: attrs.Icon, - Source: attrs.Source, + Script: attrs.Script, Cron: attrs.Cron, StartBlocksLogin: attrs.StartBlocksLogin, RunOnStart: attrs.RunOnStart, diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index 84c6d37dae05a..c88fd200474c0 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -1098,7 +1098,7 @@ type Script struct { DisplayName string `protobuf:"bytes,1,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` Icon string `protobuf:"bytes,2,opt,name=icon,proto3" json:"icon,omitempty"` - Source string `protobuf:"bytes,3,opt,name=source,proto3" json:"source,omitempty"` + Script string `protobuf:"bytes,3,opt,name=script,proto3" json:"script,omitempty"` Cron string `protobuf:"bytes,4,opt,name=cron,proto3" json:"cron,omitempty"` StartBlocksLogin bool `protobuf:"varint,5,opt,name=start_blocks_login,json=startBlocksLogin,proto3" json:"start_blocks_login,omitempty"` RunOnStart bool `protobuf:"varint,6,opt,name=run_on_start,json=runOnStart,proto3" json:"run_on_start,omitempty"` @@ -1153,9 +1153,9 @@ func (x *Script) GetIcon() string { return "" } -func (x *Script) GetSource() string { +func (x *Script) GetScript() string { if x != nil { - return x.Source + return x.Script } return "" } @@ -2632,9 +2632,9 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x70, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x69, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x4c, 0x6f, diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 751bfc8836bd7..68f0dfb587ef7 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -139,7 +139,7 @@ message DisplayApps { message Script { string display_name = 1; string icon = 2; - string source = 3; + string script = 3; string cron = 4; bool start_blocks_login = 5; bool run_on_start = 6; diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 9bc43d0540d4f..77fe8f1a48947 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1416,7 +1416,7 @@ export interface WorkspaceAgentMetadataResult { export interface WorkspaceAgentScript { readonly log_source_id: string; readonly log_path: string; - readonly source: string; + readonly script: string; readonly cron: string; readonly run_on_start: boolean; readonly run_on_stop: boolean; diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/components/Resources/AgentRow.tsx index cc2188971117b..b816da534b086 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/components/Resources/AgentRow.tsx @@ -29,6 +29,7 @@ import { import { Workspace, WorkspaceAgent, + WorkspaceAgentLogSource, WorkspaceAgentMetadata, } from "../../api/typesGenerated"; import { Stack } from "../Stack/Stack"; @@ -40,6 +41,7 @@ import { AppLink } from "./AppLink/AppLink"; import { PortForwardButton } from "./PortForwardButton"; import { SSHButton } from "./SSHButton/SSHButton"; import { TerminalLink } from "./TerminalLink/TerminalLink"; +import Tooltip from "@mui/material/Tooltip"; export interface AgentRowProps { agent: WorkspaceAgent; @@ -89,6 +91,13 @@ export const AgentRow: FC = ({ agent.status === "connecting"); const hasStartupFeatures = Boolean(agent.logs_length) || Boolean(logsMachine.context.logs?.length); + const logSourceByID = useMemo(() => { + const sources: { [id: string]: WorkspaceAgentLogSource } = {}; + for (const source of agent.log_sources) { + sources[source.id] = source; + } + return sources; + }, [agent.log_sources]); const { proxy } = useProxy(); const [showLogs, setShowLogs] = useState( @@ -302,13 +311,88 @@ export const AgentRow: FC = ({ className={styles.startupLogs} onScroll={handleLogScroll} > - {({ index, style }) => ( - - )} + {({ index, style }) => { + const log = startupLogs[index]; + let sourceIcon: string | undefined = + logSourceByID[log.source_id].icon; + if ( + index > 0 && + logSourceByID[startupLogs[index - 1].source_id].id === + log.source_id + ) { + sourceIcon = undefined; + } + + let icon = ( + + + + ); + let nextChangesSource = false; + if (index < startupLogs.length - 1) { + nextChangesSource = + logSourceByID[startupLogs[index + 1].source_id].id !== + log.source_id; + } + + if (!sourceIcon) { + icon = ( +
+
+ {nextChangesSource && ( +
+ )} +
+ ); + } + + return ( + + ); + }} )} @@ -326,7 +410,7 @@ export const AgentRow: FC = ({ }} > - Hide startup logs + Hide logs ) : ( )}
diff --git a/site/src/components/WorkspaceBuildLogs/Logs.tsx b/site/src/components/WorkspaceBuildLogs/Logs.tsx index 76dd480202570..4812a19346b9b 100644 --- a/site/src/components/WorkspaceBuildLogs/Logs.tsx +++ b/site/src/components/WorkspaceBuildLogs/Logs.tsx @@ -56,7 +56,9 @@ export const LogLine: FC<{ hideTimestamp?: boolean; number?: number; style?: React.CSSProperties; -}> = ({ line, hideTimestamp, number, style }) => { + sourceIcon?: JSX.Element; + maxNumber?: number; +}> = ({ line, hideTimestamp, number, maxNumber, sourceIcon, style }) => { const styles = useStyles(); const output = useMemo(() => { return convert.toHtml(line.output.split(/\r/g).pop() as string); @@ -64,9 +66,12 @@ export const LogLine: FC<{ return (
+ {sourceIcon} {!hideTimestamp && ( <> - + {number ? number : dayjs(line.time).format(`HH:mm:ss.SSS`)} @@ -100,6 +105,7 @@ const useStyles = makeStyles((theme) => ({ line: { wordBreak: "break-all", display: "flex", + alignItems: "center", fontSize: 14, color: theme.palette.text.primary, fontFamily: MONOSPACE_FONT_FAMILY, @@ -128,7 +134,6 @@ const useStyles = makeStyles((theme) => ({ }, time: { userSelect: "none", - width: theme.spacing(12.5), whiteSpace: "pre", display: "inline-block", color: theme.palette.text.secondary, diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index a5803cd9b7c21..8eddfebd81de6 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -550,7 +550,7 @@ export const MockWorkspaceAgentScript: TypesGen.WorkspaceAgentScript = { log_path: "", run_on_start: true, run_on_stop: false, - source: "echo 'hello world'", + script: "echo 'hello world'", start_blocks_login: false, timeout_seconds: 0, }; From 75388f701fb0804e3be45950f877eedefd48c2b3 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 13 Sep 2023 18:49:54 +0000 Subject: [PATCH 20/59] Fix schema jank --- agent/agent_test.go | 36 ++++++------- agent/agentscripts/agentscripts.go | 9 ++-- agent/agentscripts/agentscripts_test.go | 4 +- coderd/database/dbfake/dbfake.go | 8 +-- coderd/database/dbgen/dbgen.go | 2 +- coderd/database/dump.sql | 8 ++- .../000155_workspace_agent_script.up.sql | 6 +-- coderd/database/models.go | 2 +- coderd/database/queries.sql.go | 50 +++++++++---------- coderd/database/queries/workspaceagents.sql | 4 +- coderd/database/queries/workspacescripts.sql | 6 +-- coderd/database/unique_constraint.go | 1 + .../provisionerdserver/provisionerdserver.go | 8 ++- coderd/workspaceagents.go | 7 +-- codersdk/workspaceagents.go | 2 +- provisioner/terraform/resources_test.go | 6 ++- 16 files changed, 82 insertions(+), 77 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index 96cc239e01697..05a62cade73fb 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1209,9 +1209,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - Timeout: time.Nanosecond, - RunOnStart: true, + Script: "sleep 3", + TimeoutSeconds: time.Nanosecond, + RunOnStart: true, }}, }, 0) @@ -1234,9 +1234,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "false", - Timeout: 30 * time.Second, - RunOnStart: true, + Script: "false", + TimeoutSeconds: 30 * time.Second, + RunOnStart: true, }}, }, 0) @@ -1259,9 +1259,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "true", - Timeout: 30 * time.Second, - RunOnStart: true, + Script: "true", + TimeoutSeconds: 30 * time.Second, + RunOnStart: true, }}, }, 0) @@ -1284,9 +1284,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - Timeout: 30 * time.Second, - RunOnStop: true, + Script: "sleep 3", + TimeoutSeconds: 30 * time.Second, + RunOnStop: true, }}, }, 0) @@ -1325,9 +1325,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - Timeout: time.Nanosecond, - RunOnStop: true, + Script: "sleep 3", + TimeoutSeconds: time.Nanosecond, + RunOnStop: true, }}, }, 0) @@ -1367,9 +1367,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "false", - Timeout: 30 * time.Second, - RunOnStop: true, + Script: "false", + TimeoutSeconds: 30 * time.Second, + RunOnStop: true, }}, }, 0) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 7ef609466c6c2..5d90e9bd0842e 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -141,10 +141,10 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { }() var cmd *exec.Cmd - if script.Timeout > 0 { + if script.TimeoutSeconds > 0 { var cancel context.CancelFunc // Add a buffer to forcefully kill with the context. - ctx, cancel = context.WithTimeout(ctx, script.Timeout+(3*time.Second)) + ctx, cancel = context.WithTimeout(ctx, script.TimeoutSeconds+(3*time.Second)) defer cancel() } @@ -196,9 +196,9 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { // timeout stores whether the process timed out then was gracefully killed. var timeout chan struct{} - if script.Timeout > 0 { + if script.TimeoutSeconds > 0 { timeout = make(chan struct{}) - timer := time.AfterFunc(script.Timeout, func() { + timer := time.AfterFunc(script.TimeoutSeconds, func() { close(timeout) err := cmd.Process.Signal(os.Interrupt) if err != nil { @@ -232,6 +232,7 @@ func (r *Runner) Close() error { return nil } close(r.closed) + r.cron.Stop() r.cmdCloseWait.Wait() return nil } diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index af3b734839285..060486cc43684 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -46,8 +46,8 @@ func TestTimeout(t *testing.T) { runner := setup(t, nil) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - Timeout: time.Nanosecond, + Script: "sleep 3", + TimeoutSeconds: time.Nanosecond, }}) require.NoError(t, err) require.ErrorIs(t, runner.Execute(nil), agentscripts.ErrTimeout) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 288312aded8c9..b0f487168daa6 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -4557,7 +4557,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentLogSources(_ context.Context, arg data logSource := database.WorkspaceAgentLogSource{ ID: source, WorkspaceAgentID: arg.WorkspaceAgentID, - CreatedAt: arg.CreatedAt[index], + CreatedAt: arg.CreatedAt, DisplayName: arg.DisplayName[index], Icon: arg.Icon[index], } @@ -4586,7 +4586,7 @@ func (q *FakeQuerier) InsertWorkspaceAgentLogs(_ context.Context, arg database.I logs = append(logs, database.WorkspaceAgentLog{ ID: id, AgentID: arg.AgentID, - CreatedAt: arg.CreatedAt[index], + CreatedAt: arg.CreatedAt, Level: arg.Level[index], LogSourceID: arg.LogSourceID, Output: output, @@ -4650,8 +4650,8 @@ func (q *FakeQuerier) InsertWorkspaceAgentScripts(_ context.Context, arg databas StartBlocksLogin: arg.StartBlocksLogin[index], RunOnStart: arg.RunOnStart[index], RunOnStop: arg.RunOnStop[index], - Timeout: arg.Timeout[index], - CreatedAt: arg.CreatedAt[index], + TimeoutSeconds: arg.TimeoutSeconds[index], + CreatedAt: arg.CreatedAt, } scripts = append(scripts, script) } diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index c9c11e75a8655..7103a4f474dec 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -178,7 +178,7 @@ func WorkspaceAgentLogSource(t testing.TB, db database.Store, orig database.Work sources, err := db.InsertWorkspaceAgentLogSources(genCtx, database.InsertWorkspaceAgentLogSourcesParams{ WorkspaceAgentID: takeFirst(orig.WorkspaceAgentID, uuid.New()), ID: []uuid.UUID{takeFirst(orig.ID, uuid.New())}, - CreatedAt: []time.Time{takeFirst(orig.CreatedAt, dbtime.Now())}, + CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), DisplayName: []string{takeFirst(orig.DisplayName, namesgenerator.GetRandomName(1))}, Icon: []string{takeFirst(orig.Icon, namesgenerator.GetRandomName(1))}, }) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 5cbd809090670..511cfb36615e0 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -769,7 +769,7 @@ CREATE TABLE workspace_agent_scripts ( start_blocks_login boolean NOT NULL, run_on_start boolean NOT NULL, run_on_stop boolean NOT NULL, - timeout integer NOT NULL + timeout_seconds integer NOT NULL ); CREATE SEQUENCE workspace_agent_startup_logs_id_seq @@ -1170,6 +1170,9 @@ ALTER TABLE ONLY user_links ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); +ALTER TABLE ONLY workspace_agent_log_sources + ADD CONSTRAINT workspace_agent_log_sources_id_key UNIQUE (id); + ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); @@ -1367,6 +1370,9 @@ ALTER TABLE ONLY user_links ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspace_agent_scripts + ADD CONSTRAINT workspace_agent_scripts_log_source_id_fkey FOREIGN KEY (log_source_id) REFERENCES workspace_agent_log_sources(id) ON DELETE CASCADE; + ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; diff --git a/coderd/database/migrations/000155_workspace_agent_script.up.sql b/coderd/database/migrations/000155_workspace_agent_script.up.sql index 5673e5588e1f9..2b617f51d1102 100644 --- a/coderd/database/migrations/000155_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000155_workspace_agent_script.up.sql @@ -1,7 +1,7 @@ BEGIN; CREATE TABLE workspace_agent_log_sources ( workspace_agent_id uuid NOT NULL, - id uuid NOT NULL, + id uuid NOT NULL UNIQUE, created_at timestamptz NOT NULL, display_name varchar(127) NOT NULL, icon text NOT NULL, @@ -10,7 +10,7 @@ CREATE TABLE workspace_agent_log_sources ( CREATE TABLE workspace_agent_scripts ( workspace_agent_id uuid NOT NULL, - log_source_id uuid NOT NULL, + log_source_id uuid NOT NULL REFERENCES workspace_agent_log_sources(id) ON DELETE CASCADE, log_path text NOT NULL, created_at timestamptz NOT NULL, script text NOT NULL, @@ -18,7 +18,7 @@ CREATE TABLE workspace_agent_scripts ( start_blocks_login boolean NOT NULL, run_on_start boolean NOT NULL, run_on_stop boolean NOT NULL, - timeout integer NOT NULL + timeout_seconds integer NOT NULL ); ALTER TABLE workspace_agent_logs ADD COLUMN log_source_id uuid NOT NULL; diff --git a/coderd/database/models.go b/coderd/database/models.go index 08a87774cc7db..6aa68b56c6c6e 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -2009,7 +2009,7 @@ type WorkspaceAgentScript struct { StartBlocksLogin bool `db:"start_blocks_login" json:"start_blocks_login"` RunOnStart bool `db:"run_on_start" json:"run_on_start"` RunOnStop bool `db:"run_on_stop" json:"run_on_stop"` - Timeout int32 `db:"timeout" json:"timeout"` + TimeoutSeconds int32 `db:"timeout_seconds" json:"timeout_seconds"` } type WorkspaceAgentStat struct { diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index e435a34d166a1..1bd5acc95bfc1 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -7188,8 +7188,8 @@ INSERT INTO workspace_agent_log_sources (workspace_agent_id, id, created_at, display_name, icon) SELECT $1 :: uuid AS workspace_agent_id, - unnest($2 :: uuid [ ]) AS id, - unnest($3 :: timestamptz [ ]) AS created_at, + $2 :: timestamptz AS created_at, + unnest($3 :: uuid [ ]) AS id, unnest($4 :: VARCHAR(127) [ ]) AS display_name, unnest($5 :: text [ ]) AS icon RETURNING workspace_agent_log_sources.workspace_agent_id, workspace_agent_log_sources.id, workspace_agent_log_sources.created_at, workspace_agent_log_sources.display_name, workspace_agent_log_sources.icon @@ -7197,8 +7197,8 @@ INSERT INTO type InsertWorkspaceAgentLogSourcesParams struct { WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` ID []uuid.UUID `db:"id" json:"id"` - CreatedAt []time.Time `db:"created_at" json:"created_at"` DisplayName []string `db:"display_name" json:"display_name"` Icon []string `db:"icon" json:"icon"` } @@ -7206,8 +7206,8 @@ type InsertWorkspaceAgentLogSourcesParams struct { func (q *sqlQuerier) InsertWorkspaceAgentLogSources(ctx context.Context, arg InsertWorkspaceAgentLogSourcesParams) ([]WorkspaceAgentLogSource, error) { rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentLogSources, arg.WorkspaceAgentID, + arg.CreatedAt, pq.Array(arg.ID), - pq.Array(arg.CreatedAt), pq.Array(arg.DisplayName), pq.Array(arg.Icon), ) @@ -7247,7 +7247,7 @@ INSERT INTO workspace_agent_logs (agent_id, created_at, output, level, log_source_id) SELECT $1 :: uuid AS agent_id, - unnest($2 :: timestamptz [ ]) AS created_at, + $2 :: timestamptz AS created_at, unnest($3 :: VARCHAR(1024) [ ]) AS output, unnest($4 :: log_level [ ]) AS level, $5 :: uuid AS log_source_id @@ -7255,18 +7255,18 @@ INSERT INTO ` type InsertWorkspaceAgentLogsParams struct { - AgentID uuid.UUID `db:"agent_id" json:"agent_id"` - CreatedAt []time.Time `db:"created_at" json:"created_at"` - Output []string `db:"output" json:"output"` - Level []LogLevel `db:"level" json:"level"` - LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` - OutputLength int32 `db:"output_length" json:"output_length"` + AgentID uuid.UUID `db:"agent_id" json:"agent_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + Output []string `db:"output" json:"output"` + Level []LogLevel `db:"level" json:"level"` + LogSourceID uuid.UUID `db:"log_source_id" json:"log_source_id"` + OutputLength int32 `db:"output_length" json:"output_length"` } func (q *sqlQuerier) InsertWorkspaceAgentLogs(ctx context.Context, arg InsertWorkspaceAgentLogsParams) ([]WorkspaceAgentLog, error) { rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentLogs, arg.AgentID, - pq.Array(arg.CreatedAt), + arg.CreatedAt, pq.Array(arg.Output), pq.Array(arg.Level), arg.LogSourceID, @@ -9250,7 +9250,7 @@ func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg In } const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many -SELECT workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +SELECT workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) ` func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { @@ -9272,7 +9272,7 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids &i.StartBlocksLogin, &i.RunOnStart, &i.RunOnStop, - &i.Timeout, + &i.TimeoutSeconds, ); err != nil { return nil, err } @@ -9289,46 +9289,46 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) SELECT $1 :: uuid AS workspace_agent_id, - unnest($2 :: uuid [ ]) AS log_source_id, - unnest($3 :: text [ ]) AS log_path, - unnest($4 :: timestamptz [ ]) AS created_at, + $2 :: timestamptz AS created_at, + unnest($3 :: uuid [ ]) AS log_source_id, + unnest($4 :: text [ ]) AS log_path, unnest($5 :: text [ ]) AS script, unnest($6 :: text [ ]) AS cron, unnest($7 :: boolean [ ]) AS start_blocks_login, unnest($8 :: boolean [ ]) AS run_on_start, unnest($9 :: boolean [ ]) AS run_on_stop, - unnest($10 :: integer [ ]) AS timeout -RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.script, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout + unnest($10 :: integer [ ]) AS timeout_seconds +RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.script, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout_seconds ` type InsertWorkspaceAgentScriptsParams struct { WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` LogPath []string `db:"log_path" json:"log_path"` - CreatedAt []time.Time `db:"created_at" json:"created_at"` Script []string `db:"script" json:"script"` Cron []string `db:"cron" json:"cron"` StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` RunOnStart []bool `db:"run_on_start" json:"run_on_start"` RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"` - Timeout []int32 `db:"timeout" json:"timeout"` + TimeoutSeconds []int32 `db:"timeout_seconds" json:"timeout_seconds"` } func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) { rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentScripts, arg.WorkspaceAgentID, + arg.CreatedAt, pq.Array(arg.LogSourceID), pq.Array(arg.LogPath), - pq.Array(arg.CreatedAt), pq.Array(arg.Script), pq.Array(arg.Cron), pq.Array(arg.StartBlocksLogin), pq.Array(arg.RunOnStart), pq.Array(arg.RunOnStop), - pq.Array(arg.Timeout), + pq.Array(arg.TimeoutSeconds), ) if err != nil { return nil, err @@ -9347,7 +9347,7 @@ func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg Insert &i.StartBlocksLogin, &i.RunOnStart, &i.RunOnStop, - &i.Timeout, + &i.TimeoutSeconds, ); err != nil { return nil, err } diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index 0680a755fa2b9..ea470befa62f3 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -154,7 +154,7 @@ INSERT INTO workspace_agent_logs (agent_id, created_at, output, level, log_source_id) SELECT @agent_id :: uuid AS agent_id, - unnest(@created_at :: timestamptz [ ]) AS created_at, + @created_at :: timestamptz AS created_at, unnest(@output :: VARCHAR(1024) [ ]) AS output, unnest(@level :: log_level [ ]) AS level, @log_source_id :: uuid AS log_source_id @@ -165,8 +165,8 @@ INSERT INTO workspace_agent_log_sources (workspace_agent_id, id, created_at, display_name, icon) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, + @created_at :: timestamptz AS created_at, unnest(@id :: uuid [ ]) AS id, - unnest(@created_at :: timestamptz [ ]) AS created_at, unnest(@display_name :: VARCHAR(127) [ ]) AS display_name, unnest(@icon :: text [ ]) AS icon RETURNING workspace_agent_log_sources.*; diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql index bdfae1c5be66b..e0f0bf3511bad 100644 --- a/coderd/database/queries/workspacescripts.sql +++ b/coderd/database/queries/workspacescripts.sql @@ -1,17 +1,17 @@ -- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout) + workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, + @created_at :: timestamptz AS created_at, unnest(@log_source_id :: uuid [ ]) AS log_source_id, unnest(@log_path :: text [ ]) AS log_path, - unnest(@created_at :: timestamptz [ ]) AS created_at, unnest(@script :: text [ ]) AS script, unnest(@cron :: text [ ]) AS cron, unnest(@start_blocks_login :: boolean [ ]) AS start_blocks_login, unnest(@run_on_start :: boolean [ ]) AS run_on_start, unnest(@run_on_stop :: boolean [ ]) AS run_on_stop, - unnest(@timeout :: integer [ ]) AS timeout + unnest(@timeout_seconds :: integer [ ]) AS timeout_seconds RETURNING workspace_agent_scripts.*; -- name: GetWorkspaceAgentScriptsByAgentIDs :many diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index ea0a9a64d3137..067a6e0dc7478 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -20,6 +20,7 @@ const ( UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); UniqueTemplateVersionVariablesTemplateVersionIDNameKey UniqueConstraint = "template_version_variables_template_version_id_name_key" // ALTER TABLE ONLY template_version_variables ADD CONSTRAINT template_version_variables_template_version_id_name_key UNIQUE (template_version_id, name); UniqueTemplateVersionsTemplateIDNameKey UniqueConstraint = "template_versions_template_id_name_key" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_template_id_name_key UNIQUE (template_id, name); + UniqueWorkspaceAgentLogSourcesIDKey UniqueConstraint = "workspace_agent_log_sources_id_key" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_id_key UNIQUE (id); UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id); UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug); UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name); diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index c5b41dd6799bd..1a1a4f813c4bd 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1282,7 +1282,6 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. } logSourceIDs := make([]uuid.UUID, 0, len(prAgent.Scripts)) - logSourceCreatedAt := make([]time.Time, 0, len(prAgent.Scripts)) logSourceDisplayNames := make([]string, 0, len(prAgent.Scripts)) logSourceIcons := make([]string, 0, len(prAgent.Scripts)) scriptLogPaths := make([]string, 0, len(prAgent.Scripts)) @@ -1295,7 +1294,6 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. for _, script := range prAgent.Scripts { logSourceIDs = append(logSourceIDs, uuid.New()) - logSourceCreatedAt = append(logSourceCreatedAt, dbtime.Now()) logSourceDisplayNames = append(logSourceDisplayNames, script.DisplayName) logSourceIcons = append(logSourceIcons, script.Icon) scriptLogPaths = append(scriptLogPaths, script.LogPath) @@ -1310,7 +1308,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. _, err = db.InsertWorkspaceAgentLogSources(ctx, database.InsertWorkspaceAgentLogSourcesParams{ WorkspaceAgentID: agentID, ID: logSourceIDs, - CreatedAt: logSourceCreatedAt, + CreatedAt: dbtime.Now(), DisplayName: logSourceDisplayNames, Icon: logSourceIcons, }) @@ -1322,10 +1320,10 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. WorkspaceAgentID: agentID, LogSourceID: logSourceIDs, LogPath: scriptLogPaths, - CreatedAt: logSourceCreatedAt, + CreatedAt: dbtime.Now(), Script: scriptSources, Cron: scriptCron, - Timeout: scriptTimeout, + TimeoutSeconds: scriptTimeout, StartBlocksLogin: scriptStartBlocksLogin, RunOnStart: scriptRunOnStart, RunOnStop: scriptRunOnStop, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 285dd15aff0b9..07093dac50666 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -299,12 +299,10 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) }) return } - createdAt := make([]time.Time, 0) output := make([]string, 0) level := make([]database.LogLevel, 0) outputLength := 0 for _, logEntry := range req.Logs { - createdAt = append(createdAt, logEntry.CreatedAt) output = append(output, logEntry.Output) outputLength += len(logEntry.Output) if logEntry.Level == "" { @@ -324,7 +322,7 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) logs, err := api.Database.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ AgentID: workspaceAgent.ID, - CreatedAt: createdAt, + CreatedAt: dbtime.Now(), Output: output, Level: level, LogSourceID: req.LogSourceID, @@ -1327,8 +1325,7 @@ func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.Worksp RunOnStart: dbScript.RunOnStart, RunOnStop: dbScript.RunOnStop, StartBlocksLogin: dbScript.StartBlocksLogin, - // In the database it's stored as seconds! - Timeout: time.Duration(dbScript.Timeout) * time.Second, + TimeoutSeconds: time.Duration(dbScript.TimeoutSeconds) * time.Second, }) } return scripts diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index a81fe14a0a435..cbc1abf263185 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -193,7 +193,7 @@ type WorkspaceAgentScript struct { RunOnStart bool `json:"run_on_start"` RunOnStop bool `json:"run_on_stop"` StartBlocksLogin bool `json:"start_blocks_login"` - Timeout time.Duration `json:"timeout_seconds"` + TimeoutSeconds time.Duration `json:"timeout_seconds"` } type WorkspaceAgentHealth struct { diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index 4bbd001aa6ca9..d577446fde08e 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -134,7 +134,8 @@ func TestConvertResources(t *testing.T) { Scripts: []*proto.Script{{ DisplayName: "Shutdown Script", RunOnStop: true, - Source: "echo bye bye", + LogPath: "coder-shutdown-script.log", + Script: "echo bye bye", Timeout: 30, }}, }, { @@ -305,7 +306,8 @@ func TestConvertResources(t *testing.T) { Scripts: []*proto.Script{{ DisplayName: "Startup Script", RunOnStart: true, - Source: " #!/bin/bash\n # home folder can be empty, so copying default bash settings\n if [ ! -f ~/.profile ]; then\n cp /etc/skel/.profile $HOME\n fi\n if [ ! -f ~/.bashrc ]; then\n cp /etc/skel/.bashrc $HOME\n fi\n # install and start code-server\n curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log\n code-server --auth none --port 13337 | tee code-server-install.log &\n", + LogPath: "coder-startup-script.log", + Script: " #!/bin/bash\n # home folder can be empty, so copying default bash settings\n if [ ! -f ~/.profile ]; then\n cp /etc/skel/.profile $HOME\n fi\n if [ ! -f ~/.bashrc ]; then\n cp /etc/skel/.bashrc $HOME\n fi\n # install and start code-server\n curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log\n code-server --auth none --port 13337 | tee code-server-install.log &\n", }}, }}, }, From 8810326e819c70290301c123bc1de0c0cd0d1dbe Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 13 Sep 2023 18:53:19 +0000 Subject: [PATCH 21/59] Uncomment test --- agent/agentscripts/agentscripts.go | 4 +- coderd/database/querier_test.go | 88 ++++++++++---------- coderd/database/queries.sql.go | 4 +- coderd/database/queries/workspaceagents.sql | 2 +- coderd/database/queries/workspacescripts.sql | 2 +- coderd/workspaceagents.go | 11 ++- codersdk/workspaceagents.go | 2 +- 7 files changed, 61 insertions(+), 52 deletions(-) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 5d90e9bd0842e..ddfa35eaa485a 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -65,11 +65,11 @@ func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { r.scripts = scripts for _, script := range scripts { - if script.CRON == "" { + if script.Cron == "" { continue } script := script - _, err := r.cron.AddFunc(script.CRON, func() { + _, err := r.cron.AddFunc(script.Cron, func() { err := r.run(script) if err != nil { r.Logger.Warn(r.ctx, "run agent script on schedule", slog.Error(err)) diff --git a/coderd/database/querier_test.go b/coderd/database/querier_test.go index 48f16cf4059b4..1c166f4f41454 100644 --- a/coderd/database/querier_test.go +++ b/coderd/database/querier_test.go @@ -92,50 +92,50 @@ func TestGetDeploymentWorkspaceAgentStats(t *testing.T) { }) } -// func TestInsertWorkspaceAgentLogs(t *testing.T) { -// t.Parallel() -// if testing.Short() { -// t.SkipNow() -// } -// sqlDB := testSQLDB(t) -// ctx := context.Background() -// err := migrations.Up(sqlDB) -// require.NoError(t, err) -// db := database.New(sqlDB) -// org := dbgen.Organization(t, db, database.Organization{}) -// job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{ -// OrganizationID: org.ID, -// }) -// resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ -// JobID: job.ID, -// }) -// agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ -// ResourceID: resource.ID, -// }) -// source := dbgen.WorkspaceAgentLogSource(t, db, database.WorkspaceAgentLogSource{}) -// logs, err := db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ -// AgentID: agent.ID, -// CreatedAt: []time.Time{dbtime.Now()}, -// Output: []string{"first"}, -// Level: []database.LogLevel{database.LogLevelInfo}, -// LogSourceID: uuid.New(), -// Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal}, -// // 1 MB is the max -// OutputLength: 1 << 20, -// }) -// require.NoError(t, err) -// require.Equal(t, int64(1), logs[0].ID) - -// _, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ -// AgentID: agent.ID, -// CreatedAt: []time.Time{dbtime.Now()}, -// Output: []string{"second"}, -// Level: []database.LogLevel{database.LogLevelInfo}, -// Source: []database.WorkspaceAgentLogSource{database.WorkspaceAgentLogSourceExternal}, -// OutputLength: 1, -// }) -// require.True(t, database.IsWorkspaceAgentLogsLimitError(err)) -// } +func TestInsertWorkspaceAgentLogs(t *testing.T) { + t.Parallel() + if testing.Short() { + t.SkipNow() + } + sqlDB := testSQLDB(t) + ctx := context.Background() + err := migrations.Up(sqlDB) + require.NoError(t, err) + db := database.New(sqlDB) + org := dbgen.Organization(t, db, database.Organization{}) + job := dbgen.ProvisionerJob(t, db, database.ProvisionerJob{ + OrganizationID: org.ID, + }) + resource := dbgen.WorkspaceResource(t, db, database.WorkspaceResource{ + JobID: job.ID, + }) + agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ + ResourceID: resource.ID, + }) + source := dbgen.WorkspaceAgentLogSource(t, db, database.WorkspaceAgentLogSource{}) + + logs, err := db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ + AgentID: agent.ID, + CreatedAt: dbtime.Now(), + Output: []string{"first"}, + Level: []database.LogLevel{database.LogLevelInfo}, + LogSourceID: source.ID, + // 1 MB is the max + OutputLength: 1 << 20, + }) + require.NoError(t, err) + require.Equal(t, int64(1), logs[0].ID) + + _, err = db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ + AgentID: agent.ID, + CreatedAt: dbtime.Now(), + Output: []string{"second"}, + Level: []database.LogLevel{database.LogLevelInfo}, + LogSourceID: source.ID, + OutputLength: 1, + }) + require.True(t, database.IsWorkspaceAgentLogsLimitError(err)) +} func TestProxyByHostname(t *testing.T) { t.Parallel() diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 1bd5acc95bfc1..ef068b4e5ec60 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -7185,7 +7185,7 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa const insertWorkspaceAgentLogSources = `-- name: InsertWorkspaceAgentLogSources :many INSERT INTO - workspace_agent_log_sources (workspace_agent_id, id, created_at, display_name, icon) + workspace_agent_log_sources (workspace_agent_id, created_at, id, display_name, icon) SELECT $1 :: uuid AS workspace_agent_id, $2 :: timestamptz AS created_at, @@ -9289,7 +9289,7 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) + workspace_agent_scripts (workspace_agent_id, created_at, log_source_id, log_path, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) SELECT $1 :: uuid AS workspace_agent_id, $2 :: timestamptz AS created_at, diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index ea470befa62f3..0e9ec08152a69 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -162,7 +162,7 @@ INSERT INTO -- name: InsertWorkspaceAgentLogSources :many INSERT INTO - workspace_agent_log_sources (workspace_agent_id, id, created_at, display_name, icon) + workspace_agent_log_sources (workspace_agent_id, created_at, id, display_name, icon) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, @created_at :: timestamptz AS created_at, diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql index e0f0bf3511bad..8dc234afd37d3 100644 --- a/coderd/database/queries/workspacescripts.sql +++ b/coderd/database/queries/workspacescripts.sql @@ -1,6 +1,6 @@ -- name: InsertWorkspaceAgentScripts :many INSERT INTO - workspace_agent_scripts (workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) + workspace_agent_scripts (workspace_agent_id, created_at, log_source_id, log_path, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) SELECT @workspace_agent_id :: uuid AS workspace_agent_id, @created_at :: timestamptz AS created_at, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 07093dac50666..e0a939999cc84 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -75,6 +75,15 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { logSources, err = api.Database.GetWorkspaceAgentLogSourcesByAgentIDs(ctx, []uuid.UUID{workspaceAgent.ID}) return err }) + err := eg.Wait() + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Internal error fetching workspace agent.", + Detail: err.Error(), + }) + return + } + apiAgent, err := convertWorkspaceAgent( api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, convertApps(dbApps), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout, api.DeploymentValues.AgentFallbackTroubleshootingURL.String(), @@ -1321,7 +1330,7 @@ func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.Worksp LogPath: dbScript.LogPath, LogSourceID: dbScript.LogSourceID, Script: dbScript.Script, - CRON: dbScript.Cron, + Cron: dbScript.Cron, RunOnStart: dbScript.RunOnStart, RunOnStop: dbScript.RunOnStop, StartBlocksLogin: dbScript.StartBlocksLogin, diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index cbc1abf263185..ce05ceb123792 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -189,7 +189,7 @@ type WorkspaceAgentScript struct { LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` LogPath string `json:"log_path"` Script string `json:"script"` - CRON string `json:"cron"` + Cron string `json:"cron"` RunOnStart bool `json:"run_on_start"` RunOnStop bool `json:"run_on_stop"` StartBlocksLogin bool `json:"start_blocks_login"` From 45b395e39bed841215aed3914d76f89c52ba80c1 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Wed, 13 Sep 2023 18:56:17 +0000 Subject: [PATCH 22/59] Rename proto to TimeoutSeconds --- .../provisionerdserver/provisionerdserver.go | 2 +- provisioner/terraform/resources.go | 4 +- provisionersdk/proto/provisioner.pb.go | 417 +++++++++--------- provisionersdk/proto/provisioner.proto | 2 +- 4 files changed, 212 insertions(+), 213 deletions(-) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 1a1a4f813c4bd..12786dcb5b02c 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -1299,7 +1299,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. scriptLogPaths = append(scriptLogPaths, script.LogPath) scriptSources = append(scriptSources, script.Script) scriptCron = append(scriptCron, script.Cron) - scriptTimeout = append(scriptTimeout, script.Timeout) + scriptTimeout = append(scriptTimeout, script.TimeoutSeconds) scriptStartBlocksLogin = append(scriptStartBlocksLogin, script.StartBlocksLogin) scriptRunOnStart = append(scriptRunOnStart, script.RunOnStart) scriptRunOnStop = append(scriptRunOnStop, script.RunOnStop) diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 45953f90151cf..71e5fa85d2cb4 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -238,7 +238,6 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error DisplayName: "Startup Script", Script: attrs.StartupScript, StartBlocksLogin: startupScriptBehavior == string(codersdk.WorkspaceAgentStartupScriptBehaviorBlocking), - Timeout: attrs.StartupScriptTimeoutSeconds, RunOnStart: true, }) } @@ -247,7 +246,6 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error LogPath: "coder-shutdown-script.log", DisplayName: "Shutdown Script", Script: attrs.ShutdownScript, - Timeout: attrs.ShutdownScriptTimeoutSeconds, RunOnStop: true, }) } @@ -456,7 +454,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error StartBlocksLogin: attrs.StartBlocksLogin, RunOnStart: attrs.RunOnStart, RunOnStop: attrs.RunOnStop, - Timeout: attrs.TimeoutSeconds, + TimeoutSeconds: attrs.TimeoutSeconds, }) } } diff --git a/provisionersdk/proto/provisioner.pb.go b/provisionersdk/proto/provisioner.pb.go index c88fd200474c0..a09fc0f935403 100644 --- a/provisionersdk/proto/provisioner.pb.go +++ b/provisionersdk/proto/provisioner.pb.go @@ -1103,7 +1103,7 @@ type Script struct { StartBlocksLogin bool `protobuf:"varint,5,opt,name=start_blocks_login,json=startBlocksLogin,proto3" json:"start_blocks_login,omitempty"` RunOnStart bool `protobuf:"varint,6,opt,name=run_on_start,json=runOnStart,proto3" json:"run_on_start,omitempty"` RunOnStop bool `protobuf:"varint,7,opt,name=run_on_stop,json=runOnStop,proto3" json:"run_on_stop,omitempty"` - Timeout int32 `protobuf:"varint,8,opt,name=timeout,proto3" json:"timeout,omitempty"` + TimeoutSeconds int32 `protobuf:"varint,8,opt,name=timeout_seconds,json=timeoutSeconds,proto3" json:"timeout_seconds,omitempty"` LogPath string `protobuf:"bytes,9,opt,name=log_path,json=logPath,proto3" json:"log_path,omitempty"` } @@ -1188,9 +1188,9 @@ func (x *Script) GetRunOnStop() bool { return false } -func (x *Script) GetTimeout() int32 { +func (x *Script) GetTimeoutSeconds() int32 { if x != nil { - return x.Timeout + return x.TimeoutSeconds } return 0 } @@ -2628,7 +2628,7 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x72, 0x12, 0x34, 0x0a, 0x16, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x68, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x70, 0x6f, 0x72, 0x74, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0x90, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x69, + 0x67, 0x48, 0x65, 0x6c, 0x70, 0x65, 0x72, 0x22, 0x9f, 0x02, 0x0a, 0x06, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x02, 0x20, @@ -2642,212 +2642,213 @@ var file_provisionersdk_proto_provisioner_proto_rawDesc = []byte{ 0x61, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x72, 0x75, 0x6e, 0x4f, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x72, 0x75, 0x6e, 0x5f, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x72, 0x75, 0x6e, 0x4f, - 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, - 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x03, 0x41, - 0x70, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, - 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, - 0x73, 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, - 0x62, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, - 0x69, 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, - 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x75, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xf1, 0x02, - 0x0a, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, - 0x2e, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, - 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, - 0x64, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, - 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, - 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, - 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, - 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, - 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, - 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, - 0x75, 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, - 0x6c, 0x22, 0xae, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, - 0x0a, 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, - 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, - 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, - 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, - 0x61, 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x12, 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, - 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x10, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, - 0x64, 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, - 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, - 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, - 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, - 0x4f, 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x41, 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, - 0x65, 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, - 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, - 0x17, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, - 0x63, 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, - 0x65, 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, - 0x0e, 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, - 0x8b, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, - 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, 0xa6, 0x02, - 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, - 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, - 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, - 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, - 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x12, 0x67, 0x69, - 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, - 0x69, 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, - 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x6e, 0x43, - 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, - 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x0c, - 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, + 0x6e, 0x53, 0x74, 0x6f, 0x70, 0x12, 0x27, 0x0a, 0x0f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x12, 0x19, + 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x03, 0x41, 0x70, + 0x70, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x73, + 0x70, 0x6c, 0x61, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x64, + 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x75, 0x62, + 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3a, 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0b, 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, + 0x6e, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x52, 0x0c, 0x73, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, + 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x22, 0x59, 0x0a, 0x0b, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, + 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x6f, 0x6c, 0x64, 0x22, 0xf1, 0x02, 0x0a, + 0x08, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x2a, 0x0a, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x3a, 0x0a, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x69, 0x64, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x68, 0x69, 0x64, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x69, 0x63, 0x6f, + 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, + 0x63, 0x6f, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, + 0x79, 0x43, 0x6f, 0x73, 0x74, 0x1a, 0x69, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x6e, + 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x73, 0x65, + 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x6e, 0x75, + 0x6c, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4e, 0x75, 0x6c, 0x6c, + 0x22, 0xae, 0x04, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, + 0x09, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x53, 0x0a, 0x14, 0x77, 0x6f, + 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x13, 0x77, 0x6f, 0x72, 0x6b, + 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, + 0x25, 0x0a, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, + 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x12, + 0x21, 0x0a, 0x0c, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x12, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, + 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x32, 0x0a, 0x15, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, + 0x6e, 0x65, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x13, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x45, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x21, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x6f, 0x69, 0x64, 0x63, 0x5f, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x4f, + 0x69, 0x64, 0x63, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x41, + 0x0a, 0x1d, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x4f, 0x77, 0x6e, 0x65, 0x72, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x22, 0x8a, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x0a, 0x17, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x61, 0x72, 0x63, 0x68, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x15, 0x74, + 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x41, 0x72, 0x63, + 0x68, 0x69, 0x76, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x70, 0x72, + 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, + 0x76, 0x65, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x22, 0x0e, + 0x0a, 0x0c, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8b, + 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x72, 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x4c, 0x0a, 0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, + 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d, 0x65, 0x22, 0xa6, 0x02, 0x0a, + 0x0b, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, - 0xda, 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, - 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, - 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, - 0x0a, 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, - 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x0f, 0x0a, 0x0d, - 0x43, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x02, - 0x0a, 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, - 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, - 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, 0x05, 0x61, - 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x34, - 0x0a, 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, - 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, - 0x6e, 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd1, 0x01, 0x0a, - 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, - 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, - 0x32, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, - 0x73, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, - 0x72, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, - 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, - 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, - 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x2a, 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, - 0x54, 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, - 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, - 0x57, 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, - 0x04, 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, - 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, - 0x11, 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, - 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, - 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, - 0x12, 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, - 0x53, 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, - 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, - 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, - 0x30, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, - 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, + 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, + 0x13, 0x72, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, + 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, + 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x12, 0x67, 0x69, 0x74, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x47, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x0c, 0x50, 0x6c, 0x61, 0x6e, 0x43, 0x6f, + 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, + 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x41, 0x0a, 0x0c, 0x41, + 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x22, 0xda, + 0x01, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x33, 0x0a, 0x09, + 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, + 0x72, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, + 0x74, 0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x22, 0x0f, 0x0a, 0x0d, 0x43, + 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x8c, 0x02, 0x0a, + 0x07, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, + 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x70, 0x6c, + 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x31, 0x0a, 0x05, 0x61, 0x70, + 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x34, 0x0a, + 0x06, 0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x43, 0x61, 0x6e, 0x63, + 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x61, 0x6e, + 0x63, 0x65, 0x6c, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x08, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x65, 0x72, 0x2e, 0x4c, 0x6f, 0x67, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, 0x32, + 0x0a, 0x05, 0x70, 0x61, 0x72, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x61, 0x72, 0x73, + 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x05, 0x70, 0x61, 0x72, + 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, + 0x6c, 0x61, 0x6e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, 0x52, 0x04, 0x70, + 0x6c, 0x61, 0x6e, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, + 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x48, 0x00, + 0x52, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x79, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x2a, + 0x3f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x54, + 0x52, 0x41, 0x43, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, + 0x01, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x57, + 0x41, 0x52, 0x4e, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, + 0x2a, 0x3b, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x53, 0x68, 0x61, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x65, + 0x76, 0x65, 0x6c, 0x12, 0x09, 0x0a, 0x05, 0x4f, 0x57, 0x4e, 0x45, 0x52, 0x10, 0x00, 0x12, 0x11, + 0x0a, 0x0d, 0x41, 0x55, 0x54, 0x48, 0x45, 0x4e, 0x54, 0x49, 0x43, 0x41, 0x54, 0x45, 0x44, 0x10, + 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x02, 0x2a, 0x37, 0x0a, + 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x00, 0x12, + 0x08, 0x0a, 0x04, 0x53, 0x54, 0x4f, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x53, + 0x54, 0x52, 0x4f, 0x59, 0x10, 0x02, 0x32, 0x49, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, + 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x73, 0x64, 0x6b, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/provisionersdk/proto/provisioner.proto b/provisionersdk/proto/provisioner.proto index 68f0dfb587ef7..8c55227e9468a 100644 --- a/provisionersdk/proto/provisioner.proto +++ b/provisionersdk/proto/provisioner.proto @@ -144,7 +144,7 @@ message Script { bool start_blocks_login = 5; bool run_on_start = 6; bool run_on_stop = 7; - int32 timeout = 8; + int32 timeout_seconds = 8; string log_path = 9; } From 40bcd9d54fe669d4f3e593030794b34fa7c33e8b Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 17 Sep 2023 16:24:52 +0000 Subject: [PATCH 23/59] Fix comments --- agent/agentscripts/agentscripts.go | 11 +++++++++-- codersdk/agentsdk/logs.go | 6 +++--- codersdk/agentsdk/logs_test.go | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index ddfa35eaa485a..93399a3ac0ae9 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -8,6 +8,7 @@ import ( "os/exec" "path/filepath" "sync" + "sync/atomic" "time" "github.com/robfig/cron/v3" @@ -55,6 +56,7 @@ type Runner struct { closeMutex sync.Mutex ctx context.Context cron *cron.Cron + initialized atomic.Bool scripts []codersdk.WorkspaceAgentScript } @@ -62,7 +64,12 @@ type Runner struct { // It also schedules any scripts that have a schedule. // This function must be called before Execute. func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { + if r.initialized.Load() { + return xerrors.New("init: already initialized") + } + r.initialized.Store(true) r.scripts = scripts + r.Logger.Info(r.ctx, "initializing agent scripts", slog.F("script_count", len(scripts)), slog.F("log_dir", r.LogDir)) for _, script := range scripts { if script.Cron == "" { @@ -165,9 +172,9 @@ func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { } }() - infoW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelInfo) + infoW := agentsdk.LogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelInfo) defer infoW.Close() - errW := agentsdk.StartupLogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelError) + errW := agentsdk.LogsWriter(ctx, send, script.LogSourceID, codersdk.LogLevelError) defer errW.Close() cmd.Stdout = io.MultiWriter(fileWriter, infoW) cmd.Stderr = io.MultiWriter(fileWriter, errW) diff --git a/codersdk/agentsdk/logs.go b/codersdk/agentsdk/logs.go index 89b8b90848b75..8f300f4c3ea35 100644 --- a/codersdk/agentsdk/logs.go +++ b/codersdk/agentsdk/logs.go @@ -73,15 +73,15 @@ func (w *startupLogsWriter) Close() error { return nil } -// StartupLogsWriter returns an io.WriteCloser that sends logs via the +// LogsWriter returns an io.WriteCloser that sends logs via the // provided sender. The sender is expected to be non-blocking. Calling // Close flushes any remaining partially written log lines but is -// otherwise no-op. If the context passed to StartupLogsWriter is +// otherwise no-op. If the context passed to LogsWriter is // canceled, any remaining logs will be discarded. // // Neither Write nor Close is safe for concurrent use and must be used // by a single goroutine. -func StartupLogsWriter(ctx context.Context, sender func(ctx context.Context, log ...Log) error, source uuid.UUID, level codersdk.LogLevel) io.WriteCloser { +func LogsWriter(ctx context.Context, sender func(ctx context.Context, log ...Log) error, source uuid.UUID, level codersdk.LogLevel) io.WriteCloser { return &startupLogsWriter{ ctx: ctx, send: sender, diff --git a/codersdk/agentsdk/logs_test.go b/codersdk/agentsdk/logs_test.go index eb2299092efca..90e4ff42107d7 100644 --- a/codersdk/agentsdk/logs_test.go +++ b/codersdk/agentsdk/logs_test.go @@ -184,7 +184,7 @@ func TestStartupLogsWriter_Write(t *testing.T) { got = append(got, log...) return nil } - w := agentsdk.StartupLogsWriter(tt.ctx, send, uuid.New(), tt.level) + w := agentsdk.LogsWriter(tt.ctx, send, uuid.New(), tt.level) for _, s := range tt.writes { _, err := w.Write([]byte(s)) if err != nil { From b744c9f901e763940077a76eeb84946e34e3945f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 17 Sep 2023 17:03:03 +0000 Subject: [PATCH 24/59] Fix comments --- agent/agent.go | 8 ++++---- agent/agentscripts/agentscripts.go | 19 ++++++++----------- agent/agentscripts/agentscripts_test.go | 13 ++++++------- provisioner/terraform/resources_test.go | 10 +++++----- 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index cb95fbc2ff88b..9e6b70f6fc9d6 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -214,7 +214,7 @@ func (a *agent) init(ctx context.Context) { sshSrv.Manifest = &a.manifest sshSrv.ServiceBanner = &a.serviceBanner a.sshServer = sshSrv - a.scriptRunner = agentscripts.New(ctx, agentscripts.Options{ + a.scriptRunner = agentscripts.New(agentscripts.Options{ LogDir: a.logDir, Logger: a.logger, SSHServer: sshSrv, @@ -638,12 +638,12 @@ func (a *agent) run(ctx context.Context) error { } } - err = a.scriptRunner.Init(manifest.Scripts) + err = a.scriptRunner.Init(ctx, manifest.Scripts) if err != nil { return xerrors.Errorf("init script runner: %w", err) } err = a.trackConnGoroutine(func() { - err := a.scriptRunner.Execute(func(script codersdk.WorkspaceAgentScript) bool { + err := a.scriptRunner.Execute(ctx, func(script codersdk.WorkspaceAgentScript) bool { return script.RunOnStart }) if err != nil { @@ -1244,7 +1244,7 @@ func (a *agent) Close() error { } lifecycleState := codersdk.WorkspaceAgentLifecycleOff - err = a.scriptRunner.Execute(func(script codersdk.WorkspaceAgentScript) bool { + err = a.scriptRunner.Execute(ctx, func(script codersdk.WorkspaceAgentScript) bool { return script.RunOnStop }) if err != nil { diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 93399a3ac0ae9..6cc05f8749d69 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -39,11 +39,10 @@ type Options struct { } // New creates a runner for the provided scripts. -func New(ctx context.Context, opts Options) *Runner { +func New(opts Options) *Runner { return &Runner{ Options: opts, cron: cron.New(cron.WithParser(parser)), - ctx: ctx, closed: make(chan struct{}), } } @@ -54,7 +53,6 @@ type Runner struct { cmdCloseWait sync.WaitGroup closed chan struct{} closeMutex sync.Mutex - ctx context.Context cron *cron.Cron initialized atomic.Bool scripts []codersdk.WorkspaceAgentScript @@ -63,13 +61,13 @@ type Runner struct { // Init initializes the runner with the provided scripts. // It also schedules any scripts that have a schedule. // This function must be called before Execute. -func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { +func (r *Runner) Init(ctx context.Context, scripts []codersdk.WorkspaceAgentScript) error { if r.initialized.Load() { return xerrors.New("init: already initialized") } r.initialized.Store(true) r.scripts = scripts - r.Logger.Info(r.ctx, "initializing agent scripts", slog.F("script_count", len(scripts)), slog.F("log_dir", r.LogDir)) + r.Logger.Info(ctx, "initializing agent scripts", slog.F("script_count", len(scripts)), slog.F("log_dir", r.LogDir)) for _, script := range scripts { if script.Cron == "" { @@ -77,9 +75,9 @@ func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { } script := script _, err := r.cron.AddFunc(script.Cron, func() { - err := r.run(script) + err := r.run(ctx, script) if err != nil { - r.Logger.Warn(r.ctx, "run agent script on schedule", slog.Error(err)) + r.Logger.Warn(context.Background(), "run agent script on schedule", slog.Error(err)) } }) if err != nil { @@ -96,7 +94,7 @@ func (r *Runner) StartCRON() { } // Execute runs a set of scripts according to a filter. -func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) error { +func (r *Runner) Execute(ctx context.Context, filter func(script codersdk.WorkspaceAgentScript) bool) error { if filter == nil { // Execute em' all! filter = func(script codersdk.WorkspaceAgentScript) bool { @@ -110,7 +108,7 @@ func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) } script := script eg.Go(func() error { - err := r.run(script) + err := r.run(ctx, script) if err != nil { return xerrors.Errorf("run agent script %q: %w", script.LogPath, err) } @@ -124,9 +122,8 @@ func (r *Runner) Execute(filter func(script codersdk.WorkspaceAgentScript) bool) // If the timeout is exceeded, the process is sent an interrupt signal. // If the process does not exit after a few seconds, it is forcefully killed. // This function immediately returns after a timeout, and does not wait for the process to exit. -func (r *Runner) run(script codersdk.WorkspaceAgentScript) error { +func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) error { logger := r.Logger.With(slog.F("log_source", script.LogPath)) - ctx := r.ctx logger.Info(ctx, "running agent script", slog.F("script", script.Script)) logPath := script.LogPath diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index 060486cc43684..11205385164d0 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -30,11 +30,11 @@ func TestExecuteBasic(t *testing.T) { return nil }) defer runner.Close() - err := runner.Init([]codersdk.WorkspaceAgentScript{{ + err := runner.Init(context.Background(), []codersdk.WorkspaceAgentScript{{ Script: "echo hello", }}) require.NoError(t, err) - require.NoError(t, runner.Execute(func(script codersdk.WorkspaceAgentScript) bool { + require.NoError(t, runner.Execute(context.Background(), func(script codersdk.WorkspaceAgentScript) bool { return true })) log := <-logs @@ -45,12 +45,12 @@ func TestTimeout(t *testing.T) { t.Parallel() runner := setup(t, nil) defer runner.Close() - err := runner.Init([]codersdk.WorkspaceAgentScript{{ + err := runner.Init(context.Background(), []codersdk.WorkspaceAgentScript{{ Script: "sleep 3", TimeoutSeconds: time.Nanosecond, }}) require.NoError(t, err) - require.ErrorIs(t, runner.Execute(nil), agentscripts.ErrTimeout) + require.ErrorIs(t, runner.Execute(context.Background(), nil), agentscripts.ErrTimeout) } func setup(t *testing.T, patchLogs func(ctx context.Context, req agentsdk.PatchLogs) error) *agentscripts.Runner { @@ -62,16 +62,15 @@ func setup(t *testing.T, patchLogs func(ctx context.Context, req agentsdk.PatchL } } fs := afero.NewMemMapFs() - ctx := context.Background() logger := slogtest.Make(t, nil) - s, err := agentssh.NewServer(ctx, logger, prometheus.NewRegistry(), fs, 0, "") + s, err := agentssh.NewServer(context.Background(), logger, prometheus.NewRegistry(), fs, 0, "") require.NoError(t, err) s.AgentToken = func() string { return "" } s.Manifest = atomic.NewPointer(&agentsdk.Manifest{}) t.Cleanup(func() { _ = s.Close() }) - return agentscripts.New(ctx, agentscripts.Options{ + return agentscripts.New(agentscripts.Options{ LogDir: t.TempDir(), Logger: logger, SSHServer: s, diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index d577446fde08e..43ad2b922cb6d 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -132,11 +132,11 @@ func TestConvertResources(t *testing.T) { MotdFile: "/etc/motd", DisplayApps: &displayApps, Scripts: []*proto.Script{{ - DisplayName: "Shutdown Script", - RunOnStop: true, - LogPath: "coder-shutdown-script.log", - Script: "echo bye bye", - Timeout: 30, + DisplayName: "Shutdown Script", + RunOnStop: true, + LogPath: "coder-shutdown-script.log", + Script: "echo bye bye", + TimeoutSeconds: 30, }}, }, { Name: "dev3", From c8444629f23254e269adac04a3faec7d7229c0b4 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 14:55:59 +0000 Subject: [PATCH 25/59] Fix legacy endpoint without specified log_source --- coderd/apidoc/docs.go | 3 -- coderd/apidoc/swagger.json | 1 - coderd/database/dump.sql | 5 +-- .../000155_workspace_agent_script.up.sql | 6 ++-- coderd/database/unique_constraint.go | 34 ++++++++++++++++++- coderd/workspaceagents.go | 27 +++++++++++++++ coderd/workspaceagents_test.go | 2 +- codersdk/agentsdk/agentsdk.go | 34 ++++++++++++++++++- docs/api/schemas.md | 2 +- scripts/dbgen/main.go | 2 +- 10 files changed, 100 insertions(+), 16 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index aa4ffe60d7cae..632ef49a9b98a 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -6457,9 +6457,6 @@ const docTemplate = `{ }, "agentsdk.PatchLogs": { "type": "object", - "required": [ - "log_source_id" - ], "properties": { "log_source_id": { "type": "string" diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index f108a55b5e113..f78998453b5b5 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -5697,7 +5697,6 @@ }, "agentsdk.PatchLogs": { "type": "object", - "required": ["log_source_id"], "properties": { "log_source_id": { "type": "string" diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 511cfb36615e0..929a6dc76858e 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -1170,9 +1170,6 @@ ALTER TABLE ONLY user_links ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); -ALTER TABLE ONLY workspace_agent_log_sources - ADD CONSTRAINT workspace_agent_log_sources_id_key UNIQUE (id); - ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); @@ -1371,7 +1368,7 @@ ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ALTER TABLE ONLY workspace_agent_scripts - ADD CONSTRAINT workspace_agent_scripts_log_source_id_fkey FOREIGN KEY (log_source_id) REFERENCES workspace_agent_log_sources(id) ON DELETE CASCADE; + ADD CONSTRAINT workspace_agent_scripts_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; diff --git a/coderd/database/migrations/000155_workspace_agent_script.up.sql b/coderd/database/migrations/000155_workspace_agent_script.up.sql index 2b617f51d1102..660894db100c4 100644 --- a/coderd/database/migrations/000155_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000155_workspace_agent_script.up.sql @@ -1,7 +1,7 @@ BEGIN; CREATE TABLE workspace_agent_log_sources ( workspace_agent_id uuid NOT NULL, - id uuid NOT NULL UNIQUE, + id uuid NOT NULL, created_at timestamptz NOT NULL, display_name varchar(127) NOT NULL, icon text NOT NULL, @@ -9,8 +9,8 @@ CREATE TABLE workspace_agent_log_sources ( ); CREATE TABLE workspace_agent_scripts ( - workspace_agent_id uuid NOT NULL, - log_source_id uuid NOT NULL REFERENCES workspace_agent_log_sources(id) ON DELETE CASCADE, + workspace_agent_id uuid NOT NULL REFERENCES workspace_agents(id) ON DELETE CASCADE, + log_source_id uuid NOT NULL, log_path text NOT NULL, created_at timestamptz NOT NULL, script text NOT NULL, diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index 067a6e0dc7478..3eb5ae84c57eb 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -6,28 +6,60 @@ type UniqueConstraint string // UniqueConstraint enums. const ( + UniqueAgentStatsPkey UniqueConstraint = "agent_stats_pkey" // ALTER TABLE ONLY workspace_agent_stats ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id); + UniqueApiKeysPkey UniqueConstraint = "api_keys_pkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id); + UniqueAuditLogsPkey UniqueConstraint = "audit_logs_pkey" // ALTER TABLE ONLY audit_logs ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id); UniqueDbcryptKeysActiveKeyDigestKey UniqueConstraint = "dbcrypt_keys_active_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_active_key_digest_key UNIQUE (active_key_digest); + UniqueDbcryptKeysPkey UniqueConstraint = "dbcrypt_keys_pkey" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_pkey PRIMARY KEY (number); UniqueDbcryptKeysRevokedKeyDigestKey UniqueConstraint = "dbcrypt_keys_revoked_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_revoked_key_digest_key UNIQUE (revoked_key_digest); UniqueFilesHashCreatedByKey UniqueConstraint = "files_hash_created_by_key" // ALTER TABLE ONLY files ADD CONSTRAINT files_hash_created_by_key UNIQUE (hash, created_by); + UniqueFilesPkey UniqueConstraint = "files_pkey" // ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id); UniqueGitAuthLinksProviderIDUserIDKey UniqueConstraint = "git_auth_links_provider_id_user_id_key" // ALTER TABLE ONLY git_auth_links ADD CONSTRAINT git_auth_links_provider_id_user_id_key UNIQUE (provider_id, user_id); + UniqueGitsshkeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); UniqueGroupMembersUserIDGroupIDKey UniqueConstraint = "group_members_user_id_group_id_key" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_group_id_key UNIQUE (user_id, group_id); UniqueGroupsNameOrganizationIDKey UniqueConstraint = "groups_name_organization_id_key" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_name_organization_id_key UNIQUE (name, organization_id); + UniqueGroupsPkey UniqueConstraint = "groups_pkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_pkey PRIMARY KEY (id); UniqueLicensesJWTKey UniqueConstraint = "licenses_jwt_key" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_jwt_key UNIQUE (jwt); + UniqueLicensesPkey UniqueConstraint = "licenses_pkey" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id); + UniqueOrganizationMembersPkey UniqueConstraint = "organization_members_pkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id); + UniqueOrganizationsPkey UniqueConstraint = "organizations_pkey" // ALTER TABLE ONLY organizations ADD CONSTRAINT organizations_pkey PRIMARY KEY (id); UniqueParameterSchemasJobIDNameKey UniqueConstraint = "parameter_schemas_job_id_name_key" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_name_key UNIQUE (job_id, name); + UniqueParameterSchemasPkey UniqueConstraint = "parameter_schemas_pkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_pkey PRIMARY KEY (id); + UniqueParameterValuesPkey UniqueConstraint = "parameter_values_pkey" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_pkey PRIMARY KEY (id); UniqueParameterValuesScopeIDNameKey UniqueConstraint = "parameter_values_scope_id_name_key" // ALTER TABLE ONLY parameter_values ADD CONSTRAINT parameter_values_scope_id_name_key UNIQUE (scope_id, name); UniqueProvisionerDaemonsNameKey UniqueConstraint = "provisioner_daemons_name_key" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_name_key UNIQUE (name); + UniqueProvisionerDaemonsPkey UniqueConstraint = "provisioner_daemons_pkey" // ALTER TABLE ONLY provisioner_daemons ADD CONSTRAINT provisioner_daemons_pkey PRIMARY KEY (id); + UniqueProvisionerJobLogsPkey UniqueConstraint = "provisioner_job_logs_pkey" // ALTER TABLE ONLY provisioner_job_logs ADD CONSTRAINT provisioner_job_logs_pkey PRIMARY KEY (id); + UniqueProvisionerJobsPkey UniqueConstraint = "provisioner_jobs_pkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id); UniqueSiteConfigsKeyKey UniqueConstraint = "site_configs_key_key" // ALTER TABLE ONLY site_configs ADD CONSTRAINT site_configs_key_key UNIQUE (key); + UniqueTailnetAgentsPkey UniqueConstraint = "tailnet_agents_pkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetClientsPkey UniqueConstraint = "tailnet_clients_pkey" // ALTER TABLE ONLY tailnet_clients ADD CONSTRAINT tailnet_clients_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetCoordinatorsPkey UniqueConstraint = "tailnet_coordinators_pkey" // ALTER TABLE ONLY tailnet_coordinators ADD CONSTRAINT tailnet_coordinators_pkey PRIMARY KEY (id); UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); UniqueTemplateVersionVariablesTemplateVersionIDNameKey UniqueConstraint = "template_version_variables_template_version_id_name_key" // ALTER TABLE ONLY template_version_variables ADD CONSTRAINT template_version_variables_template_version_id_name_key UNIQUE (template_version_id, name); + UniqueTemplateVersionsPkey UniqueConstraint = "template_versions_pkey" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_pkey PRIMARY KEY (id); UniqueTemplateVersionsTemplateIDNameKey UniqueConstraint = "template_versions_template_id_name_key" // ALTER TABLE ONLY template_versions ADD CONSTRAINT template_versions_template_id_name_key UNIQUE (template_id, name); - UniqueWorkspaceAgentLogSourcesIDKey UniqueConstraint = "workspace_agent_log_sources_id_key" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_id_key UNIQUE (id); + UniqueTemplatesPkey UniqueConstraint = "templates_pkey" // ALTER TABLE ONLY templates ADD CONSTRAINT templates_pkey PRIMARY KEY (id); + UniqueUserLinksPkey UniqueConstraint = "user_links_pkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_pkey PRIMARY KEY (user_id, login_type); + UniqueUsersPkey UniqueConstraint = "users_pkey" // ALTER TABLE ONLY users ADD CONSTRAINT users_pkey PRIMARY KEY (id); + UniqueWorkspaceAgentLogSourcesPkey UniqueConstraint = "workspace_agent_log_sources_pkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_pkey PRIMARY KEY (workspace_agent_id, id); + UniqueWorkspaceAgentMetadataPkey UniqueConstraint = "workspace_agent_metadata_pkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_pkey PRIMARY KEY (workspace_agent_id, key); + UniqueWorkspaceAgentStartupLogsPkey UniqueConstraint = "workspace_agent_startup_logs_pkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_pkey PRIMARY KEY (id); + UniqueWorkspaceAgentsPkey UniqueConstraint = "workspace_agents_pkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_pkey PRIMARY KEY (id); + UniqueWorkspaceAppStatsPkey UniqueConstraint = "workspace_app_stats_pkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_pkey PRIMARY KEY (id); UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id); UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug); + UniqueWorkspaceAppsPkey UniqueConstraint = "workspace_apps_pkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id); UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name); UniqueWorkspaceBuildsJobIDKey UniqueConstraint = "workspace_builds_job_id_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id); + UniqueWorkspaceBuildsPkey UniqueConstraint = "workspace_builds_pkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_pkey PRIMARY KEY (id); UniqueWorkspaceBuildsWorkspaceIDBuildNumberKey UniqueConstraint = "workspace_builds_workspace_id_build_number_key" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_workspace_id_build_number_key UNIQUE (workspace_id, build_number); + UniqueWorkspaceProxiesPkey UniqueConstraint = "workspace_proxies_pkey" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_pkey PRIMARY KEY (id); UniqueWorkspaceProxiesRegionIDUnique UniqueConstraint = "workspace_proxies_region_id_unique" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_region_id_unique UNIQUE (region_id); UniqueWorkspaceResourceMetadataName UniqueConstraint = "workspace_resource_metadata_name" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_name UNIQUE (workspace_resource_id, key); + UniqueWorkspaceResourceMetadataPkey UniqueConstraint = "workspace_resource_metadata_pkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_pkey PRIMARY KEY (id); + UniqueWorkspaceResourcesPkey UniqueConstraint = "workspace_resources_pkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id); + UniqueWorkspacesPkey UniqueConstraint = "workspaces_pkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); UniqueIndexApiKeyName UniqueConstraint = "idx_api_key_name" // CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); UniqueIndexOrganizationName UniqueConstraint = "idx_organization_name" // CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name); UniqueIndexOrganizationNameLower UniqueConstraint = "idx_organization_name_lower" // CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name)); diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index e0a939999cc84..6f5b4078768ee 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -308,6 +308,33 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request) }) return } + // This is to support the legacy API where the log source ID was + // not provided in the request body. We default to the external + // log source in this case. + if req.LogSourceID == uuid.Nil { + // Use the external log source + externalSources, err := api.Database.InsertWorkspaceAgentLogSources(ctx, database.InsertWorkspaceAgentLogSourcesParams{ + WorkspaceAgentID: workspaceAgent.ID, + CreatedAt: dbtime.Now(), + ID: []uuid.UUID{agentsdk.ExternalLogSourceID}, + DisplayName: []string{"External"}, + Icon: []string{"/emojis/1f310.png"}, + }) + if database.IsUniqueViolation(err, database.UniqueWorkspaceAgentLogSourcesPkey) { + err = nil + req.LogSourceID = agentsdk.ExternalLogSourceID + } + if err != nil { + httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ + Message: "Failed to create external log source.", + Detail: err.Error(), + }) + return + } + if len(externalSources) == 1 { + req.LogSourceID = externalSources[0].ID + } + } output := make([]string, 0) level := make([]database.LogLevel, 0) outputLength := 0 diff --git a/coderd/workspaceagents_test.go b/coderd/workspaceagents_test.go index d900ead4175cf..80aeb5a47518f 100644 --- a/coderd/workspaceagents_test.go +++ b/coderd/workspaceagents_test.go @@ -268,7 +268,7 @@ func TestWorkspaceAgent(t *testing.T) { }) } -func TestWorkspaceAgentStartupLogs(t *testing.T) { +func TestWorkspaceAgentLogs(t *testing.T) { t.Parallel() t.Run("Success", func(t *testing.T) { t.Parallel() diff --git a/codersdk/agentsdk/agentsdk.go b/codersdk/agentsdk/agentsdk.go index af374c8d1404d..0a2c47f95c049 100644 --- a/codersdk/agentsdk/agentsdk.go +++ b/codersdk/agentsdk/agentsdk.go @@ -23,6 +23,15 @@ import ( "github.com/coder/retry" ) +var ( + // ExternalLogSourceID is the statically-defined ID of a log-source that + // appears as "External" in the dashboard. + // + // This is to support legacy API-consumers that do not create their own + // log-source. This should be removed in the future. + ExternalLogSourceID = uuid.MustParse("3b579bf4-1ed8-4b99-87a8-e9a1e3410410") +) + // New returns a client that is used to interact with the // Coder API from a workspace agent. func New(serverURL *url.URL) *Client { @@ -644,7 +653,7 @@ type Log struct { } type PatchLogs struct { - LogSourceID uuid.UUID `json:"log_source_id" validate:"required"` + LogSourceID uuid.UUID `json:"log_source_id"` Logs []Log `json:"logs"` } @@ -662,6 +671,29 @@ func (c *Client) PatchLogs(ctx context.Context, req PatchLogs) error { return nil } +type PostLogSource struct { + // ID is a unique identifier for the log source. + // It is scoped to a workspace agent, and can be statically + // defined inside code to prevent duplicate sources from being + // created for the same agent. + ID uuid.UUID `json:"id"` + DisplayName string `json:"display_name"` + Icon string `json:"icon"` +} + +func (c *Client) PostLogSource(ctx context.Context, req PostLogSource) (codersdk.WorkspaceAgentLogSource, error) { + res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/log-source", req) + if err != nil { + return codersdk.WorkspaceAgentLogSource{}, err + } + defer res.Body.Close() + if res.StatusCode != http.StatusCreated { + return codersdk.WorkspaceAgentLogSource{}, codersdk.ReadBodyAsError(res) + } + var logSource codersdk.WorkspaceAgentLogSource + return logSource, json.NewDecoder(res.Body).Decode(&logSource) +} + // GetServiceBanner relays the service banner config. func (c *Client) GetServiceBanner(ctx context.Context) (codersdk.ServiceBannerConfig, error) { res, err := c.SDK.Request(ctx, http.MethodGet, "/api/v2/appearance", nil) diff --git a/docs/api/schemas.md b/docs/api/schemas.md index f8a810cbe8c63..eb25f295650bd 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -329,7 +329,7 @@ | Name | Type | Required | Restrictions | Description | | --------------- | ------------------------------------- | -------- | ------------ | ----------- | -| `log_source_id` | string | true | | | +| `log_source_id` | string | false | | | | `logs` | array of [agentsdk.Log](#agentsdklog) | false | | | ## agentsdk.PostAppHealthsRequest diff --git a/scripts/dbgen/main.go b/scripts/dbgen/main.go index dfc4778e30b34..a45d084335a34 100644 --- a/scripts/dbgen/main.go +++ b/scripts/dbgen/main.go @@ -111,7 +111,7 @@ func generateUniqueConstraints() error { case line == "": case strings.HasSuffix(line, ";"): query += line - if strings.Contains(query, "UNIQUE") { + if strings.Contains(query, "UNIQUE") || strings.Contains(query, "PRIMARY KEY") { uniqueConstraints = append(uniqueConstraints, query) } query = "" From a21f0854e64ab591965d58d46939ae48160c1371 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:14:17 +0000 Subject: [PATCH 26/59] Fix non-blocking by default in agent --- coderd/workspaceagents.go | 1 + codersdk/workspaceagents.go | 6 ++++++ provisioner/terraform/resources.go | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index bb06adeda8776..131d9d4d5b011 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1465,6 +1465,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin Architecture: dbAgent.Architecture, OperatingSystem: dbAgent.OperatingSystem, Scripts: scripts, + StartupScriptBehavior: codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking, LogsLength: dbAgent.LogsLength, LogsOverflowed: dbAgent.LogsOverflowed, LogSources: logSources, diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index ce05ceb123792..677e2a6f54b5b 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -101,6 +101,7 @@ var WorkspaceAgentLifecycleOrder = []WorkspaceAgentLifecycle{ // ready (can be overridden). // // Presently, non-blocking is the default, but this may change in the future. +// Deprecated: `coder_script` allows configuration on a per-script basis. type WorkspaceAgentStartupScriptBehavior string const ( @@ -175,6 +176,11 @@ type WorkspaceAgent struct { DisplayApps []DisplayApp `json:"display_apps"` LogSources []WorkspaceAgentLogSource `json:"log_sources"` Scripts []WorkspaceAgentScript `json:"scripts"` + + // StartupScriptBehavior is a legacy field that is deprecated in favor + // of the `coder_script` resource. It's only referenced by old clients. + // Deprecated: Remove in the future! + StartupScriptBehavior WorkspaceAgentStartupScriptBehavior `json:"startup_script_behavior"` } type WorkspaceAgentLogSource struct { diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 71e5fa85d2cb4..f1ca05fef7397 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -234,6 +234,8 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error // Support the legacy script attributes in the agent! if attrs.StartupScript != "" { agent.Scripts = append(agent.Scripts, &proto.Script{ + // This is ▶️ + Icon: "/emojis/25b6.png", LogPath: "coder-startup-script.log", DisplayName: "Startup Script", Script: attrs.StartupScript, @@ -243,6 +245,8 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error } if attrs.ShutdownScript != "" { agent.Scripts = append(agent.Scripts, &proto.Script{ + // This is ◀️ + Icon: "/emojis/25c0.png", LogPath: "coder-shutdown-script.log", DisplayName: "Shutdown Script", Script: attrs.ShutdownScript, From 49808b12e5933e1d890aff025c214b67409bbc6b Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:18:32 +0000 Subject: [PATCH 27/59] Fix resources tests --- provisioner/terraform/resources_test.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/provisioner/terraform/resources_test.go b/provisioner/terraform/resources_test.go index 43ad2b922cb6d..cb3c4237c1f80 100644 --- a/provisioner/terraform/resources_test.go +++ b/provisioner/terraform/resources_test.go @@ -132,11 +132,11 @@ func TestConvertResources(t *testing.T) { MotdFile: "/etc/motd", DisplayApps: &displayApps, Scripts: []*proto.Script{{ - DisplayName: "Shutdown Script", - RunOnStop: true, - LogPath: "coder-shutdown-script.log", - Script: "echo bye bye", - TimeoutSeconds: 30, + Icon: "/emojis/25c0.png", + DisplayName: "Shutdown Script", + RunOnStop: true, + LogPath: "coder-shutdown-script.log", + Script: "echo bye bye", }}, }, { Name: "dev3", @@ -307,6 +307,7 @@ func TestConvertResources(t *testing.T) { DisplayName: "Startup Script", RunOnStart: true, LogPath: "coder-startup-script.log", + Icon: "/emojis/25b6.png", Script: " #!/bin/bash\n # home folder can be empty, so copying default bash settings\n if [ ! -f ~/.profile ]; then\n cp /etc/skel/.profile $HOME\n fi\n if [ ! -f ~/.bashrc ]; then\n cp /etc/skel/.bashrc $HOME\n fi\n # install and start code-server\n curl -fsSL https://code-server.dev/install.sh | sh | tee code-server-install.log\n code-server --auth none --port 13337 | tee code-server-install.log &\n", }}, }}, From a47fe288f2b718a45c833ec0e5348e02ec4c7c2c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:20:48 +0000 Subject: [PATCH 28/59] Fix dbfake --- coderd/database/dbfake/dbfake.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/coderd/database/dbfake/dbfake.go b/coderd/database/dbfake/dbfake.go index 995df8129d969..7687a6327e743 100644 --- a/coderd/database/dbfake/dbfake.go +++ b/coderd/database/dbfake/dbfake.go @@ -3489,9 +3489,6 @@ func (q *FakeQuerier) GetWorkspaceAppsByAgentID(_ context.Context, id uuid.UUID) apps = append(apps, app) } } - if len(apps) == 0 { - return nil, sql.ErrNoRows - } return apps, nil } From da40c798c5b5c26b7bea2f881fbcf0e5b5014ade Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:33:19 +0000 Subject: [PATCH 29/59] Fix resources --- agent/agent.go | 2 - coderd/apidoc/docs.go | 19 + coderd/apidoc/swagger.json | 16 + coderd/database/foreign_key_constraint.go | 1 + coderd/database/unique_constraint.go | 7 +- docs/api/agents.md | 1 + docs/api/builds.md | 550 +++++++++--------- docs/api/schemas.md | 89 +-- docs/api/templates.md | 432 +++++++------- docs/api/workspaces.md | 5 + provisioner/terraform/resources.go | 1 + site/src/api/typesGenerated.ts | 1 + .../components/WorkspaceBuildLogs/Logs.tsx | 9 +- 13 files changed, 611 insertions(+), 522 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 9053a0aa236aa..7c74df63ee3e5 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1392,8 +1392,6 @@ func (a *agent) Close() error { } else { lifecycleState = codersdk.WorkspaceAgentLifecycleShutdownError } - } else { - lifecycleState = codersdk.WorkspaceAgentLifecycleOff } a.setLifecycle(ctx, lifecycleState) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index cb668f5394b38..609199bbd975d 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -10717,6 +10717,14 @@ const docTemplate = `{ "type": "string", "format": "date-time" }, + "startup_script_behavior": { + "description": "StartupScriptBehavior is a legacy field that is deprecated in favor\nof the ` + "`" + `coder_script` + "`" + ` resource. It's only referenced by old clients.\nDeprecated: Remove in the future!", + "allOf": [ + { + "$ref": "#/definitions/codersdk.WorkspaceAgentStartupScriptBehavior" + } + ] + }, "status": { "$ref": "#/definitions/codersdk.WorkspaceAgentStatus" }, @@ -10915,6 +10923,17 @@ const docTemplate = `{ } } }, + "codersdk.WorkspaceAgentStartupScriptBehavior": { + "type": "string", + "enum": [ + "blocking", + "non-blocking" + ], + "x-enum-varnames": [ + "WorkspaceAgentStartupScriptBehaviorBlocking", + "WorkspaceAgentStartupScriptBehaviorNonBlocking" + ] + }, "codersdk.WorkspaceAgentStatus": { "type": "string", "enum": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 44fe035bbe0fc..b63d2194e76d7 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9720,6 +9720,14 @@ "type": "string", "format": "date-time" }, + "startup_script_behavior": { + "description": "StartupScriptBehavior is a legacy field that is deprecated in favor\nof the `coder_script` resource. It's only referenced by old clients.\nDeprecated: Remove in the future!", + "allOf": [ + { + "$ref": "#/definitions/codersdk.WorkspaceAgentStartupScriptBehavior" + } + ] + }, "status": { "$ref": "#/definitions/codersdk.WorkspaceAgentStatus" }, @@ -9918,6 +9926,14 @@ } } }, + "codersdk.WorkspaceAgentStartupScriptBehavior": { + "type": "string", + "enum": ["blocking", "non-blocking"], + "x-enum-varnames": [ + "WorkspaceAgentStartupScriptBehaviorBlocking", + "WorkspaceAgentStartupScriptBehaviorNonBlocking" + ] + }, "codersdk.WorkspaceAgentStatus": { "type": "string", "enum": ["connecting", "connected", "disconnected", "timeout"], diff --git a/coderd/database/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go index db2021166f621..f5dcf3baf4c5e 100644 --- a/coderd/database/foreign_key_constraint.go +++ b/coderd/database/foreign_key_constraint.go @@ -31,6 +31,7 @@ const ( ForeignKeyUserLinksOauthRefreshTokenKeyID ForeignKeyConstraint = "user_links_oauth_refresh_token_key_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_oauth_refresh_token_key_id_fkey FOREIGN KEY (oauth_refresh_token_key_id) REFERENCES dbcrypt_keys(active_key_digest); ForeignKeyUserLinksUserID ForeignKeyConstraint = "user_links_user_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentMetadataWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_metadata_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; + ForeignKeyWorkspaceAgentScriptsWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_scripts_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_scripts ADD CONSTRAINT workspace_agent_scripts_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentStartupLogsAgentID ForeignKeyConstraint = "workspace_agent_startup_logs_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentsResourceID ForeignKeyConstraint = "workspace_agents_resource_id_fkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE; ForeignKeyWorkspaceAppStatsAgentID ForeignKeyConstraint = "workspace_app_stats_agent_id_fkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id); diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index 367ac0141a39e..5eca7bfbcc6fb 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -7,7 +7,7 @@ type UniqueConstraint string // UniqueConstraint enums. const ( UniqueAgentStatsPkey UniqueConstraint = "agent_stats_pkey" // ALTER TABLE ONLY workspace_agent_stats ADD CONSTRAINT agent_stats_pkey PRIMARY KEY (id); - UniqueApiKeysPkey UniqueConstraint = "api_keys_pkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id); + UniqueAPIKeysPkey UniqueConstraint = "api_keys_pkey" // ALTER TABLE ONLY api_keys ADD CONSTRAINT api_keys_pkey PRIMARY KEY (id); UniqueAuditLogsPkey UniqueConstraint = "audit_logs_pkey" // ALTER TABLE ONLY audit_logs ADD CONSTRAINT audit_logs_pkey PRIMARY KEY (id); UniqueDbcryptKeysActiveKeyDigestKey UniqueConstraint = "dbcrypt_keys_active_key_digest_key" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_active_key_digest_key UNIQUE (active_key_digest); UniqueDbcryptKeysPkey UniqueConstraint = "dbcrypt_keys_pkey" // ALTER TABLE ONLY dbcrypt_keys ADD CONSTRAINT dbcrypt_keys_pkey PRIMARY KEY (number); @@ -15,7 +15,7 @@ const ( UniqueFilesHashCreatedByKey UniqueConstraint = "files_hash_created_by_key" // ALTER TABLE ONLY files ADD CONSTRAINT files_hash_created_by_key UNIQUE (hash, created_by); UniqueFilesPkey UniqueConstraint = "files_pkey" // ALTER TABLE ONLY files ADD CONSTRAINT files_pkey PRIMARY KEY (id); UniqueGitAuthLinksProviderIDUserIDKey UniqueConstraint = "git_auth_links_provider_id_user_id_key" // ALTER TABLE ONLY git_auth_links ADD CONSTRAINT git_auth_links_provider_id_user_id_key UNIQUE (provider_id, user_id); - UniqueGitsshkeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); + UniqueGitSSHKeysPkey UniqueConstraint = "gitsshkeys_pkey" // ALTER TABLE ONLY gitsshkeys ADD CONSTRAINT gitsshkeys_pkey PRIMARY KEY (user_id); UniqueGroupMembersUserIDGroupIDKey UniqueConstraint = "group_members_user_id_group_id_key" // ALTER TABLE ONLY group_members ADD CONSTRAINT group_members_user_id_group_id_key UNIQUE (user_id, group_id); UniqueGroupsNameOrganizationIDKey UniqueConstraint = "groups_name_organization_id_key" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_name_organization_id_key UNIQUE (name, organization_id); UniqueGroupsPkey UniqueConstraint = "groups_pkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_pkey PRIMARY KEY (id); @@ -57,6 +57,9 @@ const ( UniqueWorkspaceProxiesPkey UniqueConstraint = "workspace_proxies_pkey" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_pkey PRIMARY KEY (id); UniqueWorkspaceProxiesRegionIDUnique UniqueConstraint = "workspace_proxies_region_id_unique" // ALTER TABLE ONLY workspace_proxies ADD CONSTRAINT workspace_proxies_region_id_unique UNIQUE (region_id); UniqueWorkspaceResourceMetadataName UniqueConstraint = "workspace_resource_metadata_name" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_name UNIQUE (workspace_resource_id, key); + UniqueWorkspaceResourceMetadataPkey UniqueConstraint = "workspace_resource_metadata_pkey" // ALTER TABLE ONLY workspace_resource_metadata ADD CONSTRAINT workspace_resource_metadata_pkey PRIMARY KEY (id); + UniqueWorkspaceResourcesPkey UniqueConstraint = "workspace_resources_pkey" // ALTER TABLE ONLY workspace_resources ADD CONSTRAINT workspace_resources_pkey PRIMARY KEY (id); + UniqueWorkspacesPkey UniqueConstraint = "workspaces_pkey" // ALTER TABLE ONLY workspaces ADD CONSTRAINT workspaces_pkey PRIMARY KEY (id); UniqueIndexAPIKeyName UniqueConstraint = "idx_api_key_name" // CREATE UNIQUE INDEX idx_api_key_name ON api_keys USING btree (user_id, token_name) WHERE (login_type = 'token'::login_type); UniqueIndexOrganizationName UniqueConstraint = "idx_organization_name" // CREATE UNIQUE INDEX idx_organization_name ON organizations USING btree (name); UniqueIndexOrganizationNameLower UniqueConstraint = "idx_organization_name_lower" // CREATE UNIQUE INDEX idx_organization_name_lower ON organizations USING btree (lower(name)); diff --git a/docs/api/agents.md b/docs/api/agents.md index ff357d7d97677..536d7d2d5a7c7 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -729,6 +729,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/docs/api/builds.md b/docs/api/builds.md index 24f12d52d13af..1db2a8da526b6 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -136,6 +136,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -315,6 +316,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -633,6 +635,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -670,115 +673,118 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | Display name is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» display_apps` | array | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» expanded_directory` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»» log_sources` | array | false | | | -| `»»» created_at` | string(date-time) | false | | | -| `»»» display_name` | string | false | | | -| `»»» icon` | string | false | | | -| `»»» id` | string(uuid) | false | | | -| `»»» workspace_agent_id` | string(uuid) | false | | | -| `»» logs_length` | integer | false | | | -| `»» logs_overflowed` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» ready_at` | string(date-time) | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» scripts` | array | false | | | -| `»»» cron` | string | false | | | -| `»»» log_path` | string | false | | | -| `»»» log_source_id` | string(uuid) | false | | | -| `»»» run_on_start` | boolean | false | | | -| `»»» run_on_stop` | boolean | false | | | -| `»»» script` | string | false | | | -| `»»» start_blocks_login` | boolean | false | | | -| `»»» timeout_seconds` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»» subsystems` | array | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | Display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» display_apps` | array | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» expanded_directory` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»» log_sources` | array | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» display_name` | string | false | | | +| `»»» icon` | string | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» workspace_agent_id` | string(uuid) | false | | | +| `»» logs_length` | integer | false | | | +| `»» logs_overflowed` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» ready_at` | string(date-time) | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» scripts` | array | false | | | +| `»»» cron` | string | false | | | +| `»»» log_path` | string | false | | | +| `»»» log_source_id` | string(uuid) | false | | | +| `»»» run_on_start` | boolean | false | | | +| `»»» run_on_stop` | boolean | false | | | +| `»»» script` | string | false | | | +| `»»» start_blocks_login` | boolean | false | | | +| `»»» timeout_seconds` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | +| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»» subsystems` | array | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | #### Enumerated Values -| Property | Value | -| ---------------------- | ------------------ | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ------------------------- | ------------------ | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `startup_script_behavior` | `blocking` | +| `startup_script_behavior` | `non-blocking` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -916,6 +922,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -1100,6 +1107,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -1148,172 +1156,175 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ Status Code **200** -| Name | Type | Required | Restrictions | Description | -| -------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» build_number` | integer | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» deadline` | string(date-time) | false | | | -| `» id` | string(uuid) | false | | | -| `» initiator_id` | string(uuid) | false | | | -| `» initiator_name` | string | false | | | -| `» job` | [codersdk.ProvisionerJob](schemas.md#codersdkprovisionerjob) | false | | | -| `»» canceled_at` | string(date-time) | false | | | -| `»» completed_at` | string(date-time) | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» error` | string | false | | | -| `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | -| `»» file_id` | string(uuid) | false | | | -| `»» id` | string(uuid) | false | | | -| `»» queue_position` | integer | false | | | -| `»» queue_size` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | -| `»» tags` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» worker_id` | string(uuid) | false | | | -| `» max_deadline` | string(date-time) | false | | | -| `» reason` | [codersdk.BuildReason](schemas.md#codersdkbuildreason) | false | | | -| `» resources` | array | false | | | -| `»» agents` | array | false | | | -| `»»» apps` | array | false | | | -| `»»»» command` | string | false | | | -| `»»»» display_name` | string | false | | Display name is a friendly name for the app. | -| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»»» id` | string(uuid) | false | | | -| `»»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | -| `»»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»»» architecture` | string | false | | | -| `»»» connection_timeout_seconds` | integer | false | | | -| `»»» created_at` | string(date-time) | false | | | -| `»»» directory` | string | false | | | -| `»»» disconnected_at` | string(date-time) | false | | | -| `»»» display_apps` | array | false | | | -| `»»» environment_variables` | object | false | | | -| `»»»» [any property]` | string | false | | | -| `»»» expanded_directory` | string | false | | | -| `»»» first_connected_at` | string(date-time) | false | | | -| `»»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»»» id` | string(uuid) | false | | | -| `»»» instance_id` | string | false | | | -| `»»» last_connected_at` | string(date-time) | false | | | -| `»»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»»» latency_ms` | number | false | | | -| `»»»»» preferred` | boolean | false | | | -| `»»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»»» log_sources` | array | false | | | -| `»»»» created_at` | string(date-time) | false | | | -| `»»»» display_name` | string | false | | | -| `»»»» icon` | string | false | | | -| `»»»» id` | string(uuid) | false | | | -| `»»»» workspace_agent_id` | string(uuid) | false | | | -| `»»» logs_length` | integer | false | | | -| `»»» logs_overflowed` | boolean | false | | | -| `»»» name` | string | false | | | -| `»»» operating_system` | string | false | | | -| `»»» ready_at` | string(date-time) | false | | | -| `»»» resource_id` | string(uuid) | false | | | -| `»»» scripts` | array | false | | | -| `»»»» cron` | string | false | | | -| `»»»» log_path` | string | false | | | -| `»»»» log_source_id` | string(uuid) | false | | | -| `»»»» run_on_start` | boolean | false | | | -| `»»»» run_on_stop` | boolean | false | | | -| `»»»» script` | string | false | | | -| `»»»» start_blocks_login` | boolean | false | | | -| `»»»» timeout_seconds` | integer | false | | | -| `»»» started_at` | string(date-time) | false | | | -| `»»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»»» subsystems` | array | false | | | -| `»»» troubleshooting_url` | string | false | | | -| `»»» updated_at` | string(date-time) | false | | | -| `»»» version` | string | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» daily_cost` | integer | false | | | -| `»» hide` | boolean | false | | | -| `»» icon` | string | false | | | -| `»» id` | string(uuid) | false | | | -| `»» job_id` | string(uuid) | false | | | -| `»» metadata` | array | false | | | -| `»»» key` | string | false | | | -| `»»» sensitive` | boolean | false | | | -| `»»» value` | string | false | | | -| `»» name` | string | false | | | -| `»» type` | string | false | | | -| `»» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | -| `» status` | [codersdk.WorkspaceStatus](schemas.md#codersdkworkspacestatus) | false | | | -| `» template_version_id` | string(uuid) | false | | | -| `» template_version_name` | string | false | | | -| `» transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | -| `» updated_at` | string(date-time) | false | | | -| `» workspace_id` | string(uuid) | false | | | -| `» workspace_name` | string | false | | | -| `» workspace_owner_id` | string(uuid) | false | | | -| `» workspace_owner_name` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| -------------------------------- | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» build_number` | integer | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» deadline` | string(date-time) | false | | | +| `» id` | string(uuid) | false | | | +| `» initiator_id` | string(uuid) | false | | | +| `» initiator_name` | string | false | | | +| `» job` | [codersdk.ProvisionerJob](schemas.md#codersdkprovisionerjob) | false | | | +| `»» canceled_at` | string(date-time) | false | | | +| `»» completed_at` | string(date-time) | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» error` | string | false | | | +| `»» error_code` | [codersdk.JobErrorCode](schemas.md#codersdkjoberrorcode) | false | | | +| `»» file_id` | string(uuid) | false | | | +| `»» id` | string(uuid) | false | | | +| `»» queue_position` | integer | false | | | +| `»» queue_size` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» status` | [codersdk.ProvisionerJobStatus](schemas.md#codersdkprovisionerjobstatus) | false | | | +| `»» tags` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» worker_id` | string(uuid) | false | | | +| `» max_deadline` | string(date-time) | false | | | +| `» reason` | [codersdk.BuildReason](schemas.md#codersdkbuildreason) | false | | | +| `» resources` | array | false | | | +| `»» agents` | array | false | | | +| `»»» apps` | array | false | | | +| `»»»» command` | string | false | | | +| `»»»» display_name` | string | false | | Display name is a friendly name for the app. | +| `»»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»»» id` | string(uuid) | false | | | +| `»»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | +| `»»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»»» architecture` | string | false | | | +| `»»» connection_timeout_seconds` | integer | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» directory` | string | false | | | +| `»»» disconnected_at` | string(date-time) | false | | | +| `»»» display_apps` | array | false | | | +| `»»» environment_variables` | object | false | | | +| `»»»» [any property]` | string | false | | | +| `»»» expanded_directory` | string | false | | | +| `»»» first_connected_at` | string(date-time) | false | | | +| `»»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»»» id` | string(uuid) | false | | | +| `»»» instance_id` | string | false | | | +| `»»» last_connected_at` | string(date-time) | false | | | +| `»»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»»» latency_ms` | number | false | | | +| `»»»»» preferred` | boolean | false | | | +| `»»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»»» log_sources` | array | false | | | +| `»»»» created_at` | string(date-time) | false | | | +| `»»»» display_name` | string | false | | | +| `»»»» icon` | string | false | | | +| `»»»» id` | string(uuid) | false | | | +| `»»»» workspace_agent_id` | string(uuid) | false | | | +| `»»» logs_length` | integer | false | | | +| `»»» logs_overflowed` | boolean | false | | | +| `»»» name` | string | false | | | +| `»»» operating_system` | string | false | | | +| `»»» ready_at` | string(date-time) | false | | | +| `»»» resource_id` | string(uuid) | false | | | +| `»»» scripts` | array | false | | | +| `»»»» cron` | string | false | | | +| `»»»» log_path` | string | false | | | +| `»»»» log_source_id` | string(uuid) | false | | | +| `»»»» run_on_start` | boolean | false | | | +| `»»»» run_on_stop` | boolean | false | | | +| `»»»» script` | string | false | | | +| `»»»» start_blocks_login` | boolean | false | | | +| `»»»» timeout_seconds` | integer | false | | | +| `»»» started_at` | string(date-time) | false | | | +| `»»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | +| `»»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»»» subsystems` | array | false | | | +| `»»» troubleshooting_url` | string | false | | | +| `»»» updated_at` | string(date-time) | false | | | +| `»»» version` | string | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» daily_cost` | integer | false | | | +| `»» hide` | boolean | false | | | +| `»» icon` | string | false | | | +| `»» id` | string(uuid) | false | | | +| `»» job_id` | string(uuid) | false | | | +| `»» metadata` | array | false | | | +| `»»» key` | string | false | | | +| `»»» sensitive` | boolean | false | | | +| `»»» value` | string | false | | | +| `»» name` | string | false | | | +| `»» type` | string | false | | | +| `»» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| `» status` | [codersdk.WorkspaceStatus](schemas.md#codersdkworkspacestatus) | false | | | +| `» template_version_id` | string(uuid) | false | | | +| `» template_version_name` | string | false | | | +| `» transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| `» updated_at` | string(date-time) | false | | | +| `» workspace_id` | string(uuid) | false | | | +| `» workspace_name` | string | false | | | +| `» workspace_owner_id` | string(uuid) | false | | | +| `» workspace_owner_name` | string | false | | | #### Enumerated Values -| Property | Value | -| ---------------------- | ----------------------------- | -| `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | -| `status` | `pending` | -| `status` | `running` | -| `status` | `succeeded` | -| `status` | `canceling` | -| `status` | `canceled` | -| `status` | `failed` | -| `reason` | `initiator` | -| `reason` | `autostart` | -| `reason` | `autostop` | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | -| `status` | `pending` | -| `status` | `starting` | -| `status` | `running` | -| `status` | `stopping` | -| `status` | `stopped` | -| `status` | `failed` | -| `status` | `canceling` | -| `status` | `canceled` | -| `status` | `deleting` | -| `status` | `deleted` | -| `transition` | `start` | -| `transition` | `stop` | -| `transition` | `delete` | +| Property | Value | +| ------------------------- | ----------------------------- | +| `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | +| `status` | `pending` | +| `status` | `running` | +| `status` | `succeeded` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `failed` | +| `reason` | `initiator` | +| `reason` | `autostart` | +| `reason` | `autostop` | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `startup_script_behavior` | `blocking` | +| `startup_script_behavior` | `non-blocking` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | +| `status` | `pending` | +| `status` | `starting` | +| `status` | `running` | +| `status` | `stopping` | +| `status` | `stopped` | +| `status` | `failed` | +| `status` | `canceling` | +| `status` | `canceled` | +| `status` | `deleting` | +| `status` | `deleted` | +| `transition` | `start` | +| `transition` | `stop` | +| `transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -1472,6 +1483,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index ff5b74e0d5b3e..d0720e465ebe6 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -5505,6 +5505,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -5664,6 +5665,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -5674,40 +5676,41 @@ If the schedule is empty, the user will be updated to use the default schedule.| ### Properties -| Name | Type | Required | Restrictions | Description | -| ---------------------------- | ----------------------------------------------------------------------------- | -------- | ------------ | ------------------------------------------------------------------- | -| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | | -| `architecture` | string | false | | | -| `connection_timeout_seconds` | integer | false | | | -| `created_at` | string | false | | | -| `directory` | string | false | | | -| `disconnected_at` | string | false | | | -| `display_apps` | array of [codersdk.DisplayApp](#codersdkdisplayapp) | false | | | -| `environment_variables` | object | false | | | -| » `[any property]` | string | false | | | -| `expanded_directory` | string | false | | | -| `first_connected_at` | string | false | | | -| `health` | [codersdk.WorkspaceAgentHealth](#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `id` | string | false | | | -| `instance_id` | string | false | | | -| `last_connected_at` | string | false | | | -| `latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | -| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | | -| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | | -| `log_sources` | array of [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | | -| `logs_length` | integer | false | | | -| `logs_overflowed` | boolean | false | | | -| `name` | string | false | | | -| `operating_system` | string | false | | | -| `ready_at` | string | false | | | -| `resource_id` | string | false | | | -| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | | -| `started_at` | string | false | | | -| `status` | [codersdk.WorkspaceAgentStatus](#codersdkworkspaceagentstatus) | false | | | -| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | | -| `troubleshooting_url` | string | false | | | -| `updated_at` | string | false | | | -| `version` | string | false | | | +| Name | Type | Required | Restrictions | Description | +| ---------------------------- | -------------------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `apps` | array of [codersdk.WorkspaceApp](#codersdkworkspaceapp) | false | | | +| `architecture` | string | false | | | +| `connection_timeout_seconds` | integer | false | | | +| `created_at` | string | false | | | +| `directory` | string | false | | | +| `disconnected_at` | string | false | | | +| `display_apps` | array of [codersdk.DisplayApp](#codersdkdisplayapp) | false | | | +| `environment_variables` | object | false | | | +| » `[any property]` | string | false | | | +| `expanded_directory` | string | false | | | +| `first_connected_at` | string | false | | | +| `health` | [codersdk.WorkspaceAgentHealth](#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `id` | string | false | | | +| `instance_id` | string | false | | | +| `last_connected_at` | string | false | | | +| `latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | +| » `[any property]` | [codersdk.DERPRegion](#codersdkderpregion) | false | | | +| `lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | | +| `log_sources` | array of [codersdk.WorkspaceAgentLogSource](#codersdkworkspaceagentlogsource) | false | | | +| `logs_length` | integer | false | | | +| `logs_overflowed` | boolean | false | | | +| `name` | string | false | | | +| `operating_system` | string | false | | | +| `ready_at` | string | false | | | +| `resource_id` | string | false | | | +| `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | | +| `started_at` | string | false | | | +| `startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | +| `status` | [codersdk.WorkspaceAgentStatus](#codersdkworkspaceagentstatus) | false | | | +| `subsystems` | array of [codersdk.AgentSubsystem](#codersdkagentsubsystem) | false | | | +| `troubleshooting_url` | string | false | | | +| `updated_at` | string | false | | | +| `version` | string | false | | | ## codersdk.WorkspaceAgentConnectionInfo @@ -5955,6 +5958,21 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `start_blocks_login` | boolean | false | | | | `timeout_seconds` | integer | false | | | +## codersdk.WorkspaceAgentStartupScriptBehavior + +```json +"blocking" +``` + +### Properties + +#### Enumerated Values + +| Value | +| -------------- | +| `blocking` | +| `non-blocking` | + ## codersdk.WorkspaceAgentStatus ```json @@ -6166,6 +6184,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -6494,6 +6513,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -6724,6 +6744,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/docs/api/templates.md b/docs/api/templates.md index 48af51d70bf61..6187778e38ba5 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1647,6 +1647,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -1684,115 +1685,118 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | Display name is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» display_apps` | array | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» expanded_directory` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»» log_sources` | array | false | | | -| `»»» created_at` | string(date-time) | false | | | -| `»»» display_name` | string | false | | | -| `»»» icon` | string | false | | | -| `»»» id` | string(uuid) | false | | | -| `»»» workspace_agent_id` | string(uuid) | false | | | -| `»» logs_length` | integer | false | | | -| `»» logs_overflowed` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» ready_at` | string(date-time) | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» scripts` | array | false | | | -| `»»» cron` | string | false | | | -| `»»» log_path` | string | false | | | -| `»»» log_source_id` | string(uuid) | false | | | -| `»»» run_on_start` | boolean | false | | | -| `»»» run_on_stop` | boolean | false | | | -| `»»» script` | string | false | | | -| `»»» start_blocks_login` | boolean | false | | | -| `»»» timeout_seconds` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»» subsystems` | array | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | Display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» display_apps` | array | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» expanded_directory` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»» log_sources` | array | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» display_name` | string | false | | | +| `»»» icon` | string | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» workspace_agent_id` | string(uuid) | false | | | +| `»» logs_length` | integer | false | | | +| `»» logs_overflowed` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» ready_at` | string(date-time) | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» scripts` | array | false | | | +| `»»» cron` | string | false | | | +| `»»» log_path` | string | false | | | +| `»»» log_source_id` | string(uuid) | false | | | +| `»»» run_on_start` | boolean | false | | | +| `»»» run_on_stop` | boolean | false | | | +| `»»» script` | string | false | | | +| `»»» start_blocks_login` | boolean | false | | | +| `»»» timeout_seconds` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | +| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»» subsystems` | array | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | #### Enumerated Values -| Property | Value | -| ---------------------- | ------------------ | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ------------------------- | ------------------ | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `startup_script_behavior` | `blocking` | +| `startup_script_behavior` | `non-blocking` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -2064,6 +2068,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -2101,115 +2106,118 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r Status Code **200** -| Name | Type | Required | Restrictions | Description | -| ------------------------------- | -------------------------------------------------------------------------------- | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `[array item]` | array | false | | | -| `» agents` | array | false | | | -| `»» apps` | array | false | | | -| `»»» command` | string | false | | | -| `»»» display_name` | string | false | | Display name is a friendly name for the app. | -| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | -| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | -| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | -| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | -| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | -| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | -| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | -| `»»» id` | string(uuid) | false | | | -| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | -| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | -| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | -| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | -| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | -| `»» architecture` | string | false | | | -| `»» connection_timeout_seconds` | integer | false | | | -| `»» created_at` | string(date-time) | false | | | -| `»» directory` | string | false | | | -| `»» disconnected_at` | string(date-time) | false | | | -| `»» display_apps` | array | false | | | -| `»» environment_variables` | object | false | | | -| `»»» [any property]` | string | false | | | -| `»» expanded_directory` | string | false | | | -| `»» first_connected_at` | string(date-time) | false | | | -| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | -| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | -| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | -| `»» id` | string(uuid) | false | | | -| `»» instance_id` | string | false | | | -| `»» last_connected_at` | string(date-time) | false | | | -| `»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | -| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | -| `»»»» latency_ms` | number | false | | | -| `»»»» preferred` | boolean | false | | | -| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | -| `»» log_sources` | array | false | | | -| `»»» created_at` | string(date-time) | false | | | -| `»»» display_name` | string | false | | | -| `»»» icon` | string | false | | | -| `»»» id` | string(uuid) | false | | | -| `»»» workspace_agent_id` | string(uuid) | false | | | -| `»» logs_length` | integer | false | | | -| `»» logs_overflowed` | boolean | false | | | -| `»» name` | string | false | | | -| `»» operating_system` | string | false | | | -| `»» ready_at` | string(date-time) | false | | | -| `»» resource_id` | string(uuid) | false | | | -| `»» scripts` | array | false | | | -| `»»» cron` | string | false | | | -| `»»» log_path` | string | false | | | -| `»»» log_source_id` | string(uuid) | false | | | -| `»»» run_on_start` | boolean | false | | | -| `»»» run_on_stop` | boolean | false | | | -| `»»» script` | string | false | | | -| `»»» start_blocks_login` | boolean | false | | | -| `»»» timeout_seconds` | integer | false | | | -| `»» started_at` | string(date-time) | false | | | -| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | -| `»» subsystems` | array | false | | | -| `»» troubleshooting_url` | string | false | | | -| `»» updated_at` | string(date-time) | false | | | -| `»» version` | string | false | | | -| `» created_at` | string(date-time) | false | | | -| `» daily_cost` | integer | false | | | -| `» hide` | boolean | false | | | -| `» icon` | string | false | | | -| `» id` | string(uuid) | false | | | -| `» job_id` | string(uuid) | false | | | -| `» metadata` | array | false | | | -| `»» key` | string | false | | | -| `»» sensitive` | boolean | false | | | -| `»» value` | string | false | | | -| `» name` | string | false | | | -| `» type` | string | false | | | -| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | +| Name | Type | Required | Restrictions | Description | +| ------------------------------- | ------------------------------------------------------------------------------------------------------ | -------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `[array item]` | array | false | | | +| `» agents` | array | false | | | +| `»» apps` | array | false | | | +| `»»» command` | string | false | | | +| `»»» display_name` | string | false | | Display name is a friendly name for the app. | +| `»»» external` | boolean | false | | External specifies whether the URL should be opened externally on the client or not. | +| `»»» health` | [codersdk.WorkspaceAppHealth](schemas.md#codersdkworkspaceapphealth) | false | | | +| `»»» healthcheck` | [codersdk.Healthcheck](schemas.md#codersdkhealthcheck) | false | | Healthcheck specifies the configuration for checking app health. | +| `»»»» interval` | integer | false | | Interval specifies the seconds between each health check. | +| `»»»» threshold` | integer | false | | Threshold specifies the number of consecutive failed health checks before returning "unhealthy". | +| `»»»» url` | string | false | | URL specifies the endpoint to check for the app health. | +| `»»» icon` | string | false | | Icon is a relative path or external URL that specifies an icon to be displayed in the dashboard. | +| `»»» id` | string(uuid) | false | | | +| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | | +| `»»» slug` | string | false | | Slug is a unique identifier within the agent. | +| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. | +| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. | +| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. | +| `»» architecture` | string | false | | | +| `»» connection_timeout_seconds` | integer | false | | | +| `»» created_at` | string(date-time) | false | | | +| `»» directory` | string | false | | | +| `»» disconnected_at` | string(date-time) | false | | | +| `»» display_apps` | array | false | | | +| `»» environment_variables` | object | false | | | +| `»»» [any property]` | string | false | | | +| `»» expanded_directory` | string | false | | | +| `»» first_connected_at` | string(date-time) | false | | | +| `»» health` | [codersdk.WorkspaceAgentHealth](schemas.md#codersdkworkspaceagenthealth) | false | | Health reports the health of the agent. | +| `»»» healthy` | boolean | false | | Healthy is true if the agent is healthy. | +| `»»» reason` | string | false | | Reason is a human-readable explanation of the agent's health. It is empty if Healthy is true. | +| `»» id` | string(uuid) | false | | | +| `»» instance_id` | string | false | | | +| `»» last_connected_at` | string(date-time) | false | | | +| `»» latency` | object | false | | Latency is mapped by region name (e.g. "New York City", "Seattle"). | +| `»»» [any property]` | [codersdk.DERPRegion](schemas.md#codersdkderpregion) | false | | | +| `»»»» latency_ms` | number | false | | | +| `»»»» preferred` | boolean | false | | | +| `»» lifecycle_state` | [codersdk.WorkspaceAgentLifecycle](schemas.md#codersdkworkspaceagentlifecycle) | false | | | +| `»» log_sources` | array | false | | | +| `»»» created_at` | string(date-time) | false | | | +| `»»» display_name` | string | false | | | +| `»»» icon` | string | false | | | +| `»»» id` | string(uuid) | false | | | +| `»»» workspace_agent_id` | string(uuid) | false | | | +| `»» logs_length` | integer | false | | | +| `»» logs_overflowed` | boolean | false | | | +| `»» name` | string | false | | | +| `»» operating_system` | string | false | | | +| `»» ready_at` | string(date-time) | false | | | +| `»» resource_id` | string(uuid) | false | | | +| `»» scripts` | array | false | | | +| `»»» cron` | string | false | | | +| `»»» log_path` | string | false | | | +| `»»» log_source_id` | string(uuid) | false | | | +| `»»» run_on_start` | boolean | false | | | +| `»»» run_on_stop` | boolean | false | | | +| `»»» script` | string | false | | | +| `»»» start_blocks_login` | boolean | false | | | +| `»»» timeout_seconds` | integer | false | | | +| `»» started_at` | string(date-time) | false | | | +| `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | +| `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | +| `»» subsystems` | array | false | | | +| `»» troubleshooting_url` | string | false | | | +| `»» updated_at` | string(date-time) | false | | | +| `»» version` | string | false | | | +| `» created_at` | string(date-time) | false | | | +| `» daily_cost` | integer | false | | | +| `» hide` | boolean | false | | | +| `» icon` | string | false | | | +| `» id` | string(uuid) | false | | | +| `» job_id` | string(uuid) | false | | | +| `» metadata` | array | false | | | +| `»» key` | string | false | | | +| `»» sensitive` | boolean | false | | | +| `»» value` | string | false | | | +| `» name` | string | false | | | +| `» type` | string | false | | | +| `» workspace_transition` | [codersdk.WorkspaceTransition](schemas.md#codersdkworkspacetransition) | false | | | #### Enumerated Values -| Property | Value | -| ---------------------- | ------------------ | -| `health` | `disabled` | -| `health` | `initializing` | -| `health` | `healthy` | -| `health` | `unhealthy` | -| `sharing_level` | `owner` | -| `sharing_level` | `authenticated` | -| `sharing_level` | `public` | -| `lifecycle_state` | `created` | -| `lifecycle_state` | `starting` | -| `lifecycle_state` | `start_timeout` | -| `lifecycle_state` | `start_error` | -| `lifecycle_state` | `ready` | -| `lifecycle_state` | `shutting_down` | -| `lifecycle_state` | `shutdown_timeout` | -| `lifecycle_state` | `shutdown_error` | -| `lifecycle_state` | `off` | -| `status` | `connecting` | -| `status` | `connected` | -| `status` | `disconnected` | -| `status` | `timeout` | -| `workspace_transition` | `start` | -| `workspace_transition` | `stop` | -| `workspace_transition` | `delete` | +| Property | Value | +| ------------------------- | ------------------ | +| `health` | `disabled` | +| `health` | `initializing` | +| `health` | `healthy` | +| `health` | `unhealthy` | +| `sharing_level` | `owner` | +| `sharing_level` | `authenticated` | +| `sharing_level` | `public` | +| `lifecycle_state` | `created` | +| `lifecycle_state` | `starting` | +| `lifecycle_state` | `start_timeout` | +| `lifecycle_state` | `start_error` | +| `lifecycle_state` | `ready` | +| `lifecycle_state` | `shutting_down` | +| `lifecycle_state` | `shutdown_timeout` | +| `lifecycle_state` | `shutdown_error` | +| `lifecycle_state` | `off` | +| `startup_script_behavior` | `blocking` | +| `startup_script_behavior` | `non-blocking` | +| `status` | `connecting` | +| `status` | `connected` | +| `status` | `disconnected` | +| `status` | `timeout` | +| `workspace_transition` | `start` | +| `workspace_transition` | `stop` | +| `workspace_transition` | `delete` | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 68c50ddb1ff84..3ee5676f3081f 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -166,6 +166,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -372,6 +373,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -577,6 +579,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -784,6 +787,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", @@ -1070,6 +1074,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ } ], "started_at": "2019-08-24T14:15:22Z", + "startup_script_behavior": "blocking", "status": "connecting", "subsystems": ["envbox"], "troubleshooting_url": "string", diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index f1ca05fef7397..2af839f50e0f8 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -120,6 +120,7 @@ type State struct { // ConvertState consumes Terraform state and a GraphViz representation // produced by `terraform graph` to produce resources consumable by Coder. +// nolint:gocognit -- This function makes more sense being large for now, until refactored. func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error) { parsedGraph, err := gographviz.ParseString(rawGraph) if err != nil { diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 79dd5811bc85e..c4d1b58ca721a 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1321,6 +1321,7 @@ export interface WorkspaceAgent { readonly display_apps: DisplayApp[]; readonly log_sources: WorkspaceAgentLogSource[]; readonly scripts: WorkspaceAgentScript[]; + readonly startup_script_behavior: WorkspaceAgentStartupScriptBehavior; } // From codersdk/workspaceagents.go diff --git a/site/src/components/WorkspaceBuildLogs/Logs.tsx b/site/src/components/WorkspaceBuildLogs/Logs.tsx index 4812a19346b9b..b903f94fc38ae 100644 --- a/site/src/components/WorkspaceBuildLogs/Logs.tsx +++ b/site/src/components/WorkspaceBuildLogs/Logs.tsx @@ -69,9 +69,12 @@ export const LogLine: FC<{ {sourceIcon} {!hideTimestamp && ( <> - + {number ? number : dayjs(line.time).format(`HH:mm:ss.SSS`)} From 3631cfa46cf853ca87c557498387eaca25166287 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:34:37 +0000 Subject: [PATCH 30/59] Fix linting I think --- provisioner/terraform/resources.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 2af839f50e0f8..62fb17a643129 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -120,7 +120,7 @@ type State struct { // ConvertState consumes Terraform state and a GraphViz representation // produced by `terraform graph` to produce resources consumable by Coder. -// nolint:gocognit -- This function makes more sense being large for now, until refactored. +// nolint:gocognit // This function makes more sense being large for now, until refactored. func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error) { parsedGraph, err := gographviz.ParseString(rawGraph) if err != nil { From 77bc6e1c893a8f40f2614de54cae430c3c533529 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:43:22 +0000 Subject: [PATCH 31/59] Add fixtures --- .../000156_workspace_agent_script.up.sql | 2 +- .../000156_workspace_agent_script.up.sql | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 coderd/database/migrations/testdata/fixtures/000156_workspace_agent_script.up.sql diff --git a/coderd/database/migrations/000156_workspace_agent_script.up.sql b/coderd/database/migrations/000156_workspace_agent_script.up.sql index 660894db100c4..6c7a202c7eeda 100644 --- a/coderd/database/migrations/000156_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000156_workspace_agent_script.up.sql @@ -21,7 +21,7 @@ CREATE TABLE workspace_agent_scripts ( timeout_seconds integer NOT NULL ); -ALTER TABLE workspace_agent_logs ADD COLUMN log_source_id uuid NOT NULL; +ALTER TABLE workspace_agent_logs ADD COLUMN log_source_id uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'::uuid; ALTER TABLE workspace_agent_logs DROP COLUMN source; DROP TYPE workspace_agent_log_source; diff --git a/coderd/database/migrations/testdata/fixtures/000156_workspace_agent_script.up.sql b/coderd/database/migrations/testdata/fixtures/000156_workspace_agent_script.up.sql new file mode 100644 index 0000000000000..7523cd4714285 --- /dev/null +++ b/coderd/database/migrations/testdata/fixtures/000156_workspace_agent_script.up.sql @@ -0,0 +1,39 @@ +-- INSERT INTO workspace_agents VALUES ('45e89705-e09d-4850-bcec-f9a937f5d78d', '2022-11-02 13:03:45.046432+02', '2022-11-02 13:03:45.046432+02', 'main', NULL, NULL, NULL, '0ff953c0-92a6-4fe6-a415-eb0139a36ad1', 'ffc107ef-7ded-4d80-b1a9-0c1d0bf7ccbf', NULL, 'amd64', '{"GIT_AUTHOR_NAME": "default", "GIT_AUTHOR_EMAIL": "", "GIT_COMMITTER_NAME": "default", "GIT_COMMITTER_EMAIL": ""}', 'linux', 'code-server --auth none', NULL, NULL, '', '') ON CONFLICT DO NOTHING; + +INSERT INTO workspace_agent_log_sources ( + workspace_agent_id, + id, + created_at, + display_name, + icon +) VALUES ( + '45e89705-e09d-4850-bcec-f9a937f5d78d', + '0ff953c0-92a6-4fe6-a415-eb0139a36ad1', + '2022-11-02 13:03:45.046432+02', + 'main', + 'something.png' +) ON CONFLICT DO NOTHING; + +INSERT INTO workspace_agent_scripts ( + workspace_agent_id, + created_at, + log_source_id, + log_path, + script, + cron, + start_blocks_login, + run_on_start, + run_on_stop, + timeout_seconds +) VALUES ( + '45e89705-e09d-4850-bcec-f9a937f5d78d', + '2022-11-02 13:03:45.046432+02', + '0ff953c0-92a6-4fe6-a415-eb0139a36ad1', + '/tmp', + 'echo "hello world"', + '@daily', + TRUE, + TRUE, + TRUE, + 60 +) ON CONFLICT DO NOTHING; From 5b2bd864e49b6458e92a0ce0dd02a264eb7c9d0f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:46:11 +0000 Subject: [PATCH 32/59] fmt --- codersdk/agentsdk/agentsdk.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/codersdk/agentsdk/agentsdk.go b/codersdk/agentsdk/agentsdk.go index 0a2c47f95c049..c0750b1a22999 100644 --- a/codersdk/agentsdk/agentsdk.go +++ b/codersdk/agentsdk/agentsdk.go @@ -23,14 +23,12 @@ import ( "github.com/coder/retry" ) -var ( - // ExternalLogSourceID is the statically-defined ID of a log-source that - // appears as "External" in the dashboard. - // - // This is to support legacy API-consumers that do not create their own - // log-source. This should be removed in the future. - ExternalLogSourceID = uuid.MustParse("3b579bf4-1ed8-4b99-87a8-e9a1e3410410") -) +// ExternalLogSourceID is the statically-defined ID of a log-source that +// appears as "External" in the dashboard. +// +// This is to support legacy API-consumers that do not create their own +// log-source. This should be removed in the future. +var ExternalLogSourceID = uuid.MustParse("3b579bf4-1ed8-4b99-87a8-e9a1e3410410") // New returns a client that is used to interact with the // Coder API from a workspace agent. From 9d1a4fedf7b8d6496e6569b1f61384d8ad96c78f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 18 Sep 2023 15:48:24 +0000 Subject: [PATCH 33/59] Fix startup script behavior --- site/src/testHelpers/entities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 4db5cfb514fda..9f3cabd664cec 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -583,6 +583,7 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { logs_overflowed: false, log_sources: [MockWorkspaceAgentLogSource], scripts: [MockWorkspaceAgentScript], + startup_script_behavior: "non-blocking", subsystems: ["envbox", "exectrace"], health: { healthy: true, From ad0d678e10d18a2b4dd0ae61d9cd373119d5074e Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Tue, 19 Sep 2023 15:53:27 +0000 Subject: [PATCH 34/59] Fix comments --- agent/agent.go | 2 +- agent/agentscripts/agentscripts.go | 10 +++++----- coderd/database/dump.sql | 5 ++++- coderd/database/foreign_key_constraint.go | 1 + .../000156_workspace_agent_script.up.sql | 2 +- coderd/workspaceagents.go | 16 ++++++++++++++-- 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/agent/agent.go b/agent/agent.go index 7c74df63ee3e5..f5995b2935e76 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -682,7 +682,7 @@ func (a *agent) run(ctx context.Context) error { } else { a.setLifecycle(ctx, codersdk.WorkspaceAgentLifecycleReady) } - a.scriptRunner.StartCRON() + a.scriptRunner.StartCron() }) if err != nil { return xerrors.Errorf("track conn goroutine: %w", err) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 6cc05f8749d69..6f82e3208b177 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -87,9 +87,9 @@ func (r *Runner) Init(ctx context.Context, scripts []codersdk.WorkspaceAgentScri return nil } -// StartCRON starts the cron scheduler. +// StartCron starts the cron scheduler. // This is done async to allow for the caller to execute scripts prior. -func (r *Runner) StartCRON() { +func (r *Runner) StartCron() { r.cron.Start() } @@ -123,9 +123,6 @@ func (r *Runner) Execute(ctx context.Context, filter func(script codersdk.Worksp // If the process does not exit after a few seconds, it is forcefully killed. // This function immediately returns after a timeout, and does not wait for the process to exit. func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) error { - logger := r.Logger.With(slog.F("log_source", script.LogPath)) - logger.Info(ctx, "running agent script", slog.F("script", script.Script)) - logPath := script.LogPath if logPath == "" { logPath = fmt.Sprintf("coder-%s-script.log", script.LogSourceID) @@ -133,6 +130,9 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) if !filepath.IsAbs(logPath) { logPath = filepath.Join(r.LogDir, logPath) } + logger := r.Logger.With(slog.F("log_path", logPath)) + logger.Info(ctx, "running agent script", slog.F("script", script.Script)) + fileWriter, err := r.Filesystem.OpenFile(logPath, os.O_CREATE|os.O_RDWR, 0o600) if err != nil { return xerrors.Errorf("open %s script log file: %w", logPath, err) diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index f2f62844eeb5f..02a241baeafb4 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -744,7 +744,7 @@ CREATE UNLOGGED TABLE workspace_agent_logs ( output character varying(1024) NOT NULL, id bigint NOT NULL, level log_level DEFAULT 'info'::log_level NOT NULL, - log_source_id uuid NOT NULL + log_source_id uuid DEFAULT '00000000-0000-0000-0000-000000000000'::uuid NOT NULL ); CREATE UNLOGGED TABLE workspace_agent_metadata ( @@ -1364,6 +1364,9 @@ ALTER TABLE ONLY user_links ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspace_agent_log_sources + ADD CONSTRAINT workspace_agent_log_sources_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; + ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; diff --git a/coderd/database/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go index f5dcf3baf4c5e..3ba80ecd25179 100644 --- a/coderd/database/foreign_key_constraint.go +++ b/coderd/database/foreign_key_constraint.go @@ -30,6 +30,7 @@ const ( ForeignKeyUserLinksOauthAccessTokenKeyID ForeignKeyConstraint = "user_links_oauth_access_token_key_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_oauth_access_token_key_id_fkey FOREIGN KEY (oauth_access_token_key_id) REFERENCES dbcrypt_keys(active_key_digest); ForeignKeyUserLinksOauthRefreshTokenKeyID ForeignKeyConstraint = "user_links_oauth_refresh_token_key_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_oauth_refresh_token_key_id_fkey FOREIGN KEY (oauth_refresh_token_key_id) REFERENCES dbcrypt_keys(active_key_digest); ForeignKeyUserLinksUserID ForeignKeyConstraint = "user_links_user_id_fkey" // ALTER TABLE ONLY user_links ADD CONSTRAINT user_links_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; + ForeignKeyWorkspaceAgentLogSourcesWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_log_sources_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_log_sources ADD CONSTRAINT workspace_agent_log_sources_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentMetadataWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_metadata_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_metadata ADD CONSTRAINT workspace_agent_metadata_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentScriptsWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_scripts_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_scripts ADD CONSTRAINT workspace_agent_scripts_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentStartupLogsAgentID ForeignKeyConstraint = "workspace_agent_startup_logs_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; diff --git a/coderd/database/migrations/000156_workspace_agent_script.up.sql b/coderd/database/migrations/000156_workspace_agent_script.up.sql index 6c7a202c7eeda..c073318c5b804 100644 --- a/coderd/database/migrations/000156_workspace_agent_script.up.sql +++ b/coderd/database/migrations/000156_workspace_agent_script.up.sql @@ -1,6 +1,6 @@ BEGIN; CREATE TABLE workspace_agent_log_sources ( - workspace_agent_id uuid NOT NULL, + workspace_agent_id uuid NOT NULL REFERENCES workspace_agents(id) ON DELETE CASCADE, id uuid NOT NULL, created_at timestamptz NOT NULL, display_name varchar(127) NOT NULL, diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 131d9d4d5b011..0c6afed364156 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1438,7 +1438,8 @@ func convertWorkspaceAgentMetadataDesc(mds []database.WorkspaceAgentMetadatum) [ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordinator, dbAgent database.WorkspaceAgent, apps []codersdk.WorkspaceApp, scripts []codersdk.WorkspaceAgentScript, logSources []codersdk.WorkspaceAgentLogSource, - agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string) (codersdk.WorkspaceAgent, error) { + agentInactiveDisconnectTimeout time.Duration, agentFallbackTroubleshootingURL string, +) (codersdk.WorkspaceAgent, error) { var envs map[string]string if dbAgent.EnvironmentVariables.Valid { err := json.Unmarshal(dbAgent.EnvironmentVariables.RawMessage, &envs) @@ -1455,6 +1456,17 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin subsystems[i] = codersdk.AgentSubsystem(subsystem) } + legacyStartupScriptBehavior := codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking + for _, script := range scripts { + if !script.RunOnStart { + continue + } + if !script.StartBlocksLogin { + continue + } + legacyStartupScriptBehavior = codersdk.WorkspaceAgentStartupScriptBehaviorBlocking + } + workspaceAgent := codersdk.WorkspaceAgent{ ID: dbAgent.ID, CreatedAt: dbAgent.CreatedAt, @@ -1465,7 +1477,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin Architecture: dbAgent.Architecture, OperatingSystem: dbAgent.OperatingSystem, Scripts: scripts, - StartupScriptBehavior: codersdk.WorkspaceAgentStartupScriptBehaviorNonBlocking, + StartupScriptBehavior: legacyStartupScriptBehavior, LogsLength: dbAgent.LogsLength, LogsOverflowed: dbAgent.LogsOverflowed, LogSources: logSources, From 39fb9d3b6e3ad156216fecd8e7f1c93c081d978f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 21 Sep 2023 13:45:12 +0000 Subject: [PATCH 35/59] Fix context --- agent/agent.go | 2 +- agent/agentscripts/agentscripts.go | 46 ++++++++++++---------- agent/agentscripts/agentscripts_other.go | 11 ++++++ agent/agentscripts/agentscripts_test.go | 4 +- agent/agentscripts/agentscripts_windows.go | 7 ++++ 5 files changed, 46 insertions(+), 24 deletions(-) create mode 100644 agent/agentscripts/agentscripts_other.go create mode 100644 agent/agentscripts/agentscripts_windows.go diff --git a/agent/agent.go b/agent/agent.go index f5995b2935e76..d5045f499b228 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -664,7 +664,7 @@ func (a *agent) run(ctx context.Context) error { } } - err = a.scriptRunner.Init(ctx, manifest.Scripts) + err = a.scriptRunner.Init(manifest.Scripts) if err != nil { return xerrors.Errorf("init script runner: %w", err) } diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 6f82e3208b177..8850bfa156bbe 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -9,6 +9,7 @@ import ( "path/filepath" "sync" "sync/atomic" + "syscall" "time" "github.com/robfig/cron/v3" @@ -40,34 +41,39 @@ type Options struct { // New creates a runner for the provided scripts. func New(opts Options) *Runner { + cronCtx, cronCtxCancel := context.WithCancel(context.Background()) return &Runner{ - Options: opts, - cron: cron.New(cron.WithParser(parser)), - closed: make(chan struct{}), + Options: opts, + cronCtx: cronCtx, + cronCtxCancel: cronCtxCancel, + cron: cron.New(cron.WithParser(parser)), + closed: make(chan struct{}), } } type Runner struct { Options - cmdCloseWait sync.WaitGroup - closed chan struct{} - closeMutex sync.Mutex - cron *cron.Cron - initialized atomic.Bool - scripts []codersdk.WorkspaceAgentScript + cronCtx context.Context + cronCtxCancel context.CancelFunc + cmdCloseWait sync.WaitGroup + closed chan struct{} + closeMutex sync.Mutex + cron *cron.Cron + initialized atomic.Bool + scripts []codersdk.WorkspaceAgentScript } // Init initializes the runner with the provided scripts. // It also schedules any scripts that have a schedule. // This function must be called before Execute. -func (r *Runner) Init(ctx context.Context, scripts []codersdk.WorkspaceAgentScript) error { +func (r *Runner) Init(scripts []codersdk.WorkspaceAgentScript) error { if r.initialized.Load() { return xerrors.New("init: already initialized") } r.initialized.Store(true) r.scripts = scripts - r.Logger.Info(ctx, "initializing agent scripts", slog.F("script_count", len(scripts)), slog.F("log_dir", r.LogDir)) + r.Logger.Info(r.cronCtx, "initializing agent scripts", slog.F("script_count", len(scripts)), slog.F("log_dir", r.LogDir)) for _, script := range scripts { if script.Cron == "" { @@ -75,7 +81,7 @@ func (r *Runner) Init(ctx context.Context, scripts []codersdk.WorkspaceAgentScri } script := script _, err := r.cron.AddFunc(script.Cron, func() { - err := r.run(ctx, script) + err := r.run(r.cronCtx, script) if err != nil { r.Logger.Warn(context.Background(), "run agent script on schedule", slog.Error(err)) } @@ -144,19 +150,16 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) } }() - var cmd *exec.Cmd - if script.TimeoutSeconds > 0 { - var cancel context.CancelFunc - // Add a buffer to forcefully kill with the context. - ctx, cancel = context.WithTimeout(ctx, script.TimeoutSeconds+(3*time.Second)) - defer cancel() - } - cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Script, nil) if err != nil { return xerrors.Errorf("%s script: create command: %w", logPath, err) } - cmd = cmdPty.AsExec() + cmd := cmdPty.AsExec() + cmd.SysProcAttr = cmdSysProcAttr() + cmd.WaitDelay = script.TimeoutSeconds + (10 * time.Second) + cmd.Cancel = func() error { + return syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) + } send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, r.PatchLogs, logger) // If ctx is canceled here (or in a writer below), we may be @@ -236,6 +239,7 @@ func (r *Runner) Close() error { return nil } close(r.closed) + r.cronCtxCancel() r.cron.Stop() r.cmdCloseWait.Wait() return nil diff --git a/agent/agentscripts/agentscripts_other.go b/agent/agentscripts/agentscripts_other.go new file mode 100644 index 0000000000000..b460f320e77ac --- /dev/null +++ b/agent/agentscripts/agentscripts_other.go @@ -0,0 +1,11 @@ +//go:build !windows + +package agentscripts + +import "syscall" + +func cmdSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{ + Setsid: true, + } +} diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index 11205385164d0..c056adb3b4f6e 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -30,7 +30,7 @@ func TestExecuteBasic(t *testing.T) { return nil }) defer runner.Close() - err := runner.Init(context.Background(), []codersdk.WorkspaceAgentScript{{ + err := runner.Init([]codersdk.WorkspaceAgentScript{{ Script: "echo hello", }}) require.NoError(t, err) @@ -45,7 +45,7 @@ func TestTimeout(t *testing.T) { t.Parallel() runner := setup(t, nil) defer runner.Close() - err := runner.Init(context.Background(), []codersdk.WorkspaceAgentScript{{ + err := runner.Init([]codersdk.WorkspaceAgentScript{{ Script: "sleep 3", TimeoutSeconds: time.Nanosecond, }}) diff --git a/agent/agentscripts/agentscripts_windows.go b/agent/agentscripts/agentscripts_windows.go new file mode 100644 index 0000000000000..9002388c9108f --- /dev/null +++ b/agent/agentscripts/agentscripts_windows.go @@ -0,0 +1,7 @@ +package agentscripts + +import "syscall" + +func cmdSysProcAttr() *syscall.SysProcAttr { + return &syscall.SysProcAttr{} +} From c4d3cb8a950d8057723c3ae2fa64ba805f6bcd16 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 21 Sep 2023 14:10:49 +0000 Subject: [PATCH 36/59] Fix cancel --- agent/agentscripts/agentscripts.go | 5 +---- agent/agentscripts/agentscripts_other.go | 11 ++++++++++- agent/agentscripts/agentscripts_windows.go | 11 ++++++++++- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 8850bfa156bbe..87e928384362a 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -9,7 +9,6 @@ import ( "path/filepath" "sync" "sync/atomic" - "syscall" "time" "github.com/robfig/cron/v3" @@ -157,9 +156,7 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) cmd := cmdPty.AsExec() cmd.SysProcAttr = cmdSysProcAttr() cmd.WaitDelay = script.TimeoutSeconds + (10 * time.Second) - cmd.Cancel = func() error { - return syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) - } + cmd.Cancel = cmdCancel(cmd) send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, r.PatchLogs, logger) // If ctx is canceled here (or in a writer below), we may be diff --git a/agent/agentscripts/agentscripts_other.go b/agent/agentscripts/agentscripts_other.go index b460f320e77ac..2ff8b22391317 100644 --- a/agent/agentscripts/agentscripts_other.go +++ b/agent/agentscripts/agentscripts_other.go @@ -2,10 +2,19 @@ package agentscripts -import "syscall" +import ( + "os/exec" + "syscall" +) func cmdSysProcAttr() *syscall.SysProcAttr { return &syscall.SysProcAttr{ Setsid: true, } } + +func cmdCancel(cmd *exec.Cmd) func() error { + return func() error { + return syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) + } +} diff --git a/agent/agentscripts/agentscripts_windows.go b/agent/agentscripts/agentscripts_windows.go index 9002388c9108f..3587a2ad29324 100644 --- a/agent/agentscripts/agentscripts_windows.go +++ b/agent/agentscripts/agentscripts_windows.go @@ -1,7 +1,16 @@ package agentscripts -import "syscall" +import ( + "os/exec" + "syscall" +) func cmdSysProcAttr() *syscall.SysProcAttr { return &syscall.SysProcAttr{} } + +func cmdCancel(cmd *exec.Cmd) func() error { + return func() error { + return nil + } +} From 4c17a8bbfe937b5b405228475fdadfa65883ebe0 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 21 Sep 2023 14:35:52 +0000 Subject: [PATCH 37/59] Fix SQL tests --- coderd/database/querier_test.go | 5 +- site/e2e/provisionerGenerated.ts | 80 +++++++++++++++++++++++--------- 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/coderd/database/querier_test.go b/coderd/database/querier_test.go index 6bb782c851568..97e5068e3689c 100644 --- a/coderd/database/querier_test.go +++ b/coderd/database/querier_test.go @@ -112,8 +112,9 @@ func TestInsertWorkspaceAgentLogs(t *testing.T) { agent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{ ResourceID: resource.ID, }) - source := dbgen.WorkspaceAgentLogSource(t, db, database.WorkspaceAgentLogSource{}) - + source := dbgen.WorkspaceAgentLogSource(t, db, database.WorkspaceAgentLogSource{ + WorkspaceAgentID: agent.ID, + }) logs, err := db.InsertWorkspaceAgentLogs(ctx, database.InsertWorkspaceAgentLogsParams{ AgentID: agent.ID, CreatedAt: dbtime.Now(), diff --git a/site/e2e/provisionerGenerated.ts b/site/e2e/provisionerGenerated.ts index f22288c8e4bd2..abd81ad7aacc4 100644 --- a/site/e2e/provisionerGenerated.ts +++ b/site/e2e/provisionerGenerated.ts @@ -104,7 +104,7 @@ export interface Agent { id: string; name: string; env: { [key: string]: string }; - startupScript: string; + /** Field 4 was startup_script, now removed. */ operatingSystem: string; architecture: string; directory: string; @@ -114,13 +114,14 @@ export interface Agent { connectionTimeoutSeconds: number; troubleshootingUrl: string; motdFile: string; - /** Field 14 was bool login_before_ready = 14, now removed. */ - startupScriptTimeoutSeconds: number; - shutdownScript: string; - shutdownScriptTimeoutSeconds: number; + /** + * Field 14 was bool login_before_ready = 14, now removed. + * Field 15, 16, 17 were related to scripts, which are now removed. + */ metadata: Agent_Metadata[]; - startupScriptBehavior: string; + /** Field 19 was startup_script_behavior, now removed. */ displayApps: DisplayApps | undefined; + scripts: Script[]; } export interface Agent_Metadata { @@ -144,6 +145,19 @@ export interface DisplayApps { portForwardingHelper: boolean; } +/** Script represents a script to be run on the workspace. */ +export interface Script { + displayName: string; + icon: string; + script: string; + cron: string; + startBlocksLogin: boolean; + runOnStart: boolean; + runOnStop: boolean; + timeoutSeconds: number; + logPath: string; +} + /** App represents a dev-accessible application on the workspace. */ export interface App { /** @@ -470,9 +484,6 @@ export const Agent = { writer.uint32(26).fork(), ).ldelim(); }); - if (message.startupScript !== "") { - writer.uint32(34).string(message.startupScript); - } if (message.operatingSystem !== "") { writer.uint32(42).string(message.operatingSystem); } @@ -500,27 +511,18 @@ export const Agent = { if (message.motdFile !== "") { writer.uint32(106).string(message.motdFile); } - if (message.startupScriptTimeoutSeconds !== 0) { - writer.uint32(120).int32(message.startupScriptTimeoutSeconds); - } - if (message.shutdownScript !== "") { - writer.uint32(130).string(message.shutdownScript); - } - if (message.shutdownScriptTimeoutSeconds !== 0) { - writer.uint32(136).int32(message.shutdownScriptTimeoutSeconds); - } for (const v of message.metadata) { Agent_Metadata.encode(v!, writer.uint32(146).fork()).ldelim(); } - if (message.startupScriptBehavior !== "") { - writer.uint32(154).string(message.startupScriptBehavior); - } if (message.displayApps !== undefined) { DisplayApps.encode( message.displayApps, writer.uint32(162).fork(), ).ldelim(); } + for (const v of message.scripts) { + Script.encode(v!, writer.uint32(170).fork()).ldelim(); + } return writer; }, }; @@ -588,6 +590,42 @@ export const DisplayApps = { }, }; +export const Script = { + encode( + message: Script, + writer: _m0.Writer = _m0.Writer.create(), + ): _m0.Writer { + if (message.displayName !== "") { + writer.uint32(10).string(message.displayName); + } + if (message.icon !== "") { + writer.uint32(18).string(message.icon); + } + if (message.script !== "") { + writer.uint32(26).string(message.script); + } + if (message.cron !== "") { + writer.uint32(34).string(message.cron); + } + if (message.startBlocksLogin === true) { + writer.uint32(40).bool(message.startBlocksLogin); + } + if (message.runOnStart === true) { + writer.uint32(48).bool(message.runOnStart); + } + if (message.runOnStop === true) { + writer.uint32(56).bool(message.runOnStop); + } + if (message.timeoutSeconds !== 0) { + writer.uint32(64).int32(message.timeoutSeconds); + } + if (message.logPath !== "") { + writer.uint32(74).string(message.logPath); + } + return writer; + }, +}; + export const App = { encode(message: App, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { if (message.slug !== "") { From 9591e341d2fd51d6015cf85ba086d0e0bc6f6cea Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 21 Sep 2023 14:46:44 +0000 Subject: [PATCH 38/59] Fix e2e tests --- coderd/database/queries.sql.go | 226 ++++++++++++++++----------------- site/e2e/helpers.ts | 1 + 2 files changed, 114 insertions(+), 113 deletions(-) diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 0263558e221fc..7623c466f0bf5 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -9329,119 +9329,6 @@ func (q *sqlQuerier) InsertWorkspaceResourceMetadata(ctx context.Context, arg In return items, nil } -const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many -SELECT workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) -` - -func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { - rows, err := q.db.QueryContext(ctx, getWorkspaceAgentScriptsByAgentIDs, pq.Array(ids)) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WorkspaceAgentScript - for rows.Next() { - var i WorkspaceAgentScript - if err := rows.Scan( - &i.WorkspaceAgentID, - &i.LogSourceID, - &i.LogPath, - &i.CreatedAt, - &i.Script, - &i.Cron, - &i.StartBlocksLogin, - &i.RunOnStart, - &i.RunOnStop, - &i.TimeoutSeconds, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many -INSERT INTO - workspace_agent_scripts (workspace_agent_id, created_at, log_source_id, log_path, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) -SELECT - $1 :: uuid AS workspace_agent_id, - $2 :: timestamptz AS created_at, - unnest($3 :: uuid [ ]) AS log_source_id, - unnest($4 :: text [ ]) AS log_path, - unnest($5 :: text [ ]) AS script, - unnest($6 :: text [ ]) AS cron, - unnest($7 :: boolean [ ]) AS start_blocks_login, - unnest($8 :: boolean [ ]) AS run_on_start, - unnest($9 :: boolean [ ]) AS run_on_stop, - unnest($10 :: integer [ ]) AS timeout_seconds -RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.script, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout_seconds -` - -type InsertWorkspaceAgentScriptsParams struct { - WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` - LogPath []string `db:"log_path" json:"log_path"` - Script []string `db:"script" json:"script"` - Cron []string `db:"cron" json:"cron"` - StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` - RunOnStart []bool `db:"run_on_start" json:"run_on_start"` - RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"` - TimeoutSeconds []int32 `db:"timeout_seconds" json:"timeout_seconds"` -} - -func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) { - rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentScripts, - arg.WorkspaceAgentID, - arg.CreatedAt, - pq.Array(arg.LogSourceID), - pq.Array(arg.LogPath), - pq.Array(arg.Script), - pq.Array(arg.Cron), - pq.Array(arg.StartBlocksLogin), - pq.Array(arg.RunOnStart), - pq.Array(arg.RunOnStop), - pq.Array(arg.TimeoutSeconds), - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []WorkspaceAgentScript - for rows.Next() { - var i WorkspaceAgentScript - if err := rows.Scan( - &i.WorkspaceAgentID, - &i.LogSourceID, - &i.LogPath, - &i.CreatedAt, - &i.Script, - &i.Cron, - &i.StartBlocksLogin, - &i.RunOnStart, - &i.RunOnStop, - &i.TimeoutSeconds, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Close(); err != nil { - return nil, err - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const getDeploymentWorkspaceStats = `-- name: GetDeploymentWorkspaceStats :one WITH workspaces_with_jobs AS ( SELECT @@ -10390,3 +10277,116 @@ func (q *sqlQuerier) UpdateWorkspacesDormantDeletingAtByTemplateID(ctx context.C _, err := q.db.ExecContext(ctx, updateWorkspacesDormantDeletingAtByTemplateID, arg.TimeTilDormantAutodeleteMs, arg.DormantAt, arg.TemplateID) return err } + +const getWorkspaceAgentScriptsByAgentIDs = `-- name: GetWorkspaceAgentScriptsByAgentIDs :many +SELECT workspace_agent_id, log_source_id, log_path, created_at, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +` + +func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { + rows, err := q.db.QueryContext(ctx, getWorkspaceAgentScriptsByAgentIDs, pq.Array(ids)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentScript + for rows.Next() { + var i WorkspaceAgentScript + if err := rows.Scan( + &i.WorkspaceAgentID, + &i.LogSourceID, + &i.LogPath, + &i.CreatedAt, + &i.Script, + &i.Cron, + &i.StartBlocksLogin, + &i.RunOnStart, + &i.RunOnStop, + &i.TimeoutSeconds, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const insertWorkspaceAgentScripts = `-- name: InsertWorkspaceAgentScripts :many +INSERT INTO + workspace_agent_scripts (workspace_agent_id, created_at, log_source_id, log_path, script, cron, start_blocks_login, run_on_start, run_on_stop, timeout_seconds) +SELECT + $1 :: uuid AS workspace_agent_id, + $2 :: timestamptz AS created_at, + unnest($3 :: uuid [ ]) AS log_source_id, + unnest($4 :: text [ ]) AS log_path, + unnest($5 :: text [ ]) AS script, + unnest($6 :: text [ ]) AS cron, + unnest($7 :: boolean [ ]) AS start_blocks_login, + unnest($8 :: boolean [ ]) AS run_on_start, + unnest($9 :: boolean [ ]) AS run_on_stop, + unnest($10 :: integer [ ]) AS timeout_seconds +RETURNING workspace_agent_scripts.workspace_agent_id, workspace_agent_scripts.log_source_id, workspace_agent_scripts.log_path, workspace_agent_scripts.created_at, workspace_agent_scripts.script, workspace_agent_scripts.cron, workspace_agent_scripts.start_blocks_login, workspace_agent_scripts.run_on_start, workspace_agent_scripts.run_on_stop, workspace_agent_scripts.timeout_seconds +` + +type InsertWorkspaceAgentScriptsParams struct { + WorkspaceAgentID uuid.UUID `db:"workspace_agent_id" json:"workspace_agent_id"` + CreatedAt time.Time `db:"created_at" json:"created_at"` + LogSourceID []uuid.UUID `db:"log_source_id" json:"log_source_id"` + LogPath []string `db:"log_path" json:"log_path"` + Script []string `db:"script" json:"script"` + Cron []string `db:"cron" json:"cron"` + StartBlocksLogin []bool `db:"start_blocks_login" json:"start_blocks_login"` + RunOnStart []bool `db:"run_on_start" json:"run_on_start"` + RunOnStop []bool `db:"run_on_stop" json:"run_on_stop"` + TimeoutSeconds []int32 `db:"timeout_seconds" json:"timeout_seconds"` +} + +func (q *sqlQuerier) InsertWorkspaceAgentScripts(ctx context.Context, arg InsertWorkspaceAgentScriptsParams) ([]WorkspaceAgentScript, error) { + rows, err := q.db.QueryContext(ctx, insertWorkspaceAgentScripts, + arg.WorkspaceAgentID, + arg.CreatedAt, + pq.Array(arg.LogSourceID), + pq.Array(arg.LogPath), + pq.Array(arg.Script), + pq.Array(arg.Cron), + pq.Array(arg.StartBlocksLogin), + pq.Array(arg.RunOnStart), + pq.Array(arg.RunOnStop), + pq.Array(arg.TimeoutSeconds), + ) + if err != nil { + return nil, err + } + defer rows.Close() + var items []WorkspaceAgentScript + for rows.Next() { + var i WorkspaceAgentScript + if err := rows.Scan( + &i.WorkspaceAgentID, + &i.LogSourceID, + &i.LogPath, + &i.CreatedAt, + &i.Script, + &i.Cron, + &i.StartBlocksLogin, + &i.RunOnStart, + &i.RunOnStop, + &i.TimeoutSeconds, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index f7e20e68f18bc..5b2490dd71533 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -473,6 +473,7 @@ const createTemplateVersionTar = async ( env: {}, id: randomUUID(), metadata: [], + scripts: [], motdFile: "", name: "dev", operatingSystem: "linux", From a4a02704ac554ef2613bacc547de7b3999edb789 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Thu, 21 Sep 2023 17:24:05 +0000 Subject: [PATCH 39/59] Interrupt on Windows --- agent/agentscripts/agentscripts_windows.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/agent/agentscripts/agentscripts_windows.go b/agent/agentscripts/agentscripts_windows.go index 3587a2ad29324..cda1b3fcc39e1 100644 --- a/agent/agentscripts/agentscripts_windows.go +++ b/agent/agentscripts/agentscripts_windows.go @@ -1,6 +1,7 @@ package agentscripts import ( + "os" "os/exec" "syscall" ) @@ -11,6 +12,6 @@ func cmdSysProcAttr() *syscall.SysProcAttr { func cmdCancel(cmd *exec.Cmd) func() error { return func() error { - return nil + return cmd.Process.Signal(os.Interrupt) } } From dd5abdf222aa571e47b24570f34ddd5255aeb465 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 24 Sep 2023 20:17:07 +0000 Subject: [PATCH 40/59] Fix agent leaking script process --- agent/agent_test.go | 36 ++++++++++++------------- agent/agentscripts/agentscripts.go | 16 +++++++---- agent/agentscripts/agentscripts_test.go | 4 +-- coderd/workspaceagents.go | 2 +- codersdk/workspaceagents.go | 2 +- 5 files changed, 33 insertions(+), 27 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index ca1d5691b0ec7..55edd9ba8dcd0 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1214,9 +1214,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - TimeoutSeconds: time.Nanosecond, - RunOnStart: true, + Script: "sleep 3", + Timeout: time.Nanosecond, + RunOnStart: true, }}, }, 0) @@ -1239,9 +1239,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "false", - TimeoutSeconds: 30 * time.Second, - RunOnStart: true, + Script: "false", + Timeout: 30 * time.Second, + RunOnStart: true, }}, }, 0) @@ -1264,9 +1264,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "true", - TimeoutSeconds: 30 * time.Second, - RunOnStart: true, + Script: "true", + Timeout: 30 * time.Second, + RunOnStart: true, }}, }, 0) @@ -1289,9 +1289,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - TimeoutSeconds: 30 * time.Second, - RunOnStop: true, + Script: "sleep 3", + Timeout: 30 * time.Second, + RunOnStop: true, }}, }, 0) @@ -1330,9 +1330,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - TimeoutSeconds: time.Nanosecond, - RunOnStop: true, + Script: "sleep 3", + Timeout: time.Nanosecond, + RunOnStop: true, }}, }, 0) @@ -1372,9 +1372,9 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ - Script: "false", - TimeoutSeconds: 30 * time.Second, - RunOnStop: true, + Script: "false", + Timeout: 30 * time.Second, + RunOnStop: true, }}, }, 0) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 87e928384362a..9d8738da70fae 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -130,7 +130,7 @@ func (r *Runner) Execute(ctx context.Context, filter func(script codersdk.Worksp func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) error { logPath := script.LogPath if logPath == "" { - logPath = fmt.Sprintf("coder-%s-script.log", script.LogSourceID) + logPath = fmt.Sprintf("coder-script-%s.log", script.LogSourceID) } if !filepath.IsAbs(logPath) { logPath = filepath.Join(r.LogDir, logPath) @@ -149,13 +149,19 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) } }() + var cmd *exec.Cmd + if script.Timeout > 0 { + var ctxCancel context.CancelFunc + ctx, ctxCancel = context.WithTimeout(ctx, script.Timeout) + defer ctxCancel() + } cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Script, nil) if err != nil { return xerrors.Errorf("%s script: create command: %w", logPath, err) } - cmd := cmdPty.AsExec() + cmd = cmdPty.AsExec() cmd.SysProcAttr = cmdSysProcAttr() - cmd.WaitDelay = script.TimeoutSeconds + (10 * time.Second) + cmd.WaitDelay = 10 * time.Second cmd.Cancel = cmdCancel(cmd) send, flushAndClose := agentsdk.LogsSender(script.LogSourceID, r.PatchLogs, logger) @@ -200,9 +206,9 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) // timeout stores whether the process timed out then was gracefully killed. var timeout chan struct{} - if script.TimeoutSeconds > 0 { + if script.Timeout > 0 { timeout = make(chan struct{}) - timer := time.AfterFunc(script.TimeoutSeconds, func() { + timer := time.AfterFunc(script.Timeout, func() { close(timeout) err := cmd.Process.Signal(os.Interrupt) if err != nil { diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index c056adb3b4f6e..4868a1f239838 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -46,8 +46,8 @@ func TestTimeout(t *testing.T) { runner := setup(t, nil) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", - TimeoutSeconds: time.Nanosecond, + Script: "sleep 3", + Timeout: time.Nanosecond, }}) require.NoError(t, err) require.ErrorIs(t, runner.Execute(context.Background(), nil), agentscripts.ErrTimeout) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 25990483b656e..c506c83e20666 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -1459,7 +1459,7 @@ func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.Worksp RunOnStart: dbScript.RunOnStart, RunOnStop: dbScript.RunOnStop, StartBlocksLogin: dbScript.StartBlocksLogin, - TimeoutSeconds: time.Duration(dbScript.TimeoutSeconds) * time.Second, + Timeout: time.Duration(dbScript.TimeoutSeconds) * time.Second, }) } return scripts diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 9e4e99369db6a..a62fd1a2f0bad 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -199,7 +199,7 @@ type WorkspaceAgentScript struct { RunOnStart bool `json:"run_on_start"` RunOnStop bool `json:"run_on_stop"` StartBlocksLogin bool `json:"start_blocks_login"` - TimeoutSeconds time.Duration `json:"timeout_seconds"` + Timeout time.Duration `json:"timeout"` } type WorkspaceAgentHealth struct { From 9e85d7b66e93848699559f5d6a6c0ed8f0213e2c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 24 Sep 2023 20:37:34 +0000 Subject: [PATCH 41/59] Fix migrations --- ...ent_script.down.sql => 000157_workspace_agent_script.down.sql} | 0 ...e_agent_script.up.sql => 000157_workspace_agent_script.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000156_workspace_agent_script.down.sql => 000157_workspace_agent_script.down.sql} (100%) rename coderd/database/migrations/{000156_workspace_agent_script.up.sql => 000157_workspace_agent_script.up.sql} (100%) diff --git a/coderd/database/migrations/000156_workspace_agent_script.down.sql b/coderd/database/migrations/000157_workspace_agent_script.down.sql similarity index 100% rename from coderd/database/migrations/000156_workspace_agent_script.down.sql rename to coderd/database/migrations/000157_workspace_agent_script.down.sql diff --git a/coderd/database/migrations/000156_workspace_agent_script.up.sql b/coderd/database/migrations/000157_workspace_agent_script.up.sql similarity index 100% rename from coderd/database/migrations/000156_workspace_agent_script.up.sql rename to coderd/database/migrations/000157_workspace_agent_script.up.sql From 9513acf947bdc43120120b4b1c11031879ef2f4c Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Sun, 24 Sep 2023 21:15:14 +0000 Subject: [PATCH 42/59] Fix stories --- site/src/components/Resources/AgentRow.stories.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/components/Resources/AgentRow.stories.tsx b/site/src/components/Resources/AgentRow.stories.tsx index 21c470c552c75..ba13f2e35af88 100644 --- a/site/src/components/Resources/AgentRow.stories.tsx +++ b/site/src/components/Resources/AgentRow.stories.tsx @@ -95,6 +95,7 @@ const storybookLogs: LineWithID[] = [ level: "info", output: line, time: "", + source_id: "", })); const meta: Meta = { From e8b1e43b03ac554cd46ccf270276c902a6db8b3f Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 13:48:11 +0000 Subject: [PATCH 43/59] Fix duplicate logs appearing --- site/src/components/Resources/AgentRow.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/components/Resources/AgentRow.tsx index 15cc9f623c9e9..32f5f3e8c8091 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/components/Resources/AgentRow.tsx @@ -84,13 +84,13 @@ export const AgentRow: FC = ({ showApps && ((agent.status === "connected" && hasAppsToDisplay) || agent.status === "connecting"); - const logSourceByID = useMemo(() => { - const sources: { [id: string]: WorkspaceAgentLogSource } = {}; - for (const source of agent.log_sources) { - sources[source.id] = source; - } - return sources; - }, [agent.log_sources]); + const logSourceByID = useMemo(() => { + const sources: { [id: string]: WorkspaceAgentLogSource } = {}; + for (const source of agent.log_sources) { + sources[source.id] = source; + } + return sources; + }, [agent.log_sources]); const hasStartupFeatures = Boolean(agent.logs_length); const { proxy } = useProxy(); const [showLogs, setShowLogs] = useState( @@ -427,6 +427,7 @@ const useAgentLogs = ( useEffect(() => { if (!enabled) { socket.current?.close(); + setLogs([]); return; } From b837aaca3f9ac1b818c370fa460bb351b9ee8ba2 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 13:54:56 +0000 Subject: [PATCH 44/59] Gen --- coderd/apidoc/docs.go | 2 +- coderd/apidoc/swagger.json | 2 +- docs/api/agents.md | 4 ++-- docs/api/builds.md | 16 ++++++++-------- docs/api/schemas.md | 16 ++++++++-------- docs/api/templates.md | 8 ++++---- docs/api/workspaces.md | 10 +++++----- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 8f53d4b01e88a..96be09c86c324 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -10925,7 +10925,7 @@ const docTemplate = `{ "start_blocks_login": { "type": "boolean" }, - "timeout_seconds": { + "timeout": { "type": "integer" } } diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index f40eea4a6a8db..1d881c3868a3e 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -9929,7 +9929,7 @@ "start_blocks_login": { "type": "boolean" }, - "timeout_seconds": { + "timeout": { "type": "integer" } } diff --git a/docs/api/agents.md b/docs/api/agents.md index 536d7d2d5a7c7..326f523415f41 100644 --- a/docs/api/agents.md +++ b/docs/api/agents.md @@ -479,7 +479,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/me/manifest \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "vscode_port_proxy_uri": "string" @@ -725,7 +725,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", diff --git a/docs/api/builds.md b/docs/api/builds.md index 1db2a8da526b6..a1e8f25a6e69d 100644 --- a/docs/api/builds.md +++ b/docs/api/builds.md @@ -132,7 +132,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -312,7 +312,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -631,7 +631,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -734,7 +734,7 @@ Status Code **200** | `»»» run_on_stop` | boolean | false | | | | `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | -| `»»» timeout_seconds` | integer | false | | | +| `»»» timeout` | integer | false | | | | `»» started_at` | string(date-time) | false | | | | `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | | `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | @@ -918,7 +918,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -1103,7 +1103,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -1242,7 +1242,7 @@ Status Code **200** | `»»»» run_on_stop` | boolean | false | | | | `»»»» script` | string | false | | | | `»»»» start_blocks_login` | boolean | false | | | -| `»»»» timeout_seconds` | integer | false | | | +| `»»»» timeout` | integer | false | | | | `»»» started_at` | string(date-time) | false | | | | `»»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | | `»»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | @@ -1479,7 +1479,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", diff --git a/docs/api/schemas.md b/docs/api/schemas.md index 0dca74808bcb8..176c3bd4cac37 100644 --- a/docs/api/schemas.md +++ b/docs/api/schemas.md @@ -286,7 +286,7 @@ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "vscode_port_proxy_uri": "string" @@ -5502,7 +5502,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -5662,7 +5662,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -5942,7 +5942,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ``` @@ -5957,7 +5957,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `run_on_stop` | boolean | false | | | | `script` | string | false | | | | `start_blocks_login` | boolean | false | | | -| `timeout_seconds` | integer | false | | | +| `timeout` | integer | false | | | ## codersdk.WorkspaceAgentStartupScriptBehavior @@ -6181,7 +6181,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -6510,7 +6510,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -6741,7 +6741,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", diff --git a/docs/api/templates.md b/docs/api/templates.md index 6187778e38ba5..526fc193fa684 100644 --- a/docs/api/templates.md +++ b/docs/api/templates.md @@ -1643,7 +1643,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -1746,7 +1746,7 @@ Status Code **200** | `»»» run_on_stop` | boolean | false | | | | `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | -| `»»» timeout_seconds` | integer | false | | | +| `»»» timeout` | integer | false | | | | `»» started_at` | string(date-time) | false | | | | `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | | `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | @@ -2064,7 +2064,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -2167,7 +2167,7 @@ Status Code **200** | `»»» run_on_stop` | boolean | false | | | | `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | -| `»»» timeout_seconds` | integer | false | | | +| `»»» timeout` | integer | false | | | | `»» started_at` | string(date-time) | false | | | | `»» startup_script_behavior` | [codersdk.WorkspaceAgentStartupScriptBehavior](schemas.md#codersdkworkspaceagentstartupscriptbehavior) | false | | Startup script behavior is a legacy field that is deprecated in favor of the `coder_script` resource. It's only referenced by old clients. Deprecated: Remove in the future! | | `»» status` | [codersdk.WorkspaceAgentStatus](schemas.md#codersdkworkspaceagentstatus) | false | | | diff --git a/docs/api/workspaces.md b/docs/api/workspaces.md index 3ee5676f3081f..56aaabee03025 100644 --- a/docs/api/workspaces.md +++ b/docs/api/workspaces.md @@ -162,7 +162,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -369,7 +369,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -575,7 +575,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -783,7 +783,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", @@ -1070,7 +1070,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "run_on_stop": true, "script": "string", "start_blocks_login": true, - "timeout_seconds": 0 + "timeout": 0 } ], "started_at": "2019-08-24T14:15:22Z", From f1ff5cc0af75d0a181f5be4401fe1666c0e7765e Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 14:08:05 +0000 Subject: [PATCH 45/59] Fix log location --- provisioner/terraform/resources.go | 2 ++ site/src/components/Resources/AgentRow.tsx | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index 62fb17a643129..a143e6a20fa86 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -83,6 +83,7 @@ type agentScriptAttributes struct { Icon string `mapstructure:"icon"` Script string `mapstructure:"script"` Cron string `mapstructure:"cron"` + LogPath string `mapstructure:"log_path"` StartBlocksLogin bool `mapstructure:"start_blocks_login"` RunOnStart bool `mapstructure:"run_on_start"` RunOnStop bool `mapstructure:"run_on_stop"` @@ -456,6 +457,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error Icon: attrs.Icon, Script: attrs.Script, Cron: attrs.Cron, + LogPath: attrs.LogPath, StartBlocksLogin: attrs.StartBlocksLogin, RunOnStart: attrs.RunOnStart, RunOnStop: attrs.RunOnStop, diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/components/Resources/AgentRow.tsx index 32f5f3e8c8091..7ebc5e0e731ff 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/components/Resources/AgentRow.tsx @@ -299,6 +299,10 @@ export const AgentRow: FC = ({ const log = startupLogs[index]; let sourceIcon: string | undefined = logSourceByID[log.source_id].icon; + if (!sourceIcon) { + // Default to a globe for external. + sourceIcon = "/emojis/1f310.png"; + } if ( index > 0 && logSourceByID[startupLogs[index - 1].source_id].id === From 4ec3a87cb3d681f4e6019ef3d781b6a5b1e40ff3 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 14:20:08 +0000 Subject: [PATCH 46/59] Fix tests --- agent/agent_test.go | 2 +- agent/agentscripts/agentscripts.go | 21 +++++---------------- agent/agentscripts/agentscripts_test.go | 2 +- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index 55edd9ba8dcd0..882ebb5f94a11 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1215,7 +1215,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, _ := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ Script: "sleep 3", - Timeout: time.Nanosecond, + Timeout: time.Millisecond, RunOnStart: true, }}, }, 0) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 9d8738da70fae..848524e1e6482 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -2,6 +2,7 @@ package agentscripts import ( "context" + "errors" "fmt" "io" "os" @@ -204,20 +205,6 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) return xerrors.Errorf("%s script: start command: %w", logPath, err) } - // timeout stores whether the process timed out then was gracefully killed. - var timeout chan struct{} - if script.Timeout > 0 { - timeout = make(chan struct{}) - timer := time.AfterFunc(script.Timeout, func() { - close(timeout) - err := cmd.Process.Signal(os.Interrupt) - if err != nil { - logger.Warn(ctx, "send interrupt signal to script", slog.Error(err)) - } - }) - defer timer.Stop() - } - cmdDone := make(chan error, 1) err = r.trackCommandGoroutine(func() { cmdDone <- cmd.Wait() @@ -226,12 +213,14 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) return xerrors.Errorf("%s script: track command goroutine: %w", logPath, err) } select { - case <-timeout: - err = ErrTimeout case <-ctx.Done(): err = ctx.Err() case err = <-cmdDone: } + if errors.Is(err, context.DeadlineExceeded) { + err = ErrTimeout + } + fmt.Printf("ERROR %+v\n", err) return err } diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index 4868a1f239838..f72f88103a504 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -47,7 +47,7 @@ func TestTimeout(t *testing.T) { defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ Script: "sleep 3", - Timeout: time.Nanosecond, + Timeout: time.Millisecond, }}) require.NoError(t, err) require.ErrorIs(t, runner.Execute(context.Background(), nil), agentscripts.ErrTimeout) From d36ab538b7eb1ca51e002b75b9968d3fa1608ffe Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 14:21:45 +0000 Subject: [PATCH 47/59] Fix tests --- agent/agent_test.go | 2 +- site/src/pages/TerminalPage/TerminalPageAlert.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agent/agent_test.go b/agent/agent_test.go index 882ebb5f94a11..fb801a93e53de 100644 --- a/agent/agent_test.go +++ b/agent/agent_test.go @@ -1331,7 +1331,7 @@ func TestAgent_Lifecycle(t *testing.T) { _, client, _, _, closer := setupAgent(t, agentsdk.Manifest{ Scripts: []codersdk.WorkspaceAgentScript{{ Script: "sleep 3", - Timeout: time.Nanosecond, + Timeout: time.Millisecond, RunOnStop: true, }}, }, 0) diff --git a/site/src/pages/TerminalPage/TerminalPageAlert.tsx b/site/src/pages/TerminalPage/TerminalPageAlert.tsx index 13199a0911366..6c5e3862fb622 100644 --- a/site/src/pages/TerminalPage/TerminalPageAlert.tsx +++ b/site/src/pages/TerminalPage/TerminalPageAlert.tsx @@ -53,7 +53,7 @@ const mapAlertTypeToText: MapAlertTypeToComponent = { severity: "info", children: ( <> - Startup script is still running. You can continue using this terminal, + Startup scripts are still running. You can continue using this terminal, but{" "} - Startup script has completed successfully. The workspace is ready but + Startup scripts have completed successfully. The workspace is ready but this{" "} Date: Mon, 25 Sep 2023 14:51:54 +0000 Subject: [PATCH 48/59] Fix log output --- cli/cliui/agent.go | 17 +++++++++++++---- cli/cliui/agent_test.go | 27 +++++++++++++++------------ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/cli/cliui/agent.go b/cli/cliui/agent.go index c6cc9f413fe54..4efa39dd576e6 100644 --- a/cli/cliui/agent.go +++ b/cli/cliui/agent.go @@ -80,6 +80,10 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO if err != nil { return xerrors.Errorf("fetch: %w", err) } + logSources := map[uuid.UUID]codersdk.WorkspaceAgentLogSource{} + for _, source := range agent.LogSources { + logSources[source.ID] = source + } sw := &stageWriter{w: writer} @@ -123,7 +127,7 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO return nil } - stage := "Running workspace agent startup script" + stage := "Running workspace agent startup scripts" follow := opts.Wait if !follow { stage += " (non-blocking)" @@ -173,7 +177,12 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO return nil } for _, log := range logs { - sw.Log(log.CreatedAt, log.Level, log.Output) + source, hasSource := logSources[log.SourceID] + output := log.Output + if hasSource && source.DisplayName != "" { + output = source.DisplayName + ": " + output + } + sw.Log(log.CreatedAt, log.Level, output) lastLog = log } } @@ -195,13 +204,13 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO case codersdk.WorkspaceAgentLifecycleStartError: sw.Fail(stage, agent.ReadyAt.Sub(*agent.StartedAt)) // Use zero time (omitted) to separate these from the startup logs. - sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: The startup script exited with an error and your workspace may be incomplete.") + sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: A startup script exited with an error and your workspace may be incomplete.") sw.Log(time.Time{}, codersdk.LogLevelWarn, troubleshootingMessage(agent, "https://coder.com/docs/v2/latest/templates#startup-script-exited-with-an-error")) default: switch { case agent.LifecycleState.Starting(): // Use zero time (omitted) to separate these from the startup logs. - sw.Log(time.Time{}, codersdk.LogLevelWarn, "Notice: The startup script is still running and your workspace may be incomplete.") + sw.Log(time.Time{}, codersdk.LogLevelWarn, "Notice: The startup scripts are still running and your workspace may be incomplete.") sw.Log(time.Time{}, codersdk.LogLevelWarn, troubleshootingMessage(agent, "https://coder.com/docs/v2/latest/templates#your-workspace-may-be-incomplete")) // Note: We don't complete or fail the stage here, it's // intentionally left open to indicate this stage didn't diff --git a/cli/cliui/agent_test.go b/cli/cliui/agent_test.go index 6cfba173e8e09..93aa4da4f8eab 100644 --- a/cli/cliui/agent_test.go +++ b/cli/cliui/agent_test.go @@ -52,8 +52,8 @@ func TestAgent(t *testing.T) { want: []string{ "⧗ Waiting for the workspace agent to connect", "✔ Waiting for the workspace agent to connect", - "⧗ Running workspace agent startup script (non-blocking)", - "Notice: The startup script is still running and your workspace may be incomplete.", + "⧗ Running workspace agent startup scripts (non-blocking)", + "Notice: The startup scripts are still running and your workspace may be incomplete.", "For more information and troubleshooting, see", }, }, @@ -86,8 +86,8 @@ func TestAgent(t *testing.T) { "The workspace agent is having trouble connecting, wait for it to connect or restart your workspace.", "For more information and troubleshooting, see", "✔ Waiting for the workspace agent to connect", - "⧗ Running workspace agent startup script (non-blocking)", - "✔ Running workspace agent startup script (non-blocking)", + "⧗ Running workspace agent startup scripts (non-blocking)", + "✔ Running workspace agent startup scripts (non-blocking)", }, }, { @@ -120,7 +120,7 @@ func TestAgent(t *testing.T) { }, }, { - name: "Startup script logs", + name: "Startup Logs", opts: cliui.AgentOptions{ FetchInterval: time.Millisecond, Wait: true, @@ -152,10 +152,10 @@ func TestAgent(t *testing.T) { }, }, want: []string{ - "⧗ Running workspace agent startup script", + "⧗ Running workspace agent startup scripts", "Hello world", "Bye now", - "✔ Running workspace agent startup script", + "✔ Running workspace agent startup scripts", }, }, { @@ -181,10 +181,10 @@ func TestAgent(t *testing.T) { }, }, want: []string{ - "⧗ Running workspace agent startup script", + "⧗ Running workspace agent startup scripts", "Hello world", - "✘ Running workspace agent startup script", - "Warning: The startup script exited with an error and your workspace may be incomplete.", + "✘ Running workspace agent startup scripts", + "Warning: A startup script exited with an error and your workspace may be incomplete.", "For more information and troubleshooting, see", }, }, @@ -229,9 +229,9 @@ func TestAgent(t *testing.T) { }, }, want: []string{ - "⧗ Running workspace agent startup script", + "⧗ Running workspace agent startup scripts", "Hello world", - "✔ Running workspace agent startup script", + "✔ Running workspace agent startup scripts", }, wantErr: true, }, @@ -339,6 +339,9 @@ func TestAgent(t *testing.T) { line := s.Text() t.Log(line) if len(tc.want) == 0 { + for i := 0; i < 5; i++ { + t.Log(line) + } require.Fail(t, "unexpected line", line) } require.Contains(t, line, tc.want[0]) From aa5540bf157ab5b13ed2ef5ed16c80cbcee178c6 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 14:53:51 +0000 Subject: [PATCH 49/59] Show display name in output --- cli/cliui/agent_test.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cli/cliui/agent_test.go b/cli/cliui/agent_test.go index 93aa4da4f8eab..d70923395a0ae 100644 --- a/cli/cliui/agent_test.go +++ b/cli/cliui/agent_test.go @@ -131,10 +131,15 @@ func TestAgent(t *testing.T) { agent.FirstConnectedAt = ptr.Ref(time.Now()) agent.LifecycleState = codersdk.WorkspaceAgentLifecycleStarting agent.StartedAt = ptr.Ref(time.Now()) + agent.LogSources = []codersdk.WorkspaceAgentLogSource{{ + ID: uuid.Nil, + DisplayName: "testing", + }} logs <- []codersdk.WorkspaceAgentLog{ { CreatedAt: time.Now(), Output: "Hello world", + SourceID: uuid.Nil, }, } return nil @@ -153,7 +158,7 @@ func TestAgent(t *testing.T) { }, want: []string{ "⧗ Running workspace agent startup scripts", - "Hello world", + "testing: Hello world", "Bye now", "✔ Running workspace agent startup scripts", }, From f866a92cb9aa76d83156b3a06b37916c7088fb26 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 15:02:27 +0000 Subject: [PATCH 50/59] Fix print --- agent/agentscripts/agentscripts.go | 1 - 1 file changed, 1 deletion(-) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 848524e1e6482..54858fe7e4808 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -220,7 +220,6 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) if errors.Is(err, context.DeadlineExceeded) { err = ErrTimeout } - fmt.Printf("ERROR %+v\n", err) return err } From a99b6dd6f28bdb576d16c1715a770aa9330d07fc Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 15:03:08 +0000 Subject: [PATCH 51/59] Return timeout on start context --- agent/agentscripts/agentscripts.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 54858fe7e4808..471053dd51cf5 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -202,6 +202,9 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) err = cmd.Start() if err != nil { + if errors.Is(err, context.DeadlineExceeded) { + return ErrTimeout + } return xerrors.Errorf("%s script: start command: %w", logPath, err) } From 1865590e562ec576ecd2649c852ab5c865eb2048 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 18:07:27 +0000 Subject: [PATCH 52/59] Gen --- coderd/database/unique_constraint.go | 1 + site/src/api/typesGenerated.ts | 2 +- site/src/testHelpers/entities.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/coderd/database/unique_constraint.go b/coderd/database/unique_constraint.go index 5eca7bfbcc6fb..5be23df0d8bcf 100644 --- a/coderd/database/unique_constraint.go +++ b/coderd/database/unique_constraint.go @@ -33,6 +33,7 @@ const ( UniqueProvisionerJobsPkey UniqueConstraint = "provisioner_jobs_pkey" // ALTER TABLE ONLY provisioner_jobs ADD CONSTRAINT provisioner_jobs_pkey PRIMARY KEY (id); UniqueSiteConfigsKeyKey UniqueConstraint = "site_configs_key_key" // ALTER TABLE ONLY site_configs ADD CONSTRAINT site_configs_key_key UNIQUE (key); UniqueTailnetAgentsPkey UniqueConstraint = "tailnet_agents_pkey" // ALTER TABLE ONLY tailnet_agents ADD CONSTRAINT tailnet_agents_pkey PRIMARY KEY (id, coordinator_id); + UniqueTailnetClientSubscriptionsPkey UniqueConstraint = "tailnet_client_subscriptions_pkey" // ALTER TABLE ONLY tailnet_client_subscriptions ADD CONSTRAINT tailnet_client_subscriptions_pkey PRIMARY KEY (client_id, coordinator_id, agent_id); UniqueTailnetClientsPkey UniqueConstraint = "tailnet_clients_pkey" // ALTER TABLE ONLY tailnet_clients ADD CONSTRAINT tailnet_clients_pkey PRIMARY KEY (id, coordinator_id); UniqueTailnetCoordinatorsPkey UniqueConstraint = "tailnet_coordinators_pkey" // ALTER TABLE ONLY tailnet_coordinators ADD CONSTRAINT tailnet_coordinators_pkey PRIMARY KEY (id); UniqueTemplateVersionParametersTemplateVersionIDNameKey UniqueConstraint = "template_version_parameters_template_version_id_name_key" // ALTER TABLE ONLY template_version_parameters ADD CONSTRAINT template_version_parameters_template_version_id_name_key UNIQUE (template_version_id, name); diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 221b060a10317..7c10879ebe3ff 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -1392,7 +1392,7 @@ export interface WorkspaceAgentScript { readonly run_on_start: boolean; readonly run_on_stop: boolean; readonly start_blocks_login: boolean; - readonly timeout_seconds: number; + readonly timeout: number; } // From codersdk/workspaceapps.go diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 9f3cabd664cec..39fdf710d1930 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -555,7 +555,7 @@ export const MockWorkspaceAgentScript: TypesGen.WorkspaceAgentScript = { run_on_stop: false, script: "echo 'hello world'", start_blocks_login: false, - timeout_seconds: 0, + timeout: 0, }; export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { From 3b26aa0745feff64d9831ae6413bead6937040ee Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 18:08:16 +0000 Subject: [PATCH 53/59] Fix fixture --- ...e_agent_script.up.sql => 000157_workspace_agent_script.up.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/testdata/fixtures/{000156_workspace_agent_script.up.sql => 000157_workspace_agent_script.up.sql} (100%) diff --git a/coderd/database/migrations/testdata/fixtures/000156_workspace_agent_script.up.sql b/coderd/database/migrations/testdata/fixtures/000157_workspace_agent_script.up.sql similarity index 100% rename from coderd/database/migrations/testdata/fixtures/000156_workspace_agent_script.up.sql rename to coderd/database/migrations/testdata/fixtures/000157_workspace_agent_script.up.sql From f2f69bbd0ea3c555fceb32e0e300e3fa05e31fe2 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 19:03:47 +0000 Subject: [PATCH 54/59] Fix the agent status --- site/src/components/Resources/AgentStatus.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/site/src/components/Resources/AgentStatus.tsx b/site/src/components/Resources/AgentStatus.tsx index db5b4f108d0ab..c8179249d1ac5 100644 --- a/site/src/components/Resources/AgentStatus.tsx +++ b/site/src/components/Resources/AgentStatus.tsx @@ -244,6 +244,11 @@ const OffLifecycle: React.FC = () => { const ConnectedStatus: React.FC<{ agent: WorkspaceAgent; }> = ({ agent }) => { + // This is to support legacy agents that do not support + // reporting the lifecycle_state field. + if (agent.scripts.length === 0) { + return ; + } return ( From aa687965c59bf590e441c1e8b302a280da4f9b45 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 20:53:35 +0000 Subject: [PATCH 55/59] Fix startup timeout msg --- agent/agentscripts/agentscripts_test.go | 2 +- cli/cliui/agent.go | 3 +++ cli/cliui/agent_test.go | 25 +++++++++++++++++++++++++ codersdk/workspaceagents.go | 2 +- provisioner/terraform/resources.go | 2 +- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/agent/agentscripts/agentscripts_test.go b/agent/agentscripts/agentscripts_test.go index f72f88103a504..1570e35d59b31 100644 --- a/agent/agentscripts/agentscripts_test.go +++ b/agent/agentscripts/agentscripts_test.go @@ -46,7 +46,7 @@ func TestTimeout(t *testing.T) { runner := setup(t, nil) defer runner.Close() err := runner.Init([]codersdk.WorkspaceAgentScript{{ - Script: "sleep 3", + Script: "sleep infinity", Timeout: time.Millisecond, }}) require.NoError(t, err) diff --git a/cli/cliui/agent.go b/cli/cliui/agent.go index 4efa39dd576e6..42a42f82de983 100644 --- a/cli/cliui/agent.go +++ b/cli/cliui/agent.go @@ -201,6 +201,9 @@ func Agent(ctx context.Context, writer io.Writer, agentID uuid.UUID, opts AgentO switch agent.LifecycleState { case codersdk.WorkspaceAgentLifecycleReady: sw.Complete(stage, agent.ReadyAt.Sub(*agent.StartedAt)) + case codersdk.WorkspaceAgentLifecycleStartTimeout: + sw.Fail(stage, 0) + sw.Log(time.Time{}, codersdk.LogLevelWarn, "Warning: A startup script timed out and your workspace may be incomplete.") case codersdk.WorkspaceAgentLifecycleStartError: sw.Fail(stage, agent.ReadyAt.Sub(*agent.StartedAt)) // Use zero time (omitted) to separate these from the startup logs. diff --git a/cli/cliui/agent_test.go b/cli/cliui/agent_test.go index d70923395a0ae..2c474e84c22f5 100644 --- a/cli/cliui/agent_test.go +++ b/cli/cliui/agent_test.go @@ -57,6 +57,31 @@ func TestAgent(t *testing.T) { "For more information and troubleshooting, see", }, }, + { + name: "Start timeout", + opts: cliui.AgentOptions{ + FetchInterval: time.Millisecond, + }, + iter: []func(context.Context, *codersdk.WorkspaceAgent, chan []codersdk.WorkspaceAgentLog) error{ + func(_ context.Context, agent *codersdk.WorkspaceAgent, _ chan []codersdk.WorkspaceAgentLog) error { + agent.Status = codersdk.WorkspaceAgentConnecting + return nil + }, + func(_ context.Context, agent *codersdk.WorkspaceAgent, logs chan []codersdk.WorkspaceAgentLog) error { + agent.Status = codersdk.WorkspaceAgentConnected + agent.LifecycleState = codersdk.WorkspaceAgentLifecycleStartTimeout + agent.FirstConnectedAt = ptr.Ref(time.Now()) + return nil + }, + }, + want: []string{ + "⧗ Waiting for the workspace agent to connect", + "✔ Waiting for the workspace agent to connect", + "⧗ Running workspace agent startup scripts (non-blocking)", + "✘ Running workspace agent startup scripts (non-blocking)", + "Warning: A startup script timed out and your workspace may be incomplete.", + }, + }, { name: "Initial connection timeout", opts: cliui.AgentOptions{ diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index a62fd1a2f0bad..367fafa087a6d 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -58,7 +58,7 @@ const ( // Starting returns true if the agent is in the process of starting. func (l WorkspaceAgentLifecycle) Starting() bool { switch l { - case WorkspaceAgentLifecycleCreated, WorkspaceAgentLifecycleStarting, WorkspaceAgentLifecycleStartTimeout: + case WorkspaceAgentLifecycleCreated, WorkspaceAgentLifecycleStarting: return true default: return false diff --git a/provisioner/terraform/resources.go b/provisioner/terraform/resources.go index a143e6a20fa86..4cdd16890731c 100644 --- a/provisioner/terraform/resources.go +++ b/provisioner/terraform/resources.go @@ -547,7 +547,7 @@ func ConvertState(modules []*tfjson.StateModule, rawGraph string) (*State, error if resource.Mode == tfjson.DataResourceMode { continue } - if resource.Type == "coder_agent" || resource.Type == "coder_agent_instance" || resource.Type == "coder_app" || resource.Type == "coder_metadata" { + if resource.Type == "coder_script" || resource.Type == "coder_agent" || resource.Type == "coder_agent_instance" || resource.Type == "coder_app" || resource.Type == "coder_metadata" { continue } label := convertAddressToLabel(resource.Address) From 73a7a782fb5b62f8417b05603febc99c4e1f546e Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 20:55:20 +0000 Subject: [PATCH 56/59] Fix command using shared context --- agent/agentscripts/agentscripts.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 471053dd51cf5..7cf664c9c283b 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -151,12 +151,13 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) }() var cmd *exec.Cmd + cmdCtx := ctx if script.Timeout > 0 { var ctxCancel context.CancelFunc - ctx, ctxCancel = context.WithTimeout(ctx, script.Timeout) + cmdCtx, ctxCancel = context.WithTimeout(ctx, script.Timeout) defer ctxCancel() } - cmdPty, err := r.SSHServer.CreateCommand(ctx, script.Script, nil) + cmdPty, err := r.SSHServer.CreateCommand(cmdCtx, script.Script, nil) if err != nil { return xerrors.Errorf("%s script: create command: %w", logPath, err) } From d1f4963c2e1fb2e607aa96c499e96857ef335836 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 21:02:32 +0000 Subject: [PATCH 57/59] Fix timeout draining --- agent/agentscripts/agentscripts.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/agent/agentscripts/agentscripts.go b/agent/agentscripts/agentscripts.go index 7cf664c9c283b..970b8004e9a60 100644 --- a/agent/agentscripts/agentscripts.go +++ b/agent/agentscripts/agentscripts.go @@ -116,7 +116,7 @@ func (r *Runner) Execute(ctx context.Context, filter func(script codersdk.Worksp eg.Go(func() error { err := r.run(ctx, script) if err != nil { - return xerrors.Errorf("run agent script %q: %w", script.LogPath, err) + return xerrors.Errorf("run agent script %q: %w", script.LogSourceID, err) } return nil }) @@ -217,8 +217,13 @@ func (r *Runner) run(ctx context.Context, script codersdk.WorkspaceAgentScript) return xerrors.Errorf("%s script: track command goroutine: %w", logPath, err) } select { - case <-ctx.Done(): - err = ctx.Err() + case <-cmdCtx.Done(): + // Wait for the command to drain! + select { + case <-cmdDone: + case <-time.After(10 * time.Second): + } + err = cmdCtx.Err() case err = <-cmdDone: } if errors.Is(err, context.DeadlineExceeded) { From 784e616ada0d46ed294baa11bb5c167aefcdc80d Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 21:03:08 +0000 Subject: [PATCH 58/59] Change signal type --- agent/agentscripts/agentscripts_other.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/agentscripts/agentscripts_other.go b/agent/agentscripts/agentscripts_other.go index 2ff8b22391317..a7ab83276e67d 100644 --- a/agent/agentscripts/agentscripts_other.go +++ b/agent/agentscripts/agentscripts_other.go @@ -15,6 +15,6 @@ func cmdSysProcAttr() *syscall.SysProcAttr { func cmdCancel(cmd *exec.Cmd) func() error { return func() error { - return syscall.Kill(-cmd.Process.Pid, syscall.SIGINT) + return syscall.Kill(-cmd.Process.Pid, syscall.SIGHUP) } } From 7ac782bd0202283a2a5234664110d0cf15dbd4e4 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Mon, 25 Sep 2023 21:27:13 +0000 Subject: [PATCH 59/59] Add deterministic colors to startup script logs --- .../components/Resources/AgentRow.stories.tsx | 3 +- site/src/components/Resources/AgentRow.tsx | 96 +++++++++++++++---- 2 files changed, 77 insertions(+), 22 deletions(-) diff --git a/site/src/components/Resources/AgentRow.stories.tsx b/site/src/components/Resources/AgentRow.stories.tsx index ba13f2e35af88..1c73aa7f789f0 100644 --- a/site/src/components/Resources/AgentRow.stories.tsx +++ b/site/src/components/Resources/AgentRow.stories.tsx @@ -16,6 +16,7 @@ import { MockWorkspaceAgentTimeout, MockWorkspaceApp, MockProxyLatencies, + MockWorkspaceAgentLogSource, } from "testHelpers/entities"; import { AgentRow, LineWithID } from "./AgentRow"; import { ProxyContext, getPreferredProxy } from "contexts/ProxyContext"; @@ -95,7 +96,7 @@ const storybookLogs: LineWithID[] = [ level: "info", output: line, time: "", - source_id: "", + source_id: MockWorkspaceAgentLogSource.id, })); const meta: Meta = { diff --git a/site/src/components/Resources/AgentRow.tsx b/site/src/components/Resources/AgentRow.tsx index 7ebc5e0e731ff..737d84852dd2e 100644 --- a/site/src/components/Resources/AgentRow.tsx +++ b/site/src/components/Resources/AgentRow.tsx @@ -297,24 +297,15 @@ export const AgentRow: FC = ({ > {({ index, style }) => { const log = startupLogs[index]; - let sourceIcon: string | undefined = + const sourceIcon: string | undefined = logSourceByID[log.source_id].icon; - if (!sourceIcon) { - // Default to a globe for external. - sourceIcon = "/emojis/1f310.png"; - } - if ( - index > 0 && - logSourceByID[startupLogs[index - 1].source_id].id === - log.source_id - ) { - sourceIcon = undefined; - } - let icon = ( - + let assignedIcon = false; + let icon: JSX.Element; + // If no icon is specified, we show a deterministic + // colored circle to identify unique scripts. + if (sourceIcon) { + icon = ( = ({ marginRight: 8, }} /> - - ); + ); + } else { + icon = ( +
+ ); + assignedIcon = true; + } + let nextChangesSource = false; if (index < startupLogs.length - 1) { nextChangesSource = logSourceByID[startupLogs[index + 1].source_id].id !== log.source_id; } - - if (!sourceIcon) { + // We don't want every line to repeat the icon, because + // that is ugly and repetitive. This removes the icon + // for subsequent lines of the same source and shows a + // line instead, visually indicating they are from the + // same source. + if ( + index > 0 && + logSourceByID[startupLogs[index - 1].source_id].id === + log.source_id + ) { icon = (
= ({ number={index + 1} maxNumber={startupLogs.length} style={style} - sourceIcon={icon} + sourceIcon={ + + {logSourceByID[log.source_id].display_name} + {assignedIcon && ( + +
+ No icon specified! +
+ )} + + } + > + {icon} +
+ } /> ); }} @@ -674,3 +705,26 @@ const useStyles = makeStyles((theme) => ({ textTransform: "capitalize", }, })); + +// These colors were picked at random. Feel free +// to add more, adjust, or change! Users will not +// depend on these colors. +const scriptDisplayColors = [ + "#85A3B2", + "#A37EB2", + "#C29FDE", + "#90B3D7", + "#829AC7", + "#728B8E", + "#506080", + "#5654B0", + "#6B56D6", + "#7847CC", +]; + +const determineScriptDisplayColor = (displayName: string): string => { + const hash = displayName.split("").reduce((hash, char) => { + return (hash << 5) + hash + char.charCodeAt(0); // bit-shift and add for our simple hash + }, 0); + return scriptDisplayColors[Math.abs(hash) % scriptDisplayColors.length]; +};