mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd/database): improve task status in tasks_with_status view (#20683)
This change restructures the `tasks_with_status` view query to: - Improve debuggability by adding a `status_debug` column to better understand the outcome - Reduce clutter from `bool_or`, `bool_and` which are aggregate functions that did not actually have serve a purpose (each join is 0-1 rows) - Improve agent lifecycle state coverage, `start_timeout` and `start_error` were omitted - These states are easy to trigger even in a perfectly functioning workspace/task so we now rely on app health to report whether or not there was an issue - Mark canceling and canceled workspace build jobs as error state - Agent stop states were implicitly `unknown`, now there are explicit (I initially considered `error`, could go either way)
This commit is contained in:
Generated
+36
-28
@@ -1965,32 +1965,21 @@ CREATE VIEW tasks_with_status AS
|
||||
tasks.created_at,
|
||||
tasks.deleted_at,
|
||||
CASE
|
||||
WHEN ((tasks.workspace_id IS NULL) OR (latest_build.job_status IS NULL)) THEN 'pending'::task_status
|
||||
WHEN (latest_build.job_status = 'failed'::provisioner_job_status) THEN 'error'::task_status
|
||||
WHEN ((latest_build.transition = ANY (ARRAY['stop'::workspace_transition, 'delete'::workspace_transition])) AND (latest_build.job_status = 'succeeded'::provisioner_job_status)) THEN 'paused'::task_status
|
||||
WHEN ((latest_build.transition = 'start'::workspace_transition) AND (latest_build.job_status = 'pending'::provisioner_job_status)) THEN 'initializing'::task_status
|
||||
WHEN ((latest_build.transition = 'start'::workspace_transition) AND (latest_build.job_status = ANY (ARRAY['running'::provisioner_job_status, 'succeeded'::provisioner_job_status]))) THEN
|
||||
CASE
|
||||
WHEN agent_status."none" THEN 'initializing'::task_status
|
||||
WHEN agent_status.connecting THEN 'initializing'::task_status
|
||||
WHEN agent_status.connected THEN
|
||||
CASE
|
||||
WHEN app_status.any_unhealthy THEN 'error'::task_status
|
||||
WHEN app_status.any_initializing THEN 'initializing'::task_status
|
||||
WHEN app_status.all_healthy_or_disabled THEN 'active'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END
|
||||
ELSE 'unknown'::task_status
|
||||
END
|
||||
ELSE 'unknown'::task_status
|
||||
WHEN (tasks.workspace_id IS NULL) THEN 'pending'::task_status
|
||||
WHEN (build_status.status <> 'active'::task_status) THEN build_status.status
|
||||
WHEN (agent_status.status <> 'active'::task_status) THEN agent_status.status
|
||||
ELSE app_status.status
|
||||
END AS status,
|
||||
jsonb_build_object('build', jsonb_build_object('transition', latest_build_raw.transition, 'job_status', latest_build_raw.job_status, 'computed', build_status.status), 'agent', jsonb_build_object('lifecycle_state', agent_raw.lifecycle_state, 'computed', agent_status.status), 'app', jsonb_build_object('health', app_raw.health, 'computed', app_status.status)) AS status_debug,
|
||||
task_app.workspace_build_number,
|
||||
task_app.workspace_agent_id,
|
||||
task_app.workspace_app_id,
|
||||
agent_raw.lifecycle_state AS workspace_agent_lifecycle_state,
|
||||
app_raw.health AS workspace_app_health,
|
||||
task_owner.owner_username,
|
||||
task_owner.owner_name,
|
||||
task_owner.owner_avatar_url
|
||||
FROM (((((tasks
|
||||
FROM ((((((((tasks
|
||||
CROSS JOIN LATERAL ( SELECT vu.username AS owner_username,
|
||||
vu.name AS owner_name,
|
||||
vu.avatar_url AS owner_avatar_url
|
||||
@@ -2008,17 +1997,36 @@ CREATE VIEW tasks_with_status AS
|
||||
workspace_build.job_id
|
||||
FROM (workspace_builds workspace_build
|
||||
JOIN provisioner_jobs provisioner_job ON ((provisioner_job.id = workspace_build.job_id)))
|
||||
WHERE ((workspace_build.workspace_id = tasks.workspace_id) AND (workspace_build.build_number = task_app.workspace_build_number))) latest_build ON (true))
|
||||
CROSS JOIN LATERAL ( SELECT (count(*) = 0) AS "none",
|
||||
bool_or((workspace_agent.lifecycle_state = ANY (ARRAY['created'::workspace_agent_lifecycle_state, 'starting'::workspace_agent_lifecycle_state]))) AS connecting,
|
||||
bool_and((workspace_agent.lifecycle_state = 'ready'::workspace_agent_lifecycle_state)) AS connected
|
||||
WHERE ((workspace_build.workspace_id = tasks.workspace_id) AND (workspace_build.build_number = task_app.workspace_build_number))) latest_build_raw ON (true))
|
||||
LEFT JOIN LATERAL ( SELECT workspace_agent.lifecycle_state
|
||||
FROM workspace_agents workspace_agent
|
||||
WHERE (workspace_agent.id = task_app.workspace_agent_id)) agent_status)
|
||||
CROSS JOIN LATERAL ( SELECT bool_or((workspace_app.health = 'unhealthy'::workspace_app_health)) AS any_unhealthy,
|
||||
bool_or((workspace_app.health = 'initializing'::workspace_app_health)) AS any_initializing,
|
||||
bool_and((workspace_app.health = ANY (ARRAY['healthy'::workspace_app_health, 'disabled'::workspace_app_health]))) AS all_healthy_or_disabled
|
||||
WHERE (workspace_agent.id = task_app.workspace_agent_id)) agent_raw ON (true))
|
||||
LEFT JOIN LATERAL ( SELECT workspace_app.health
|
||||
FROM workspace_apps workspace_app
|
||||
WHERE (workspace_app.id = task_app.workspace_app_id)) app_status)
|
||||
WHERE (workspace_app.id = task_app.workspace_app_id)) app_raw ON (true))
|
||||
CROSS JOIN LATERAL ( SELECT
|
||||
CASE
|
||||
WHEN (latest_build_raw.job_status IS NULL) THEN 'pending'::task_status
|
||||
WHEN (latest_build_raw.job_status = ANY (ARRAY['failed'::provisioner_job_status, 'canceling'::provisioner_job_status, 'canceled'::provisioner_job_status])) THEN 'error'::task_status
|
||||
WHEN ((latest_build_raw.transition = ANY (ARRAY['stop'::workspace_transition, 'delete'::workspace_transition])) AND (latest_build_raw.job_status = 'succeeded'::provisioner_job_status)) THEN 'paused'::task_status
|
||||
WHEN ((latest_build_raw.transition = 'start'::workspace_transition) AND (latest_build_raw.job_status = 'pending'::provisioner_job_status)) THEN 'initializing'::task_status
|
||||
WHEN ((latest_build_raw.transition = 'start'::workspace_transition) AND (latest_build_raw.job_status = ANY (ARRAY['running'::provisioner_job_status, 'succeeded'::provisioner_job_status]))) THEN 'active'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status) build_status)
|
||||
CROSS JOIN LATERAL ( SELECT
|
||||
CASE
|
||||
WHEN ((agent_raw.lifecycle_state IS NULL) OR (agent_raw.lifecycle_state = ANY (ARRAY['created'::workspace_agent_lifecycle_state, 'starting'::workspace_agent_lifecycle_state]))) THEN 'initializing'::task_status
|
||||
WHEN (agent_raw.lifecycle_state = ANY (ARRAY['ready'::workspace_agent_lifecycle_state, 'start_timeout'::workspace_agent_lifecycle_state, 'start_error'::workspace_agent_lifecycle_state])) THEN 'active'::task_status
|
||||
WHEN (agent_raw.lifecycle_state <> ALL (ARRAY['created'::workspace_agent_lifecycle_state, 'starting'::workspace_agent_lifecycle_state, 'ready'::workspace_agent_lifecycle_state, 'start_timeout'::workspace_agent_lifecycle_state, 'start_error'::workspace_agent_lifecycle_state])) THEN 'unknown'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status) agent_status)
|
||||
CROSS JOIN LATERAL ( SELECT
|
||||
CASE
|
||||
WHEN (app_raw.health = 'initializing'::workspace_app_health) THEN 'initializing'::task_status
|
||||
WHEN (app_raw.health = 'unhealthy'::workspace_app_health) THEN 'error'::task_status
|
||||
WHEN (app_raw.health = ANY (ARRAY['healthy'::workspace_app_health, 'disabled'::workspace_app_health])) THEN 'active'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status) app_status)
|
||||
WHERE (tasks.deleted_at IS NULL);
|
||||
|
||||
CREATE TABLE telemetry_items (
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
-- Restore previous view.
|
||||
DROP VIEW IF EXISTS tasks_with_status;
|
||||
|
||||
CREATE VIEW
|
||||
tasks_with_status
|
||||
AS
|
||||
SELECT
|
||||
tasks.*,
|
||||
CASE
|
||||
WHEN tasks.workspace_id IS NULL OR latest_build.job_status IS NULL THEN 'pending'::task_status
|
||||
|
||||
WHEN latest_build.job_status = 'failed' THEN 'error'::task_status
|
||||
|
||||
WHEN latest_build.transition IN ('stop', 'delete')
|
||||
AND latest_build.job_status = 'succeeded' THEN 'paused'::task_status
|
||||
|
||||
WHEN latest_build.transition = 'start'
|
||||
AND latest_build.job_status = 'pending' THEN 'initializing'::task_status
|
||||
|
||||
WHEN latest_build.transition = 'start' AND latest_build.job_status IN ('running', 'succeeded') THEN
|
||||
CASE
|
||||
WHEN agent_status.none THEN 'initializing'::task_status
|
||||
WHEN agent_status.connecting THEN 'initializing'::task_status
|
||||
WHEN agent_status.connected THEN
|
||||
CASE
|
||||
WHEN app_status.any_unhealthy THEN 'error'::task_status
|
||||
WHEN app_status.any_initializing THEN 'initializing'::task_status
|
||||
WHEN app_status.all_healthy_or_disabled THEN 'active'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END
|
||||
ELSE 'unknown'::task_status
|
||||
END
|
||||
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status,
|
||||
task_app.*,
|
||||
task_owner.*
|
||||
FROM
|
||||
tasks
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
vu.username AS owner_username,
|
||||
vu.name AS owner_name,
|
||||
vu.avatar_url AS owner_avatar_url
|
||||
FROM visible_users vu
|
||||
WHERE vu.id = tasks.owner_id
|
||||
) task_owner
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT workspace_build_number, workspace_agent_id, workspace_app_id
|
||||
FROM task_workspace_apps task_app
|
||||
WHERE task_id = tasks.id
|
||||
ORDER BY workspace_build_number DESC
|
||||
LIMIT 1
|
||||
) task_app ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
workspace_build.transition,
|
||||
provisioner_job.job_status,
|
||||
workspace_build.job_id
|
||||
FROM workspace_builds workspace_build
|
||||
JOIN provisioner_jobs provisioner_job ON provisioner_job.id = workspace_build.job_id
|
||||
WHERE workspace_build.workspace_id = tasks.workspace_id
|
||||
AND workspace_build.build_number = task_app.workspace_build_number
|
||||
) latest_build ON TRUE
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
COUNT(*) = 0 AS none,
|
||||
bool_or(workspace_agent.lifecycle_state IN ('created', 'starting')) AS connecting,
|
||||
bool_and(workspace_agent.lifecycle_state = 'ready') AS connected
|
||||
FROM workspace_agents workspace_agent
|
||||
WHERE workspace_agent.id = task_app.workspace_agent_id
|
||||
) agent_status
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
bool_or(workspace_app.health = 'unhealthy') AS any_unhealthy,
|
||||
bool_or(workspace_app.health = 'initializing') AS any_initializing,
|
||||
bool_and(workspace_app.health IN ('healthy', 'disabled')) AS all_healthy_or_disabled
|
||||
FROM workspace_apps workspace_app
|
||||
WHERE workspace_app.id = task_app.workspace_app_id
|
||||
) app_status
|
||||
WHERE
|
||||
tasks.deleted_at IS NULL;
|
||||
@@ -0,0 +1,142 @@
|
||||
-- Update task status in view.
|
||||
DROP VIEW IF EXISTS tasks_with_status;
|
||||
|
||||
CREATE VIEW
|
||||
tasks_with_status
|
||||
AS
|
||||
SELECT
|
||||
tasks.*,
|
||||
-- Combine component statuses with precedence: build -> agent -> app.
|
||||
CASE
|
||||
WHEN tasks.workspace_id IS NULL THEN 'pending'::task_status
|
||||
WHEN build_status.status != 'active' THEN build_status.status::task_status
|
||||
WHEN agent_status.status != 'active' THEN agent_status.status::task_status
|
||||
ELSE app_status.status::task_status
|
||||
END AS status,
|
||||
-- Attach debug information for troubleshooting status.
|
||||
jsonb_build_object(
|
||||
'build', jsonb_build_object(
|
||||
'transition', latest_build_raw.transition,
|
||||
'job_status', latest_build_raw.job_status,
|
||||
'computed', build_status.status
|
||||
),
|
||||
'agent', jsonb_build_object(
|
||||
'lifecycle_state', agent_raw.lifecycle_state,
|
||||
'computed', agent_status.status
|
||||
),
|
||||
'app', jsonb_build_object(
|
||||
'health', app_raw.health,
|
||||
'computed', app_status.status
|
||||
)
|
||||
) AS status_debug,
|
||||
task_app.*,
|
||||
agent_raw.lifecycle_state AS workspace_agent_lifecycle_state,
|
||||
app_raw.health AS workspace_app_health,
|
||||
task_owner.*
|
||||
FROM
|
||||
tasks
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
vu.username AS owner_username,
|
||||
vu.name AS owner_name,
|
||||
vu.avatar_url AS owner_avatar_url
|
||||
FROM
|
||||
visible_users vu
|
||||
WHERE
|
||||
vu.id = tasks.owner_id
|
||||
) task_owner
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
task_app.workspace_build_number,
|
||||
task_app.workspace_agent_id,
|
||||
task_app.workspace_app_id
|
||||
FROM
|
||||
task_workspace_apps task_app
|
||||
WHERE
|
||||
task_id = tasks.id
|
||||
ORDER BY
|
||||
task_app.workspace_build_number DESC
|
||||
LIMIT 1
|
||||
) task_app ON TRUE
|
||||
|
||||
-- Join the raw data for computing task status.
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
workspace_build.transition,
|
||||
provisioner_job.job_status,
|
||||
workspace_build.job_id
|
||||
FROM
|
||||
workspace_builds workspace_build
|
||||
JOIN
|
||||
provisioner_jobs provisioner_job
|
||||
ON provisioner_job.id = workspace_build.job_id
|
||||
WHERE
|
||||
workspace_build.workspace_id = tasks.workspace_id
|
||||
AND workspace_build.build_number = task_app.workspace_build_number
|
||||
) latest_build_raw ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
workspace_agent.lifecycle_state
|
||||
FROM
|
||||
workspace_agents workspace_agent
|
||||
WHERE
|
||||
workspace_agent.id = task_app.workspace_agent_id
|
||||
) agent_raw ON TRUE
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT
|
||||
workspace_app.health
|
||||
FROM
|
||||
workspace_apps workspace_app
|
||||
WHERE
|
||||
workspace_app.id = task_app.workspace_app_id
|
||||
) app_raw ON TRUE
|
||||
|
||||
-- Compute the status for each component.
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
CASE
|
||||
WHEN latest_build_raw.job_status IS NULL THEN 'pending'::task_status
|
||||
WHEN latest_build_raw.job_status IN ('failed', 'canceling', 'canceled') THEN 'error'::task_status
|
||||
WHEN
|
||||
latest_build_raw.transition IN ('stop', 'delete')
|
||||
AND latest_build_raw.job_status = 'succeeded' THEN 'paused'::task_status
|
||||
WHEN
|
||||
latest_build_raw.transition = 'start'
|
||||
AND latest_build_raw.job_status = 'pending' THEN 'initializing'::task_status
|
||||
-- Build is running or done, defer to agent/app status.
|
||||
WHEN
|
||||
latest_build_raw.transition = 'start'
|
||||
AND latest_build_raw.job_status IN ('running', 'succeeded') THEN 'active'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status
|
||||
) build_status
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
CASE
|
||||
-- No agent or connecting.
|
||||
WHEN
|
||||
agent_raw.lifecycle_state IS NULL
|
||||
OR agent_raw.lifecycle_state IN ('created', 'starting') THEN 'initializing'::task_status
|
||||
-- Agent is running, defer to app status.
|
||||
-- NOTE(mafredri): The start_error/start_timeout states means connected, but some startup script failed.
|
||||
-- This may or may not affect the task status but this has to be caught by app health check.
|
||||
WHEN agent_raw.lifecycle_state IN ('ready', 'start_timeout', 'start_error') THEN 'active'::task_status
|
||||
-- If the agent is shutting down or turned off, this is an unknown state because we would expect a stop
|
||||
-- build to be running.
|
||||
-- This is essentially equal to: `IN ('shutting_down', 'shutdown_timeout', 'shutdown_error', 'off')`,
|
||||
-- but we cannot use them because the values were added in a migration.
|
||||
WHEN agent_raw.lifecycle_state NOT IN ('created', 'starting', 'ready', 'start_timeout', 'start_error') THEN 'unknown'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status
|
||||
) agent_status
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT
|
||||
CASE
|
||||
WHEN app_raw.health = 'initializing' THEN 'initializing'::task_status
|
||||
WHEN app_raw.health = 'unhealthy' THEN 'error'::task_status
|
||||
WHEN app_raw.health IN ('healthy', 'disabled') THEN 'active'::task_status
|
||||
ELSE 'unknown'::task_status
|
||||
END AS status
|
||||
) app_status
|
||||
WHERE
|
||||
tasks.deleted_at IS NULL;
|
||||
+20
-17
@@ -4208,23 +4208,26 @@ type TailnetTunnel struct {
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
|
||||
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
|
||||
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
|
||||
TemplateParameters json.RawMessage `db:"template_parameters" json:"template_parameters"`
|
||||
Prompt string `db:"prompt" json:"prompt"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
DeletedAt sql.NullTime `db:"deleted_at" json:"deleted_at"`
|
||||
Status TaskStatus `db:"status" json:"status"`
|
||||
WorkspaceBuildNumber sql.NullInt32 `db:"workspace_build_number" json:"workspace_build_number"`
|
||||
WorkspaceAgentID uuid.NullUUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
WorkspaceAppID uuid.NullUUID `db:"workspace_app_id" json:"workspace_app_id"`
|
||||
OwnerUsername string `db:"owner_username" json:"owner_username"`
|
||||
OwnerName string `db:"owner_name" json:"owner_name"`
|
||||
OwnerAvatarUrl string `db:"owner_avatar_url" json:"owner_avatar_url"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
|
||||
OwnerID uuid.UUID `db:"owner_id" json:"owner_id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
WorkspaceID uuid.NullUUID `db:"workspace_id" json:"workspace_id"`
|
||||
TemplateVersionID uuid.UUID `db:"template_version_id" json:"template_version_id"`
|
||||
TemplateParameters json.RawMessage `db:"template_parameters" json:"template_parameters"`
|
||||
Prompt string `db:"prompt" json:"prompt"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
DeletedAt sql.NullTime `db:"deleted_at" json:"deleted_at"`
|
||||
Status TaskStatus `db:"status" json:"status"`
|
||||
StatusDebug json.RawMessage `db:"status_debug" json:"status_debug"`
|
||||
WorkspaceBuildNumber sql.NullInt32 `db:"workspace_build_number" json:"workspace_build_number"`
|
||||
WorkspaceAgentID uuid.NullUUID `db:"workspace_agent_id" json:"workspace_agent_id"`
|
||||
WorkspaceAppID uuid.NullUUID `db:"workspace_app_id" json:"workspace_app_id"`
|
||||
WorkspaceAgentLifecycleState NullWorkspaceAgentLifecycleState `db:"workspace_agent_lifecycle_state" json:"workspace_agent_lifecycle_state"`
|
||||
WorkspaceAppHealth NullWorkspaceAppHealth `db:"workspace_app_health" json:"workspace_app_health"`
|
||||
OwnerUsername string `db:"owner_username" json:"owner_username"`
|
||||
OwnerName string `db:"owner_name" json:"owner_name"`
|
||||
OwnerAvatarUrl string `db:"owner_avatar_url" json:"owner_avatar_url"`
|
||||
}
|
||||
|
||||
type TaskTable struct {
|
||||
|
||||
@@ -6664,6 +6664,23 @@ func TestTasksWithStatusView(t *testing.T) {
|
||||
StartedAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
CompletedAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
}
|
||||
case database.ProvisionerJobStatusCanceling:
|
||||
jobParams = database.ProvisionerJob{
|
||||
OrganizationID: org.ID,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
InitiatorID: user.ID,
|
||||
StartedAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
CanceledAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
}
|
||||
case database.ProvisionerJobStatusCanceled:
|
||||
jobParams = database.ProvisionerJob{
|
||||
OrganizationID: org.ID,
|
||||
Type: database.ProvisionerJobTypeWorkspaceBuild,
|
||||
InitiatorID: user.ID,
|
||||
StartedAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
CompletedAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
CanceledAt: sql.NullTime{Valid: true, Time: dbtime.Now()},
|
||||
}
|
||||
default:
|
||||
t.Errorf("invalid build status: %v", buildStatus)
|
||||
}
|
||||
@@ -6816,6 +6833,28 @@ func TestTasksWithStatusView(t *testing.T) {
|
||||
expectWorkspaceAgentValid: false,
|
||||
expectWorkspaceAppValid: false,
|
||||
},
|
||||
{
|
||||
name: "CancelingBuild",
|
||||
buildStatus: database.ProvisionerJobStatusCanceling,
|
||||
buildTransition: database.WorkspaceTransitionStart,
|
||||
expectedStatus: database.TaskStatusError,
|
||||
description: "Latest workspace build is canceling",
|
||||
expectBuildNumberValid: true,
|
||||
expectBuildNumber: 1,
|
||||
expectWorkspaceAgentValid: false,
|
||||
expectWorkspaceAppValid: false,
|
||||
},
|
||||
{
|
||||
name: "CanceledBuild",
|
||||
buildStatus: database.ProvisionerJobStatusCanceled,
|
||||
buildTransition: database.WorkspaceTransitionStart,
|
||||
expectedStatus: database.TaskStatusError,
|
||||
description: "Latest workspace build was canceled",
|
||||
expectBuildNumberValid: true,
|
||||
expectBuildNumber: 1,
|
||||
expectWorkspaceAgentValid: false,
|
||||
expectWorkspaceAppValid: false,
|
||||
},
|
||||
{
|
||||
name: "StoppedWorkspace",
|
||||
buildStatus: database.ProvisionerJobStatusSucceeded,
|
||||
@@ -6943,24 +6982,26 @@ func TestTasksWithStatusView(t *testing.T) {
|
||||
buildStatus: database.ProvisionerJobStatusSucceeded,
|
||||
buildTransition: database.WorkspaceTransitionStart,
|
||||
agentState: database.WorkspaceAgentLifecycleStateStartTimeout,
|
||||
expectedStatus: database.TaskStatusUnknown,
|
||||
description: "Agent start timed out",
|
||||
appHealths: []database.WorkspaceAppHealth{database.WorkspaceAppHealthHealthy},
|
||||
expectedStatus: database.TaskStatusActive,
|
||||
description: "Agent start timed out but app is healthy, defer to app",
|
||||
expectBuildNumberValid: true,
|
||||
expectBuildNumber: 1,
|
||||
expectWorkspaceAgentValid: true,
|
||||
expectWorkspaceAppValid: false,
|
||||
expectWorkspaceAppValid: true,
|
||||
},
|
||||
{
|
||||
name: "AgentStartError",
|
||||
buildStatus: database.ProvisionerJobStatusSucceeded,
|
||||
buildTransition: database.WorkspaceTransitionStart,
|
||||
agentState: database.WorkspaceAgentLifecycleStateStartError,
|
||||
expectedStatus: database.TaskStatusUnknown,
|
||||
description: "Agent failed to start",
|
||||
appHealths: []database.WorkspaceAppHealth{database.WorkspaceAppHealthHealthy},
|
||||
expectedStatus: database.TaskStatusActive,
|
||||
description: "Agent start failed but app is healthy, defer to app",
|
||||
expectBuildNumberValid: true,
|
||||
expectBuildNumber: 1,
|
||||
expectWorkspaceAgentValid: true,
|
||||
expectWorkspaceAppValid: false,
|
||||
expectWorkspaceAppValid: true,
|
||||
},
|
||||
{
|
||||
name: "AgentShuttingDown",
|
||||
@@ -7081,6 +7122,8 @@ func TestTasksWithStatusView(t *testing.T) {
|
||||
got, err := db.GetTaskByID(ctx, task.ID)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Logf("Task status debug: %s", got.StatusDebug)
|
||||
|
||||
require.Equal(t, tt.expectedStatus, got.Status)
|
||||
|
||||
require.Equal(t, tt.expectBuildNumberValid, got.WorkspaceBuildNumber.Valid)
|
||||
|
||||
@@ -13072,7 +13072,7 @@ func (q *sqlQuerier) DeleteTask(ctx context.Context, arg DeleteTaskParams) (Task
|
||||
}
|
||||
|
||||
const getTaskByID = `-- name: GetTaskByID :one
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, workspace_build_number, workspace_agent_id, workspace_app_id, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE id = $1::uuid
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE id = $1::uuid
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error) {
|
||||
@@ -13090,9 +13090,12 @@ func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error
|
||||
&i.CreatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Status,
|
||||
&i.StatusDebug,
|
||||
&i.WorkspaceBuildNumber,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.WorkspaceAppID,
|
||||
&i.WorkspaceAgentLifecycleState,
|
||||
&i.WorkspaceAppHealth,
|
||||
&i.OwnerUsername,
|
||||
&i.OwnerName,
|
||||
&i.OwnerAvatarUrl,
|
||||
@@ -13101,7 +13104,7 @@ func (q *sqlQuerier) GetTaskByID(ctx context.Context, id uuid.UUID) (Task, error
|
||||
}
|
||||
|
||||
const getTaskByOwnerIDAndName = `-- name: GetTaskByOwnerIDAndName :one
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, workspace_build_number, workspace_agent_id, workspace_app_id, owner_username, owner_name, owner_avatar_url FROM tasks_with_status
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status
|
||||
WHERE
|
||||
owner_id = $1::uuid
|
||||
AND deleted_at IS NULL
|
||||
@@ -13128,9 +13131,12 @@ func (q *sqlQuerier) GetTaskByOwnerIDAndName(ctx context.Context, arg GetTaskByO
|
||||
&i.CreatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Status,
|
||||
&i.StatusDebug,
|
||||
&i.WorkspaceBuildNumber,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.WorkspaceAppID,
|
||||
&i.WorkspaceAgentLifecycleState,
|
||||
&i.WorkspaceAppHealth,
|
||||
&i.OwnerUsername,
|
||||
&i.OwnerName,
|
||||
&i.OwnerAvatarUrl,
|
||||
@@ -13139,7 +13145,7 @@ func (q *sqlQuerier) GetTaskByOwnerIDAndName(ctx context.Context, arg GetTaskByO
|
||||
}
|
||||
|
||||
const getTaskByWorkspaceID = `-- name: GetTaskByWorkspaceID :one
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, workspace_build_number, workspace_agent_id, workspace_app_id, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE workspace_id = $1::uuid
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status WHERE workspace_id = $1::uuid
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (Task, error) {
|
||||
@@ -13157,9 +13163,12 @@ func (q *sqlQuerier) GetTaskByWorkspaceID(ctx context.Context, workspaceID uuid.
|
||||
&i.CreatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Status,
|
||||
&i.StatusDebug,
|
||||
&i.WorkspaceBuildNumber,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.WorkspaceAppID,
|
||||
&i.WorkspaceAgentLifecycleState,
|
||||
&i.WorkspaceAppHealth,
|
||||
&i.OwnerUsername,
|
||||
&i.OwnerName,
|
||||
&i.OwnerAvatarUrl,
|
||||
@@ -13216,7 +13225,7 @@ func (q *sqlQuerier) InsertTask(ctx context.Context, arg InsertTaskParams) (Task
|
||||
}
|
||||
|
||||
const listTasks = `-- name: ListTasks :many
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, workspace_build_number, workspace_agent_id, workspace_app_id, owner_username, owner_name, owner_avatar_url FROM tasks_with_status tws
|
||||
SELECT id, organization_id, owner_id, name, workspace_id, template_version_id, template_parameters, prompt, created_at, deleted_at, status, status_debug, workspace_build_number, workspace_agent_id, workspace_app_id, workspace_agent_lifecycle_state, workspace_app_health, owner_username, owner_name, owner_avatar_url FROM tasks_with_status tws
|
||||
WHERE tws.deleted_at IS NULL
|
||||
AND CASE WHEN $1::UUID != '00000000-0000-0000-0000-000000000000' THEN tws.owner_id = $1::UUID ELSE TRUE END
|
||||
AND CASE WHEN $2::UUID != '00000000-0000-0000-0000-000000000000' THEN tws.organization_id = $2::UUID ELSE TRUE END
|
||||
@@ -13251,9 +13260,12 @@ func (q *sqlQuerier) ListTasks(ctx context.Context, arg ListTasksParams) ([]Task
|
||||
&i.CreatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.Status,
|
||||
&i.StatusDebug,
|
||||
&i.WorkspaceBuildNumber,
|
||||
&i.WorkspaceAgentID,
|
||||
&i.WorkspaceAppID,
|
||||
&i.WorkspaceAgentLifecycleState,
|
||||
&i.WorkspaceAppHealth,
|
||||
&i.OwnerUsername,
|
||||
&i.OwnerName,
|
||||
&i.OwnerAvatarUrl,
|
||||
|
||||
@@ -106,6 +106,15 @@ sql:
|
||||
# Workaround for sqlc not interpreting the left join correctly.
|
||||
- column: "tasks_with_status.workspace_build_number"
|
||||
go_type: "database/sql.NullInt32"
|
||||
- column: "tasks_with_status.status"
|
||||
go_type:
|
||||
type: "TaskStatus"
|
||||
- column: "tasks_with_status.workspace_agent_lifecycle_state"
|
||||
go_type:
|
||||
type: "NullWorkspaceAgentLifecycleState"
|
||||
- column: "tasks_with_status.workspace_app_health"
|
||||
go_type:
|
||||
type: "NullWorkspaceAppHealth"
|
||||
rename:
|
||||
group_member: GroupMemberTable
|
||||
group_members_expanded: GroupMember
|
||||
|
||||
Reference in New Issue
Block a user