From be57af5ff020987359ac27733881a45354d08312 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 29 Apr 2026 12:24:26 -0800 Subject: [PATCH] feat: add exit code and status to workspace agent scripts (#24505) For scripts that have not finished or in dry run cases these will be omitted. --- coderd/agentapi/manifest.go | 6 +-- coderd/agentapi/manifest_test.go | 6 +-- coderd/apidoc/docs.go | 21 ++++++++ coderd/apidoc/swagger.json | 16 ++++++ coderd/database/db2sdk/db2sdk.go | 28 ++++++++++ coderd/database/dbauthz/dbauthz.go | 2 +- coderd/database/dbauthz/dbauthz_test.go | 2 +- coderd/database/dbmetrics/querymetrics.go | 2 +- coderd/database/dbmock/dbmock.go | 4 +- coderd/database/querier.go | 2 +- coderd/database/queries.sql.go | 36 +++++++++++-- coderd/database/queries/workspacescripts.sql | 11 +++- coderd/provisionerjobs.go | 2 +- coderd/workspaceagents.go | 17 ++---- coderd/workspacebuilds.go | 12 ++--- coderd/workspaces.go | 2 +- coderd/workspaces_test.go | 15 +++++- codersdk/workspaceagents.go | 32 +++++++---- docs/reference/api/agents.md | 2 + docs/reference/api/builds.md | 48 +++++++++++------ docs/reference/api/schemas.md | 56 +++++++++++++++----- docs/reference/api/tasks.md | 4 ++ docs/reference/api/templates.md | 12 ++++- docs/reference/api/workspaces.md | 12 +++++ site/src/api/typesGenerated.ts | 16 ++++++ 25 files changed, 286 insertions(+), 80 deletions(-) diff --git a/coderd/agentapi/manifest.go b/coderd/agentapi/manifest.go index c123913a12..fd8e6f7739 100644 --- a/coderd/agentapi/manifest.go +++ b/coderd/agentapi/manifest.go @@ -40,7 +40,7 @@ type ManifestAPI struct { func (a *ManifestAPI) GetManifest(ctx context.Context, _ *agentproto.GetManifestRequest) (*agentproto.Manifest, error) { var ( dbApps []database.WorkspaceApp - scripts []database.WorkspaceAgentScript + scripts []database.GetWorkspaceAgentScriptsByAgentIDsRow metadata []database.WorkspaceAgentMetadatum workspace database.Workspace devcontainers []database.WorkspaceAgentDevcontainer @@ -184,7 +184,7 @@ func dbAgentMetadatumToProtoDescription(metadatum database.WorkspaceAgentMetadat } } -func dbAgentScriptsToProto(scripts []database.WorkspaceAgentScript) []*agentproto.WorkspaceAgentScript { +func dbAgentScriptsToProto(scripts []database.GetWorkspaceAgentScriptsByAgentIDsRow) []*agentproto.WorkspaceAgentScript { ret := make([]*agentproto.WorkspaceAgentScript, len(scripts)) for i, script := range scripts { ret[i] = dbAgentScriptToProto(script) @@ -192,7 +192,7 @@ func dbAgentScriptsToProto(scripts []database.WorkspaceAgentScript) []*agentprot return ret } -func dbAgentScriptToProto(script database.WorkspaceAgentScript) *agentproto.WorkspaceAgentScript { +func dbAgentScriptToProto(script database.GetWorkspaceAgentScriptsByAgentIDsRow) *agentproto.WorkspaceAgentScript { return &agentproto.WorkspaceAgentScript{ Id: script.ID[:], LogSourceId: script.LogSourceID[:], diff --git a/coderd/agentapi/manifest_test.go b/coderd/agentapi/manifest_test.go index bbcef00f83..4c5890052b 100644 --- a/coderd/agentapi/manifest_test.go +++ b/coderd/agentapi/manifest_test.go @@ -114,7 +114,7 @@ func TestGetManifest(t *testing.T) { Hidden: true, }, } - scripts = []database.WorkspaceAgentScript{ + scripts = []database.GetWorkspaceAgentScriptsByAgentIDsRow{ { ID: uuid.New(), WorkspaceAgentID: agent.ID, @@ -396,7 +396,7 @@ func TestGetManifest(t *testing.T) { } mDB.EXPECT().GetWorkspaceAppsByAgentID(gomock.Any(), childAgent.ID).Return([]database.WorkspaceApp{}, nil) - mDB.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), []uuid.UUID{childAgent.ID}).Return([]database.WorkspaceAgentScript{}, nil) + mDB.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), []uuid.UUID{childAgent.ID}).Return([]database.GetWorkspaceAgentScriptsByAgentIDsRow{}, nil) mDB.EXPECT().GetWorkspaceAgentMetadata(gomock.Any(), database.GetWorkspaceAgentMetadataParams{ WorkspaceAgentID: childAgent.ID, Keys: nil, // all @@ -459,7 +459,7 @@ func TestGetManifest(t *testing.T) { } mDB.EXPECT().GetWorkspaceAppsByAgentID(gomock.Any(), childAgent.ID).Return([]database.WorkspaceApp{}, nil) - mDB.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), []uuid.UUID{childAgent.ID}).Return([]database.WorkspaceAgentScript{}, nil) + mDB.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), []uuid.UUID{childAgent.ID}).Return([]database.GetWorkspaceAgentScriptsByAgentIDsRow{}, nil) mDB.EXPECT().GetWorkspaceAgentMetadata(gomock.Any(), database.GetWorkspaceAgentMetadataParams{ WorkspaceAgentID: childAgent.ID, Keys: nil, diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 4275367b30..183951a2ac 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -22895,6 +22895,9 @@ const docTemplate = `{ "display_name": { "type": "string" }, + "exit_code": { + "type": "integer" + }, "id": { "type": "string", "format": "uuid" @@ -22918,11 +22921,29 @@ const docTemplate = `{ "start_blocks_login": { "type": "boolean" }, + "status": { + "$ref": "#/definitions/codersdk.WorkspaceAgentScriptStatus" + }, "timeout": { "type": "integer" } } }, + "codersdk.WorkspaceAgentScriptStatus": { + "type": "string", + "enum": [ + "ok", + "exit_failure", + "timed_out", + "pipes_left_open" + ], + "x-enum-varnames": [ + "WorkspaceAgentScriptStatusOK", + "WorkspaceAgentScriptStatusExitFailure", + "WorkspaceAgentScriptStatusTimedOut", + "WorkspaceAgentScriptStatusPipesLeftOpen" + ] + }, "codersdk.WorkspaceAgentStartupScriptBehavior": { "type": "string", "enum": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index e03d4d09a2..4bef3d5c83 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -21086,6 +21086,9 @@ "display_name": { "type": "string" }, + "exit_code": { + "type": "integer" + }, "id": { "type": "string", "format": "uuid" @@ -21109,11 +21112,24 @@ "start_blocks_login": { "type": "boolean" }, + "status": { + "$ref": "#/definitions/codersdk.WorkspaceAgentScriptStatus" + }, "timeout": { "type": "integer" } } }, + "codersdk.WorkspaceAgentScriptStatus": { + "type": "string", + "enum": ["ok", "exit_failure", "timed_out", "pipes_left_open"], + "x-enum-varnames": [ + "WorkspaceAgentScriptStatusOK", + "WorkspaceAgentScriptStatusExitFailure", + "WorkspaceAgentScriptStatusTimedOut", + "WorkspaceAgentScriptStatusPipesLeftOpen" + ] + }, "codersdk.WorkspaceAgentStartupScriptBehavior": { "type": "string", "enum": ["blocking", "non-blocking"], diff --git a/coderd/database/db2sdk/db2sdk.go b/coderd/database/db2sdk/db2sdk.go index 1db74da36f..77b6bd0867 100644 --- a/coderd/database/db2sdk/db2sdk.go +++ b/coderd/database/db2sdk/db2sdk.go @@ -675,6 +675,27 @@ func WorkspaceAgentLog(log database.WorkspaceAgentLog) codersdk.WorkspaceAgentLo } } +func WorkspaceAgentScript(dbScript database.GetWorkspaceAgentScriptsByAgentIDsRow) codersdk.WorkspaceAgentScript { + script := codersdk.WorkspaceAgentScript{ + ID: dbScript.ID, + LogPath: dbScript.LogPath, + LogSourceID: dbScript.LogSourceID, + Script: dbScript.Script, + Cron: dbScript.Cron, + RunOnStart: dbScript.RunOnStart, + RunOnStop: dbScript.RunOnStop, + StartBlocksLogin: dbScript.StartBlocksLogin, + Timeout: time.Duration(dbScript.TimeoutSeconds) * time.Second, + DisplayName: dbScript.DisplayName, + ExitCode: nullInt32Ptr(dbScript.ExitCode), + } + if dbScript.Status.Valid { + status := codersdk.WorkspaceAgentScriptStatus(dbScript.Status.WorkspaceAgentScriptTimingStatus) + script.Status = &status + } + return script +} + func ProvisionerDaemon(dbDaemon database.ProvisionerDaemon) codersdk.ProvisionerDaemon { result := codersdk.ProvisionerDaemon{ ID: dbDaemon.ID, @@ -1563,6 +1584,13 @@ func nullInt64Ptr(v sql.NullInt64) *int64 { return &value } +func nullInt32Ptr(n sql.NullInt32) *int32 { + if !n.Valid { + return nil + } + return &n.Int32 +} + func nullStringPtr(v sql.NullString) *string { if !v.Valid { return nil diff --git a/coderd/database/dbauthz/dbauthz.go b/coderd/database/dbauthz/dbauthz.go index 1412054c5f..bbc09d3573 100644 --- a/coderd/database/dbauthz/dbauthz.go +++ b/coderd/database/dbauthz/dbauthz.go @@ -4595,7 +4595,7 @@ func (q *querier) GetWorkspaceAgentScriptTimingsByBuildID(ctx context.Context, i return q.db.GetWorkspaceAgentScriptTimingsByBuildID(ctx, id) } -func (q *querier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { +func (q *querier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.GetWorkspaceAgentScriptsByAgentIDsRow, error) { if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil { return nil, err } diff --git a/coderd/database/dbauthz/dbauthz_test.go b/coderd/database/dbauthz/dbauthz_test.go index 1ccb0bd4df..96ce142706 100644 --- a/coderd/database/dbauthz/dbauthz_test.go +++ b/coderd/database/dbauthz/dbauthz_test.go @@ -4694,7 +4694,7 @@ func (s *MethodTestSuite) TestSystemFunctions() { })) s.Run("GetWorkspaceAgentScriptsByAgentIDs", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { ids := []uuid.UUID{uuid.New()} - dbm.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), ids).Return([]database.WorkspaceAgentScript{}, nil).AnyTimes() + dbm.EXPECT().GetWorkspaceAgentScriptsByAgentIDs(gomock.Any(), ids).Return([]database.GetWorkspaceAgentScriptsByAgentIDsRow{}, nil).AnyTimes() check.Args(ids).Asserts(rbac.ResourceSystem, policy.ActionRead) })) s.Run("GetWorkspaceAgentLogSourcesByAgentIDs", s.Mocked(func(dbm *dbmock.MockStore, _ *gofakeit.Faker, check *expects) { diff --git a/coderd/database/dbmetrics/querymetrics.go b/coderd/database/dbmetrics/querymetrics.go index 5ca4e1d6cb..aa5017e912 100644 --- a/coderd/database/dbmetrics/querymetrics.go +++ b/coderd/database/dbmetrics/querymetrics.go @@ -3024,7 +3024,7 @@ func (m queryMetricsStore) GetWorkspaceAgentScriptTimingsByBuildID(ctx context.C return r0, r1 } -func (m queryMetricsStore) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { +func (m queryMetricsStore) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.GetWorkspaceAgentScriptsByAgentIDsRow, error) { start := time.Now() r0, r1 := m.s.GetWorkspaceAgentScriptsByAgentIDs(ctx, ids) m.queryLatencies.WithLabelValues("GetWorkspaceAgentScriptsByAgentIDs").Observe(time.Since(start).Seconds()) diff --git a/coderd/database/dbmock/dbmock.go b/coderd/database/dbmock/dbmock.go index a9dacf2a93..a218b84a41 100644 --- a/coderd/database/dbmock/dbmock.go +++ b/coderd/database/dbmock/dbmock.go @@ -5657,10 +5657,10 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAgentScriptTimingsByBuildID(ctx, id } // GetWorkspaceAgentScriptsByAgentIDs mocks base method. -func (m *MockStore) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAgentScript, error) { +func (m *MockStore) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]database.GetWorkspaceAgentScriptsByAgentIDsRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetWorkspaceAgentScriptsByAgentIDs", ctx, ids) - ret0, _ := ret[0].([]database.WorkspaceAgentScript) + ret0, _ := ret[0].([]database.GetWorkspaceAgentScriptsByAgentIDsRow) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/coderd/database/querier.go b/coderd/database/querier.go index 5fb8b8baa7..84c142e4de 100644 --- a/coderd/database/querier.go +++ b/coderd/database/querier.go @@ -732,7 +732,7 @@ type sqlcQuerier interface { GetWorkspaceAgentMetadata(ctx context.Context, arg GetWorkspaceAgentMetadataParams) ([]WorkspaceAgentMetadatum, error) GetWorkspaceAgentPortShare(ctx context.Context, arg GetWorkspaceAgentPortShareParams) (WorkspaceAgentPortShare, error) GetWorkspaceAgentScriptTimingsByBuildID(ctx context.Context, id uuid.UUID) ([]GetWorkspaceAgentScriptTimingsByBuildIDRow, error) - GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) + GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]GetWorkspaceAgentScriptsByAgentIDsRow, error) GetWorkspaceAgentStats(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsRow, error) GetWorkspaceAgentStatsAndLabels(ctx context.Context, createdAt time.Time) ([]GetWorkspaceAgentStatsAndLabelsRow, error) // `minute_buckets` could return 0 rows if there are no usage stats since `created_at`. diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 27e5474f03..14546f09e1 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -33347,18 +33347,44 @@ func (q *sqlQuerier) UpdateWorkspacesTTLByTemplateID(ctx context.Context, arg Up } 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, display_name, id FROM workspace_agent_scripts WHERE workspace_agent_id = ANY($1 :: uuid [ ]) +SELECT + DISTINCT ON (workspace_agent_scripts.id) 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, workspace_agent_scripts.display_name, workspace_agent_scripts.id, + workspace_agent_script_timings.exit_code, + workspace_agent_script_timings.status + FROM workspace_agent_scripts + LEFT JOIN workspace_agent_script_timings + ON workspace_agent_script_timings.script_id = workspace_agent_scripts.id + WHERE workspace_agent_scripts.workspace_agent_id = ANY($1 :: uuid [ ]) + ORDER BY workspace_agent_scripts.id, workspace_agent_script_timings.started_at + DESC NULLS LAST ` -func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAgentScript, error) { +type GetWorkspaceAgentScriptsByAgentIDsRow struct { + 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"` + 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"` + DisplayName string `db:"display_name" json:"display_name"` + ID uuid.UUID `db:"id" json:"id"` + ExitCode sql.NullInt32 `db:"exit_code" json:"exit_code"` + Status NullWorkspaceAgentScriptTimingStatus `db:"status" json:"status"` +} + +func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]GetWorkspaceAgentScriptsByAgentIDsRow, error) { rows, err := q.db.QueryContext(ctx, getWorkspaceAgentScriptsByAgentIDs, pq.Array(ids)) if err != nil { return nil, err } defer rows.Close() - var items []WorkspaceAgentScript + var items []GetWorkspaceAgentScriptsByAgentIDsRow for rows.Next() { - var i WorkspaceAgentScript + var i GetWorkspaceAgentScriptsByAgentIDsRow if err := rows.Scan( &i.WorkspaceAgentID, &i.LogSourceID, @@ -33372,6 +33398,8 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptsByAgentIDs(ctx context.Context, ids &i.TimeoutSeconds, &i.DisplayName, &i.ID, + &i.ExitCode, + &i.Status, ); err != nil { return nil, err } diff --git a/coderd/database/queries/workspacescripts.sql b/coderd/database/queries/workspacescripts.sql index aa1407647b..fcf90a7832 100644 --- a/coderd/database/queries/workspacescripts.sql +++ b/coderd/database/queries/workspacescripts.sql @@ -17,4 +17,13 @@ SELECT RETURNING workspace_agent_scripts.*; -- name: GetWorkspaceAgentScriptsByAgentIDs :many -SELECT * FROM workspace_agent_scripts WHERE workspace_agent_id = ANY(@ids :: uuid [ ]); +SELECT + DISTINCT ON (workspace_agent_scripts.id) workspace_agent_scripts.*, + workspace_agent_script_timings.exit_code, + workspace_agent_script_timings.status + FROM workspace_agent_scripts + LEFT JOIN workspace_agent_script_timings + ON workspace_agent_script_timings.script_id = workspace_agent_scripts.id + WHERE workspace_agent_scripts.workspace_agent_id = ANY(@ids :: uuid [ ]) + ORDER BY workspace_agent_scripts.id, workspace_agent_script_timings.started_at + DESC NULLS LAST; diff --git a/coderd/provisionerjobs.go b/coderd/provisionerjobs.go index bf5a644795..bb7c83b2c6 100644 --- a/coderd/provisionerjobs.go +++ b/coderd/provisionerjobs.go @@ -315,7 +315,7 @@ func (api *API) provisionerJobResources(rw http.ResponseWriter, r *http.Request, dbApps = append(dbApps, app) } } - dbScripts := make([]database.WorkspaceAgentScript, 0) + dbScripts := make([]database.GetWorkspaceAgentScriptsByAgentIDsRow, 0) for _, script := range scripts { if script.WorkspaceAgentID == agent.ID { dbScripts = append(dbScripts, script) diff --git a/coderd/workspaceagents.go b/coderd/workspaceagents.go index 125fdbbe68..d98a027d20 100644 --- a/coderd/workspaceagents.go +++ b/coderd/workspaceagents.go @@ -67,7 +67,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) { ctx = r.Context() waws = httpmw.WorkspaceAgentAndWorkspaceParam(r) dbApps []database.WorkspaceApp - scripts []database.WorkspaceAgentScript + scripts []database.GetWorkspaceAgentScriptsByAgentIDsRow logSources []database.WorkspaceAgentLogSource ) @@ -1634,21 +1634,10 @@ func convertLogSources(dbLogSources []database.WorkspaceAgentLogSource) []coders return logSources } -func convertScripts(dbScripts []database.WorkspaceAgentScript) []codersdk.WorkspaceAgentScript { +func convertScripts(dbScripts []database.GetWorkspaceAgentScriptsByAgentIDsRow) []codersdk.WorkspaceAgentScript { scripts := make([]codersdk.WorkspaceAgentScript, 0) for _, dbScript := range dbScripts { - scripts = append(scripts, codersdk.WorkspaceAgentScript{ - ID: dbScript.ID, - LogPath: dbScript.LogPath, - LogSourceID: dbScript.LogSourceID, - Script: dbScript.Script, - Cron: dbScript.Cron, - RunOnStart: dbScript.RunOnStart, - RunOnStop: dbScript.RunOnStop, - StartBlocksLogin: dbScript.StartBlocksLogin, - Timeout: time.Duration(dbScript.TimeoutSeconds) * time.Second, - DisplayName: dbScript.DisplayName, - }) + scripts = append(scripts, db2sdk.WorkspaceAgentScript(dbScript)) } return scripts } diff --git a/coderd/workspacebuilds.go b/coderd/workspacebuilds.go index 453333d1c1..3f7b48b9ec 100644 --- a/coderd/workspacebuilds.go +++ b/coderd/workspacebuilds.go @@ -542,7 +542,7 @@ func (api *API) postWorkspaceBuildsInternal( []database.WorkspaceAgent{}, []database.WorkspaceApp{}, []database.WorkspaceAppStatus{}, - []database.WorkspaceAgentScript{}, + []database.GetWorkspaceAgentScriptsByAgentIDsRow{}, []database.WorkspaceAgentLogSource{}, database.TemplateVersion{}, provisionerDaemons, @@ -982,7 +982,7 @@ type workspaceBuildsData struct { agents []database.WorkspaceAgent apps []database.WorkspaceApp appStatuses []database.WorkspaceAppStatus - scripts []database.WorkspaceAgentScript + scripts []database.GetWorkspaceAgentScriptsByAgentIDsRow logSources []database.WorkspaceAgentLogSource provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow } @@ -1070,7 +1070,7 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaceBuilds []datab var ( apps []database.WorkspaceApp - scripts []database.WorkspaceAgentScript + scripts []database.GetWorkspaceAgentScriptsByAgentIDsRow logSources []database.WorkspaceAgentLogSource ) @@ -1129,7 +1129,7 @@ func (api *API) convertWorkspaceBuilds( resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, agentAppStatuses []database.WorkspaceAppStatus, - agentScripts []database.WorkspaceAgentScript, + agentScripts []database.GetWorkspaceAgentScriptsByAgentIDsRow, agentLogSources []database.WorkspaceAgentLogSource, templateVersions []database.TemplateVersion, provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, @@ -1196,7 +1196,7 @@ func (api *API) convertWorkspaceBuild( resourceAgents []database.WorkspaceAgent, agentApps []database.WorkspaceApp, agentAppStatuses []database.WorkspaceAppStatus, - agentScripts []database.WorkspaceAgentScript, + agentScripts []database.GetWorkspaceAgentScriptsByAgentIDsRow, agentLogSources []database.WorkspaceAgentLogSource, templateVersion database.TemplateVersion, provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow, @@ -1217,7 +1217,7 @@ func (api *API) convertWorkspaceBuild( for _, app := range agentApps { appsByAgentID[app.AgentID] = append(appsByAgentID[app.AgentID], app) } - scriptsByAgentID := map[uuid.UUID][]database.WorkspaceAgentScript{} + scriptsByAgentID := map[uuid.UUID][]database.GetWorkspaceAgentScriptsByAgentIDsRow{} for _, script := range agentScripts { scriptsByAgentID[script.WorkspaceAgentID] = append(scriptsByAgentID[script.WorkspaceAgentID], script) } diff --git a/coderd/workspaces.go b/coderd/workspaces.go index 961db5b6ce..f33af027ba 100644 --- a/coderd/workspaces.go +++ b/coderd/workspaces.go @@ -860,7 +860,7 @@ func createWorkspace( []database.WorkspaceAgent{}, []database.WorkspaceApp{}, []database.WorkspaceAppStatus{}, - []database.WorkspaceAgentScript{}, + []database.GetWorkspaceAgentScriptsByAgentIDsRow{}, []database.WorkspaceAgentLogSource{}, database.TemplateVersion{}, provisionerDaemons, diff --git a/coderd/workspaces_test.go b/coderd/workspaces_test.go index 7f6b2559d2..255b9e2128 100644 --- a/coderd/workspaces_test.go +++ b/coderd/workspaces_test.go @@ -5027,7 +5027,7 @@ func TestWorkspaceTimings(t *testing.T) { scripts := dbgen.WorkspaceAgentScripts(t, db, 3, database.WorkspaceAgentScript{ WorkspaceAgentID: agent.ID, }) - dbgen.WorkspaceAgentScriptTimings(t, db, scripts) + timings := dbgen.WorkspaceAgentScriptTimings(t, db, scripts) // When: fetching the timings ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong) @@ -5038,6 +5038,19 @@ func TestWorkspaceTimings(t *testing.T) { require.NoError(t, err) require.Len(t, res.ProvisionerTimings, 5) require.Len(t, res.AgentScriptTimings, 3) + + // The same timings should be on the workspace response. + workspace, err := client.Workspace(ctx, ws.ID) + require.NoError(t, err) + require.Len(t, workspace.LatestBuild.Resources[0].Agents[0].Scripts, 3) + for _, script := range workspace.LatestBuild.Resources[0].Agents[0].Scripts { + timing, found := slice.Find(timings, func(timing database.WorkspaceAgentScriptTiming) bool { + return timing.ScriptID == script.ID + }) + require.True(t, found) + require.Equal(t, *script.ExitCode, timing.ExitCode) + require.Equal(t, *script.Status, codersdk.WorkspaceAgentScriptStatus(timing.Status)) + } }) t.Run("NonExistentWorkspace", func(t *testing.T) { diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 7cf7665dca..fa246fc39c 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -185,17 +185,29 @@ type WorkspaceAgentLogSource struct { Icon string `json:"icon"` } +type WorkspaceAgentScriptStatus string + +// This is also in database/models.go and should be kept in sync. +const ( + WorkspaceAgentScriptStatusOK WorkspaceAgentScriptStatus = "ok" + WorkspaceAgentScriptStatusExitFailure WorkspaceAgentScriptStatus = "exit_failure" + WorkspaceAgentScriptStatusTimedOut WorkspaceAgentScriptStatus = "timed_out" + WorkspaceAgentScriptStatusPipesLeftOpen WorkspaceAgentScriptStatus = "pipes_left_open" +) + type WorkspaceAgentScript struct { - ID uuid.UUID `json:"id" format:"uuid"` - LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` - LogPath string `json:"log_path"` - Script string `json:"script"` - 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"` - DisplayName string `json:"display_name"` + ID uuid.UUID `json:"id" format:"uuid"` + LogSourceID uuid.UUID `json:"log_source_id" format:"uuid"` + LogPath string `json:"log_path"` + Script string `json:"script"` + 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"` + DisplayName string `json:"display_name"` + ExitCode *int32 `json:"exit_code,omitempty"` + Status *WorkspaceAgentScriptStatus `json:"status,omitempty"` } type WorkspaceAgentHealth struct { diff --git a/docs/reference/api/agents.md b/docs/reference/api/agents.md index 2d2848755f..05ff98209a 100644 --- a/docs/reference/api/agents.md +++ b/docs/reference/api/agents.md @@ -632,6 +632,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -639,6 +640,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], diff --git a/docs/reference/api/builds.md b/docs/reference/api/builds.md index 6bc15054ec..59b68f8b0a 100644 --- a/docs/reference/api/builds.md +++ b/docs/reference/api/builds.md @@ -182,6 +182,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -189,6 +190,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -423,6 +425,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -430,6 +433,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -782,6 +786,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -789,6 +794,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -908,6 +914,7 @@ Status Code **200** | `»» scripts` | array | false | | | | `»»» cron` | string | false | | | | `»»» display_name` | string | false | | | +| `»»» exit_code` | integer | false | | | | `»»» id` | string(uuid) | false | | | | `»»» log_path` | string | false | | | | `»»» log_source_id` | string(uuid) | false | | | @@ -915,6 +922,7 @@ Status Code **200** | `»»» run_on_stop` | boolean | false | | | | `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | +| `»»» status` | [codersdk.WorkspaceAgentScriptStatus](schemas.md#codersdkworkspaceagentscriptstatus) | 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! | @@ -946,8 +954,8 @@ Status Code **200** | `sharing_level` | `authenticated`, `organization`, `owner`, `public` | | `state` | `complete`, `failure`, `idle`, `working` | | `lifecycle_state` | `created`, `off`, `ready`, `shutdown_error`, `shutdown_timeout`, `shutting_down`, `start_error`, `start_timeout`, `starting` | +| `status` | `connected`, `connecting`, `disconnected`, `exit_failure`, `ok`, `pipes_left_open`, `timed_out`, `timeout` | | `startup_script_behavior` | `blocking`, `non-blocking` | -| `status` | `connected`, `connecting`, `disconnected`, `timeout` | | `workspace_transition` | `delete`, `start`, `stop` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -1132,6 +1140,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -1139,6 +1148,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -1484,6 +1494,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -1491,6 +1502,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -1673,6 +1685,7 @@ Status Code **200** | `»»» scripts` | array | false | | | | `»»»» cron` | string | false | | | | `»»»» display_name` | string | false | | | +| `»»»» exit_code` | integer | false | | | | `»»»» id` | string(uuid) | false | | | | `»»»» log_path` | string | false | | | | `»»»» log_source_id` | string(uuid) | false | | | @@ -1680,6 +1693,7 @@ Status Code **200** | `»»»» run_on_stop` | boolean | false | | | | `»»»» script` | string | false | | | | `»»»» start_blocks_login` | boolean | false | | | +| `»»»» status` | [codersdk.WorkspaceAgentScriptStatus](schemas.md#codersdkworkspaceagentscriptstatus) | 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! | @@ -1715,21 +1729,21 @@ Status Code **200** #### Enumerated Values -| Property | Value(s) | -|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | -| `workspace_build_transition` | `delete`, `start`, `stop` | -| `status` | `canceled`, `canceling`, `connected`, `connecting`, `deleted`, `deleting`, `disconnected`, `failed`, `pending`, `running`, `starting`, `stopped`, `stopping`, `succeeded`, `timeout` | -| `type` | `template_version_dry_run`, `template_version_import`, `workspace_build` | -| `reason` | `autostart`, `autostop`, `initiator` | -| `health` | `disabled`, `healthy`, `initializing`, `unhealthy` | -| `open_in` | `slim-window`, `tab` | -| `sharing_level` | `authenticated`, `organization`, `owner`, `public` | -| `state` | `complete`, `failure`, `idle`, `working` | -| `lifecycle_state` | `created`, `off`, `ready`, `shutdown_error`, `shutdown_timeout`, `shutting_down`, `start_error`, `start_timeout`, `starting` | -| `startup_script_behavior` | `blocking`, `non-blocking` | -| `workspace_transition` | `delete`, `start`, `stop` | -| `transition` | `delete`, `start`, `stop` | +| Property | Value(s) | +|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `error_code` | `REQUIRED_TEMPLATE_VARIABLES` | +| `workspace_build_transition` | `delete`, `start`, `stop` | +| `status` | `canceled`, `canceling`, `connected`, `connecting`, `deleted`, `deleting`, `disconnected`, `exit_failure`, `failed`, `ok`, `pending`, `pipes_left_open`, `running`, `starting`, `stopped`, `stopping`, `succeeded`, `timed_out`, `timeout` | +| `type` | `template_version_dry_run`, `template_version_import`, `workspace_build` | +| `reason` | `autostart`, `autostop`, `initiator` | +| `health` | `disabled`, `healthy`, `initializing`, `unhealthy` | +| `open_in` | `slim-window`, `tab` | +| `sharing_level` | `authenticated`, `organization`, `owner`, `public` | +| `state` | `complete`, `failure`, `idle`, `working` | +| `lifecycle_state` | `created`, `off`, `ready`, `shutdown_error`, `shutdown_timeout`, `shutting_down`, `start_error`, `start_timeout`, `starting` | +| `startup_script_behavior` | `blocking`, `non-blocking` | +| `workspace_transition` | `delete`, `start`, `stop` | +| `transition` | `delete`, `start`, `stop` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -1938,6 +1952,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -1945,6 +1960,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index dfdc7a1a9e..c7ef91a142 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -7337,6 +7337,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -7344,6 +7345,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -8687,6 +8689,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -8694,6 +8697,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit| "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -11644,6 +11648,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -11651,6 +11656,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, + "status": "ok", "timeout": 0 } ], @@ -11927,6 +11933,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -11934,6 +11941,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, + "status": "ok", "timeout": 0 } ], @@ -12433,6 +12441,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -12440,24 +12449,41 @@ 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, + "status": "ok", "timeout": 0 } ``` ### Properties -| Name | Type | Required | Restrictions | Description | -|----------------------|---------|----------|--------------|-------------| -| `cron` | string | false | | | -| `display_name` | string | false | | | -| `id` | string | false | | | -| `log_path` | string | false | | | -| `log_source_id` | string | false | | | -| `run_on_start` | boolean | false | | | -| `run_on_stop` | boolean | false | | | -| `script` | string | false | | | -| `start_blocks_login` | boolean | false | | | -| `timeout` | integer | false | | | +| Name | Type | Required | Restrictions | Description | +|----------------------|----------------------------------------------------------------------------|----------|--------------|-------------| +| `cron` | string | false | | | +| `display_name` | string | false | | | +| `exit_code` | integer | false | | | +| `id` | string | false | | | +| `log_path` | string | false | | | +| `log_source_id` | string | false | | | +| `run_on_start` | boolean | false | | | +| `run_on_stop` | boolean | false | | | +| `script` | string | false | | | +| `start_blocks_login` | boolean | false | | | +| `status` | [codersdk.WorkspaceAgentScriptStatus](#codersdkworkspaceagentscriptstatus) | false | | | +| `timeout` | integer | false | | | + +## codersdk.WorkspaceAgentScriptStatus + +```json +"ok" +``` + +### Properties + +#### Enumerated Values + +| Value(s) | +|------------------------------------------------------| +| `exit_failure`, `ok`, `pipes_left_open`, `timed_out` | ## codersdk.WorkspaceAgentStartupScriptBehavior @@ -12803,6 +12829,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -12810,6 +12837,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, + "status": "ok", "timeout": 0 } ], @@ -13271,6 +13299,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -13278,6 +13307,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, + "status": "ok", "timeout": 0 } ], @@ -13619,6 +13649,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -13626,6 +13657,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, + "status": "ok", "timeout": 0 } ], diff --git a/docs/reference/api/tasks.md b/docs/reference/api/tasks.md index f1f0e1fe7a..f1f112b580 100644 --- a/docs/reference/api/tasks.md +++ b/docs/reference/api/tasks.md @@ -547,6 +547,7 @@ curl -X POST http://coder-server:8080/api/v2/tasks/{user}/{task}/pause \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -554,6 +555,7 @@ curl -X POST http://coder-server:8080/api/v2/tasks/{user}/{task}/pause \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -791,6 +793,7 @@ curl -X POST http://coder-server:8080/api/v2/tasks/{user}/{task}/resume \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -798,6 +801,7 @@ curl -X POST http://coder-server:8080/api/v2/tasks/{user}/{task}/resume \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md index 4d0b89b77a..c36709bb6f 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -2494,6 +2494,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -2501,6 +2502,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -2620,6 +2622,7 @@ Status Code **200** | `»» scripts` | array | false | | | | `»»» cron` | string | false | | | | `»»» display_name` | string | false | | | +| `»»» exit_code` | integer | false | | | | `»»» id` | string(uuid) | false | | | | `»»» log_path` | string | false | | | | `»»» log_source_id` | string(uuid) | false | | | @@ -2627,6 +2630,7 @@ Status Code **200** | `»»» run_on_stop` | boolean | false | | | | `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | +| `»»» status` | [codersdk.WorkspaceAgentScriptStatus](schemas.md#codersdkworkspaceagentscriptstatus) | 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! | @@ -2658,8 +2662,8 @@ Status Code **200** | `sharing_level` | `authenticated`, `organization`, `owner`, `public` | | `state` | `complete`, `failure`, `idle`, `working` | | `lifecycle_state` | `created`, `off`, `ready`, `shutdown_error`, `shutdown_timeout`, `shutting_down`, `start_error`, `start_timeout`, `starting` | +| `status` | `connected`, `connecting`, `disconnected`, `exit_failure`, `ok`, `pipes_left_open`, `timed_out`, `timeout` | | `startup_script_behavior` | `blocking`, `non-blocking` | -| `status` | `connected`, `connecting`, `disconnected`, `timeout` | | `workspace_transition` | `delete`, `start`, `stop` | To perform this operation, you must be authenticated. [Learn more](authentication.md). @@ -3160,6 +3164,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -3167,6 +3172,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -3286,6 +3292,7 @@ Status Code **200** | `»» scripts` | array | false | | | | `»»» cron` | string | false | | | | `»»» display_name` | string | false | | | +| `»»» exit_code` | integer | false | | | | `»»» id` | string(uuid) | false | | | | `»»» log_path` | string | false | | | | `»»» log_source_id` | string(uuid) | false | | | @@ -3293,6 +3300,7 @@ Status Code **200** | `»»» run_on_stop` | boolean | false | | | | `»»» script` | string | false | | | | `»»» start_blocks_login` | boolean | false | | | +| `»»» status` | [codersdk.WorkspaceAgentScriptStatus](schemas.md#codersdkworkspaceagentscriptstatus) | 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! | @@ -3324,8 +3332,8 @@ Status Code **200** | `sharing_level` | `authenticated`, `organization`, `owner`, `public` | | `state` | `complete`, `failure`, `idle`, `working` | | `lifecycle_state` | `created`, `off`, `ready`, `shutdown_error`, `shutdown_timeout`, `shutting_down`, `start_error`, `start_timeout`, `starting` | +| `status` | `connected`, `connecting`, `disconnected`, `exit_failure`, `ok`, `pipes_left_open`, `timed_out`, `timeout` | | `startup_script_behavior` | `blocking`, `non-blocking` | -| `status` | `connected`, `connecting`, `disconnected`, `timeout` | | `workspace_transition` | `delete`, `start`, `stop` | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/docs/reference/api/workspaces.md b/docs/reference/api/workspaces.md index e975fc2136..0d2e18ce91 100644 --- a/docs/reference/api/workspaces.md +++ b/docs/reference/api/workspaces.md @@ -237,6 +237,7 @@ of the template will be used. { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -244,6 +245,7 @@ of the template will be used. "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -601,6 +603,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -608,6 +611,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -932,6 +936,7 @@ of the template will be used. { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -939,6 +944,7 @@ of the template will be used. "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -1224,6 +1230,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -1231,6 +1238,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -1531,6 +1539,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -1538,6 +1547,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], @@ -2098,6 +2108,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ { "cron": "string", "display_name": "string", + "exit_code": 0, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "log_path": "string", "log_source_id": "4197ab25-95cf-4b91-9c78-f7f2af5d353a", @@ -2105,6 +2116,7 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "run_on_stop": true, "script": "string", "start_blocks_login": true, + "status": "ok", "timeout": 0 } ], diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 6f26f6c1f3..0b3913ab03 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -9039,8 +9039,24 @@ export interface WorkspaceAgentScript { readonly start_blocks_login: boolean; readonly timeout: number; readonly display_name: string; + readonly exit_code?: number; + readonly status?: WorkspaceAgentScriptStatus; } +// From codersdk/workspaceagents.go +export type WorkspaceAgentScriptStatus = + | "exit_failure" + | "ok" + | "pipes_left_open" + | "timed_out"; + +export const WorkspaceAgentScriptStatuses: WorkspaceAgentScriptStatus[] = [ + "exit_failure", + "ok", + "pipes_left_open", + "timed_out", +]; + // From codersdk/workspaceagents.go export type WorkspaceAgentStartupScriptBehavior = "blocking" | "non-blocking";