feat: add agent timings (#14713)

* feat: begin impl of agent script timings

* feat: add job_id and display_name to script timings

* fix: increment migration number

* fix: rename migrations from 251 to 254

* test: get tests compiling

* fix: appease the linter

* fix: get tests passing again

* fix: drop column from correct table

* test: add fixture for agent script timings

* fix: typo

* fix: use job id used in provisioner job timings

* fix: increment migration number

* test: behaviour of script runner

* test: rewrite test

* test: does exit 1 script break things?

* test: rewrite test again

* fix: revert change

Not sure how this came to be, I do not recall manually changing
these files.

* fix: let code breathe

* fix: wrap errors

* fix: justify nolint

* fix: swap require.Equal argument order

* fix: add mutex operations

* feat: add 'ran_on_start' and 'blocked_login' fields

* fix: update testdata fixture

* fix: refer to agent_id instead of job_id in timings

* fix: JobID -> AgentID in dbauthz_test

* fix: add 'id' to scripts, make timing refer to script id

* fix: fix broken tests and convert bug

* fix: update testdata fixtures

* fix: update testdata fixtures again

* feat: capture stage and if script timed out

* fix: update migration number

* test: add test for script api

* fix: fake db query

* fix: use UTC time

* fix: ensure r.scriptComplete is not nil

* fix: move err check to right after call

* fix: uppercase sql

* fix: use dbtime.Now()

* fix: debug log on r.scriptCompleted being nil

* fix: ensure correct rbac permissions

* chore: remove DisplayName

* fix: get tests passing

* fix: remove space in sql up

* docs: document ExecuteOption

* fix: drop 'RETURNING' from sql

* chore: remove 'display_name' from timing table

* fix: testdata fixture

* fix: put r.scriptCompleted call in goroutine

* fix: track goroutine for test + use separate context for reporting

* fix: appease linter, handle trackCommandGoroutine error

* fix: resolve race condition

* feat: replace timed_out column with status column

* test: update testdata fixture

* fix: apply suggestions from review

* revert: linter changes
This commit is contained in:
Danielle Maywood
2024-09-24 10:51:49 +01:00
committed by GitHub
parent b8944074c4
commit ae522c558d
43 changed files with 1367 additions and 232 deletions
+137
View File
@@ -1881,6 +1881,133 @@ func AllWorkspaceAgentLifecycleStateValues() []WorkspaceAgentLifecycleState {
}
}
// What stage the script was ran in.
type WorkspaceAgentScriptTimingStage string
const (
WorkspaceAgentScriptTimingStageStart WorkspaceAgentScriptTimingStage = "start"
WorkspaceAgentScriptTimingStageStop WorkspaceAgentScriptTimingStage = "stop"
WorkspaceAgentScriptTimingStageCron WorkspaceAgentScriptTimingStage = "cron"
)
func (e *WorkspaceAgentScriptTimingStage) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = WorkspaceAgentScriptTimingStage(s)
case string:
*e = WorkspaceAgentScriptTimingStage(s)
default:
return fmt.Errorf("unsupported scan type for WorkspaceAgentScriptTimingStage: %T", src)
}
return nil
}
type NullWorkspaceAgentScriptTimingStage struct {
WorkspaceAgentScriptTimingStage WorkspaceAgentScriptTimingStage `json:"workspace_agent_script_timing_stage"`
Valid bool `json:"valid"` // Valid is true if WorkspaceAgentScriptTimingStage is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullWorkspaceAgentScriptTimingStage) Scan(value interface{}) error {
if value == nil {
ns.WorkspaceAgentScriptTimingStage, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.WorkspaceAgentScriptTimingStage.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullWorkspaceAgentScriptTimingStage) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.WorkspaceAgentScriptTimingStage), nil
}
func (e WorkspaceAgentScriptTimingStage) Valid() bool {
switch e {
case WorkspaceAgentScriptTimingStageStart,
WorkspaceAgentScriptTimingStageStop,
WorkspaceAgentScriptTimingStageCron:
return true
}
return false
}
func AllWorkspaceAgentScriptTimingStageValues() []WorkspaceAgentScriptTimingStage {
return []WorkspaceAgentScriptTimingStage{
WorkspaceAgentScriptTimingStageStart,
WorkspaceAgentScriptTimingStageStop,
WorkspaceAgentScriptTimingStageCron,
}
}
// What the exit status of the script is.
type WorkspaceAgentScriptTimingStatus string
const (
WorkspaceAgentScriptTimingStatusOk WorkspaceAgentScriptTimingStatus = "ok"
WorkspaceAgentScriptTimingStatusExitFailure WorkspaceAgentScriptTimingStatus = "exit_failure"
WorkspaceAgentScriptTimingStatusTimedOut WorkspaceAgentScriptTimingStatus = "timed_out"
WorkspaceAgentScriptTimingStatusPipesLeftOpen WorkspaceAgentScriptTimingStatus = "pipes_left_open"
)
func (e *WorkspaceAgentScriptTimingStatus) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = WorkspaceAgentScriptTimingStatus(s)
case string:
*e = WorkspaceAgentScriptTimingStatus(s)
default:
return fmt.Errorf("unsupported scan type for WorkspaceAgentScriptTimingStatus: %T", src)
}
return nil
}
type NullWorkspaceAgentScriptTimingStatus struct {
WorkspaceAgentScriptTimingStatus WorkspaceAgentScriptTimingStatus `json:"workspace_agent_script_timing_status"`
Valid bool `json:"valid"` // Valid is true if WorkspaceAgentScriptTimingStatus is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullWorkspaceAgentScriptTimingStatus) Scan(value interface{}) error {
if value == nil {
ns.WorkspaceAgentScriptTimingStatus, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.WorkspaceAgentScriptTimingStatus.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullWorkspaceAgentScriptTimingStatus) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.WorkspaceAgentScriptTimingStatus), nil
}
func (e WorkspaceAgentScriptTimingStatus) Valid() bool {
switch e {
case WorkspaceAgentScriptTimingStatusOk,
WorkspaceAgentScriptTimingStatusExitFailure,
WorkspaceAgentScriptTimingStatusTimedOut,
WorkspaceAgentScriptTimingStatusPipesLeftOpen:
return true
}
return false
}
func AllWorkspaceAgentScriptTimingStatusValues() []WorkspaceAgentScriptTimingStatus {
return []WorkspaceAgentScriptTimingStatus{
WorkspaceAgentScriptTimingStatusOk,
WorkspaceAgentScriptTimingStatusExitFailure,
WorkspaceAgentScriptTimingStatusTimedOut,
WorkspaceAgentScriptTimingStatusPipesLeftOpen,
}
}
type WorkspaceAgentSubsystem string
const (
@@ -2881,6 +3008,16 @@ type WorkspaceAgentScript struct {
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"`
}
type WorkspaceAgentScriptTiming struct {
ScriptID uuid.UUID `db:"script_id" json:"script_id"`
StartedAt time.Time `db:"started_at" json:"started_at"`
EndedAt time.Time `db:"ended_at" json:"ended_at"`
ExitCode int32 `db:"exit_code" json:"exit_code"`
Stage WorkspaceAgentScriptTimingStage `db:"stage" json:"stage"`
Status WorkspaceAgentScriptTimingStatus `db:"status" json:"status"`
}
type WorkspaceAgentStat struct {