mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
911d734df9
Parent agents were re-using AuthInstanceID when spawning child agents. This caused GetWorkspaceAgentByInstanceID to return the most recently created sub agent instead of the parent when the parent tried to refetch its own manifest. Fix by not reusing AuthInstanceID for sub agents, and updating GetWorkspaceAgentByInstanceID to filter them out entirely.
480 lines
12 KiB
SQL
480 lines
12 KiB
SQL
-- name: GetWorkspaceAgentByID :one
|
|
SELECT
|
|
*
|
|
FROM
|
|
workspace_agents
|
|
WHERE
|
|
id = $1
|
|
-- Filter out deleted sub agents.
|
|
AND deleted = FALSE;
|
|
|
|
-- name: GetWorkspaceAgentByInstanceID :one
|
|
SELECT
|
|
*
|
|
FROM
|
|
workspace_agents
|
|
WHERE
|
|
auth_instance_id = @auth_instance_id :: TEXT
|
|
-- Filter out deleted sub agents.
|
|
AND deleted = FALSE
|
|
-- Filter out sub agents, they do not authenticate with auth_instance_id.
|
|
AND parent_id IS NULL
|
|
ORDER BY
|
|
created_at DESC;
|
|
|
|
-- name: GetWorkspaceAgentsByResourceIDs :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
workspace_agents
|
|
WHERE
|
|
resource_id = ANY(@ids :: uuid [ ])
|
|
-- Filter out deleted sub agents.
|
|
AND deleted = FALSE;
|
|
|
|
-- name: GetWorkspaceAgentsCreatedAfter :many
|
|
SELECT * FROM workspace_agents
|
|
WHERE
|
|
created_at > $1
|
|
-- Filter out deleted sub agents.
|
|
AND deleted = FALSE;
|
|
|
|
-- name: InsertWorkspaceAgent :one
|
|
INSERT INTO
|
|
workspace_agents (
|
|
id,
|
|
parent_id,
|
|
created_at,
|
|
updated_at,
|
|
name,
|
|
resource_id,
|
|
auth_token,
|
|
auth_instance_id,
|
|
architecture,
|
|
environment_variables,
|
|
operating_system,
|
|
directory,
|
|
instance_metadata,
|
|
resource_metadata,
|
|
connection_timeout_seconds,
|
|
troubleshooting_url,
|
|
motd_file,
|
|
display_apps,
|
|
display_order,
|
|
api_key_scope
|
|
)
|
|
VALUES
|
|
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20) RETURNING *;
|
|
|
|
-- name: UpdateWorkspaceAgentConnectionByID :exec
|
|
UPDATE
|
|
workspace_agents
|
|
SET
|
|
first_connected_at = $2,
|
|
last_connected_at = $3,
|
|
last_connected_replica_id = $4,
|
|
disconnected_at = $5,
|
|
updated_at = $6
|
|
WHERE
|
|
id = $1;
|
|
|
|
-- name: UpdateWorkspaceAgentStartupByID :exec
|
|
UPDATE
|
|
workspace_agents
|
|
SET
|
|
version = $2,
|
|
expanded_directory = $3,
|
|
subsystems = $4,
|
|
api_version = $5
|
|
WHERE
|
|
id = $1;
|
|
|
|
-- name: GetWorkspaceAgentLifecycleStateByID :one
|
|
SELECT
|
|
lifecycle_state,
|
|
started_at,
|
|
ready_at
|
|
FROM
|
|
workspace_agents
|
|
WHERE
|
|
id = $1;
|
|
|
|
|
|
-- name: UpdateWorkspaceAgentLifecycleStateByID :exec
|
|
UPDATE
|
|
workspace_agents
|
|
SET
|
|
lifecycle_state = $2,
|
|
started_at = $3,
|
|
ready_at = $4
|
|
WHERE
|
|
id = $1;
|
|
|
|
-- name: InsertWorkspaceAgentMetadata :exec
|
|
INSERT INTO
|
|
workspace_agent_metadata (
|
|
workspace_agent_id,
|
|
display_name,
|
|
key,
|
|
script,
|
|
timeout,
|
|
interval,
|
|
display_order
|
|
)
|
|
VALUES
|
|
($1, $2, $3, $4, $5, $6, $7);
|
|
|
|
-- name: UpdateWorkspaceAgentMetadata :exec
|
|
WITH metadata AS (
|
|
SELECT
|
|
unnest(sqlc.arg('key')::text[]) AS key,
|
|
unnest(sqlc.arg('value')::text[]) AS value,
|
|
unnest(sqlc.arg('error')::text[]) AS error,
|
|
unnest(sqlc.arg('collected_at')::timestamptz[]) AS collected_at
|
|
)
|
|
UPDATE
|
|
workspace_agent_metadata wam
|
|
SET
|
|
value = m.value,
|
|
error = m.error,
|
|
collected_at = m.collected_at
|
|
FROM
|
|
metadata m
|
|
WHERE
|
|
wam.workspace_agent_id = $1
|
|
AND wam.key = m.key;
|
|
|
|
-- name: BatchUpdateWorkspaceAgentMetadata :exec
|
|
WITH metadata AS (
|
|
SELECT
|
|
unnest(sqlc.arg('workspace_agent_id')::uuid[]) AS workspace_agent_id,
|
|
unnest(sqlc.arg('key')::text[]) AS key,
|
|
unnest(sqlc.arg('value')::text[]) AS value,
|
|
unnest(sqlc.arg('error')::text[]) AS error,
|
|
unnest(sqlc.arg('collected_at')::timestamptz[]) AS collected_at
|
|
)
|
|
UPDATE
|
|
workspace_agent_metadata wam
|
|
SET
|
|
value = m.value,
|
|
error = m.error,
|
|
collected_at = m.collected_at
|
|
FROM
|
|
metadata m
|
|
WHERE
|
|
wam.workspace_agent_id = m.workspace_agent_id
|
|
AND wam.key = m.key;
|
|
|
|
-- name: GetWorkspaceAgentMetadata :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
workspace_agent_metadata
|
|
WHERE
|
|
workspace_agent_id = $1
|
|
AND CASE WHEN COALESCE(array_length(sqlc.arg('keys')::text[], 1), 0) > 0 THEN key = ANY(sqlc.arg('keys')::text[]) ELSE TRUE END;
|
|
|
|
-- name: UpdateWorkspaceAgentLogOverflowByID :exec
|
|
UPDATE
|
|
workspace_agents
|
|
SET
|
|
logs_overflowed = $2
|
|
WHERE
|
|
id = $1;
|
|
|
|
-- name: UpdateWorkspaceAgentDisplayAppsByID :exec
|
|
UPDATE
|
|
workspace_agents
|
|
SET
|
|
display_apps = $2, updated_at = $3
|
|
WHERE
|
|
id = $1;
|
|
|
|
-- name: GetWorkspaceAgentLogsAfter :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
workspace_agent_logs
|
|
WHERE
|
|
agent_id = $1
|
|
AND (
|
|
id > @created_after
|
|
) ORDER BY id ASC;
|
|
|
|
-- name: InsertWorkspaceAgentLogs :many
|
|
WITH new_length AS (
|
|
UPDATE workspace_agents SET
|
|
logs_length = logs_length + @output_length WHERE workspace_agents.id = @agent_id
|
|
)
|
|
INSERT INTO
|
|
workspace_agent_logs (agent_id, created_at, output, level, log_source_id)
|
|
SELECT
|
|
@agent_id :: uuid AS agent_id,
|
|
@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
|
|
RETURNING workspace_agent_logs.*;
|
|
|
|
-- name: InsertWorkspaceAgentLogSources :many
|
|
INSERT INTO
|
|
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,
|
|
unnest(@id :: uuid [ ]) AS id,
|
|
unnest(@display_name :: VARCHAR(127) [ ]) AS display_name,
|
|
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 within the retention period, we purge its logs.
|
|
-- Exception: if the logs are related to the latest build, we keep those around.
|
|
-- Logs can take up a lot of space, so it's important we clean up frequently.
|
|
-- name: DeleteOldWorkspaceAgentLogs :execrows
|
|
WITH
|
|
latest_builds AS (
|
|
SELECT
|
|
workspace_id, max(build_number) AS max_build_number
|
|
FROM
|
|
workspace_builds
|
|
GROUP BY
|
|
workspace_id
|
|
),
|
|
old_agents AS (
|
|
SELECT
|
|
wa.id
|
|
FROM
|
|
workspace_agents AS wa
|
|
JOIN
|
|
workspace_resources AS wr
|
|
ON
|
|
wa.resource_id = wr.id
|
|
JOIN
|
|
workspace_builds AS wb
|
|
ON
|
|
wb.job_id = wr.job_id
|
|
LEFT JOIN
|
|
latest_builds
|
|
ON
|
|
latest_builds.workspace_id = wb.workspace_id
|
|
AND
|
|
latest_builds.max_build_number = wb.build_number
|
|
WHERE
|
|
-- Filter out the latest builds for each workspace.
|
|
latest_builds.workspace_id IS NULL
|
|
AND CASE
|
|
-- If the last time the agent connected was before @threshold
|
|
WHEN wa.last_connected_at IS NOT NULL THEN
|
|
wa.last_connected_at < @threshold :: timestamptz
|
|
-- The agent never connected, and was created before @threshold
|
|
ELSE wa.created_at < @threshold :: timestamptz
|
|
END
|
|
)
|
|
DELETE FROM workspace_agent_logs WHERE agent_id IN (SELECT id FROM old_agents);
|
|
|
|
-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many
|
|
SELECT
|
|
workspace_agents.*
|
|
FROM
|
|
workspace_agents
|
|
JOIN
|
|
workspace_resources ON workspace_agents.resource_id = workspace_resources.id
|
|
JOIN
|
|
workspace_builds ON workspace_resources.job_id = workspace_builds.job_id
|
|
WHERE
|
|
workspace_builds.workspace_id = @workspace_id :: uuid AND
|
|
workspace_builds.build_number = (
|
|
SELECT
|
|
MAX(build_number)
|
|
FROM
|
|
workspace_builds AS wb
|
|
WHERE
|
|
wb.workspace_id = @workspace_id :: uuid
|
|
)
|
|
-- Filter out deleted sub agents.
|
|
AND workspace_agents.deleted = FALSE;
|
|
|
|
-- name: GetWorkspaceAgentsByWorkspaceAndBuildNumber :many
|
|
SELECT
|
|
workspace_agents.*
|
|
FROM
|
|
workspace_agents
|
|
JOIN
|
|
workspace_resources ON workspace_agents.resource_id = workspace_resources.id
|
|
JOIN
|
|
workspace_builds ON workspace_resources.job_id = workspace_builds.job_id
|
|
WHERE
|
|
workspace_builds.workspace_id = @workspace_id :: uuid AND
|
|
workspace_builds.build_number = @build_number :: int
|
|
-- Filter out deleted sub agents.
|
|
AND workspace_agents.deleted = FALSE;
|
|
|
|
-- GetAuthenticatedWorkspaceAgentAndBuildByAuthToken returns an authenticated
|
|
-- workspace agent and its associated build. During normal operation, this is
|
|
-- the latest build. During shutdown, this may be the previous START build while
|
|
-- the STOP build is executing, allowing shutdown scripts to authenticate (see
|
|
-- issue #19467).
|
|
-- name: GetAuthenticatedWorkspaceAgentAndBuildByAuthToken :one
|
|
SELECT
|
|
sqlc.embed(workspaces),
|
|
sqlc.embed(workspace_agents),
|
|
sqlc.embed(workspace_build_with_user),
|
|
tasks.id AS task_id
|
|
FROM
|
|
workspace_agents
|
|
JOIN
|
|
workspace_resources
|
|
ON
|
|
workspace_agents.resource_id = workspace_resources.id
|
|
JOIN
|
|
workspace_build_with_user
|
|
ON
|
|
workspace_resources.job_id = workspace_build_with_user.job_id
|
|
JOIN
|
|
workspaces
|
|
ON
|
|
workspace_build_with_user.workspace_id = workspaces.id
|
|
LEFT JOIN
|
|
tasks
|
|
ON
|
|
tasks.workspace_id = workspaces.id
|
|
WHERE
|
|
-- This should only match 1 agent, so 1 returned row or 0.
|
|
workspace_agents.auth_token = @auth_token::uuid
|
|
AND workspaces.deleted = FALSE
|
|
-- Filter out deleted sub agents.
|
|
AND workspace_agents.deleted = FALSE
|
|
-- Filter out builds that are not the latest, with exception for shutdown case.
|
|
-- Use CASE for short-circuiting: check normal case first (most common), then shutdown case.
|
|
AND CASE
|
|
-- Normal case: Agent's build is the latest build.
|
|
WHEN workspace_build_with_user.build_number = (
|
|
SELECT
|
|
MAX(build_number)
|
|
FROM
|
|
workspace_builds
|
|
WHERE
|
|
workspace_id = workspace_build_with_user.workspace_id
|
|
) THEN TRUE
|
|
-- Shutdown case: Agent from previous START build during STOP build execution.
|
|
WHEN workspace_build_with_user.transition = 'start'
|
|
-- Agent's START build job succeeded.
|
|
AND (SELECT job_status FROM provisioner_jobs WHERE id = workspace_build_with_user.job_id) = 'succeeded'
|
|
-- Latest build is a STOP build whose job is still active,
|
|
-- and agent's build is immediately previous.
|
|
AND EXISTS (
|
|
SELECT 1
|
|
FROM workspace_builds latest
|
|
JOIN provisioner_jobs pj ON pj.id = latest.job_id
|
|
WHERE latest.workspace_id = workspace_build_with_user.workspace_id
|
|
AND latest.build_number = workspace_build_with_user.build_number + 1
|
|
AND latest.build_number = (
|
|
SELECT MAX(build_number)
|
|
FROM workspace_builds l2
|
|
WHERE l2.workspace_id = latest.workspace_id
|
|
)
|
|
AND latest.transition = 'stop'
|
|
AND pj.job_status IN ('pending', 'running')
|
|
) THEN TRUE
|
|
ELSE FALSE
|
|
END
|
|
;
|
|
|
|
-- name: InsertWorkspaceAgentScriptTimings :one
|
|
INSERT INTO
|
|
workspace_agent_script_timings (
|
|
script_id,
|
|
started_at,
|
|
ended_at,
|
|
exit_code,
|
|
stage,
|
|
status
|
|
)
|
|
VALUES
|
|
($1, $2, $3, $4, $5, $6)
|
|
RETURNING workspace_agent_script_timings.*;
|
|
|
|
-- name: GetWorkspaceAgentScriptTimingsByBuildID :many
|
|
SELECT
|
|
DISTINCT ON (workspace_agent_script_timings.script_id) workspace_agent_script_timings.*,
|
|
workspace_agent_scripts.display_name,
|
|
workspace_agents.id as workspace_agent_id,
|
|
workspace_agents.name as workspace_agent_name
|
|
FROM workspace_agent_script_timings
|
|
INNER JOIN workspace_agent_scripts ON workspace_agent_scripts.id = workspace_agent_script_timings.script_id
|
|
INNER JOIN workspace_agents ON workspace_agents.id = workspace_agent_scripts.workspace_agent_id
|
|
INNER JOIN workspace_resources ON workspace_resources.id = workspace_agents.resource_id
|
|
INNER JOIN workspace_builds ON workspace_builds.job_id = workspace_resources.job_id
|
|
WHERE workspace_builds.id = $1
|
|
ORDER BY workspace_agent_script_timings.script_id, workspace_agent_script_timings.started_at;
|
|
|
|
-- name: GetWorkspaceAgentsByParentID :many
|
|
SELECT
|
|
*
|
|
FROM
|
|
workspace_agents
|
|
WHERE
|
|
parent_id = @parent_id::uuid
|
|
AND deleted = FALSE;
|
|
|
|
-- name: DeleteWorkspaceSubAgentByID :exec
|
|
UPDATE
|
|
workspace_agents
|
|
SET
|
|
deleted = TRUE
|
|
WHERE
|
|
id = $1
|
|
AND parent_id IS NOT NULL
|
|
AND deleted = FALSE;
|
|
|
|
-- name: GetWorkspaceAgentsForMetrics :many
|
|
SELECT
|
|
w.id as workspace_id,
|
|
w.name as workspace_name,
|
|
u.username as owner_username,
|
|
t.name as template_name,
|
|
tv.name as template_version_name,
|
|
sqlc.embed(workspace_agents)
|
|
FROM workspaces w
|
|
JOIN users u ON w.owner_id = u.id
|
|
JOIN templates t ON w.template_id = t.id
|
|
JOIN workspace_builds wb ON w.id = wb.workspace_id
|
|
LEFT JOIN template_versions tv ON wb.template_version_id = tv.id
|
|
JOIN workspace_resources wr ON wb.job_id = wr.job_id
|
|
JOIN workspace_agents ON wr.id = workspace_agents.resource_id
|
|
WHERE w.deleted = false
|
|
AND wb.build_number = (
|
|
SELECT MAX(wb2.build_number)
|
|
FROM workspace_builds wb2
|
|
WHERE wb2.workspace_id = w.id
|
|
)
|
|
AND workspace_agents.deleted = FALSE;
|
|
|
|
-- name: GetWorkspaceAgentAndWorkspaceByID :one
|
|
SELECT
|
|
sqlc.embed(workspace_agents),
|
|
sqlc.embed(workspaces),
|
|
users.username as owner_username
|
|
FROM
|
|
workspace_agents
|
|
JOIN
|
|
workspace_resources ON workspace_agents.resource_id = workspace_resources.id
|
|
JOIN
|
|
provisioner_jobs ON workspace_resources.job_id = provisioner_jobs.id
|
|
JOIN
|
|
workspace_builds ON provisioner_jobs.id = workspace_builds.job_id
|
|
JOIN
|
|
workspaces ON workspace_builds.workspace_id = workspaces.id
|
|
JOIN
|
|
users ON workspaces.owner_id = users.id
|
|
WHERE
|
|
workspace_agents.id = @id
|
|
AND workspace_agents.deleted = FALSE
|
|
AND provisioner_jobs.type = 'workspace_build'::provisioner_job_type
|
|
AND workspaces.deleted = FALSE
|
|
AND users.deleted = FALSE
|
|
LIMIT 1;
|