mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: include agent metadata in workspace list responses (#27934)
Closes #27933. Related: #27897 (single-agent GET). Agent metadata is only readable via a per-agent watch stream, so reading it across N workspaces costs N+1 requests. This adds a batch read to the list endpoint: ```text GET /api/v2/workspaces?q=param:"pool=demo" include_agent_metadata:task_status ``` - New `include_agent_metadata` search key, repeatable and key-scoped. It expands the response, it does not filter workspaces. - `GetWorkspaces` aggregates the requested keys as JSON behind a `CASE`: without opt-in the response is unchanged and the subquery never runs. Runs only for the returned page, inside the same authorized query. - Agents in the response gain `metadata` (`[]codersdk.WorkspaceAgentMetadata`, `omitempty`), mapped by the `workspace_agent_id` each element carries. The collection script is omitted; it can be long. - `codersdk.WorkspaceFilter` gains `IncludeAgentMetadata []string`. - No wildcard, no schema change, no migration. --- Authored by Coder Agents on behalf of @Emyrk.
This commit is contained in:
@@ -117,7 +117,8 @@ SELECT
|
||||
latest_build.error as latest_build_error,
|
||||
latest_build.transition as latest_build_transition,
|
||||
latest_build.job_status as latest_build_status,
|
||||
latest_build.has_external_agent as latest_build_has_external_agent
|
||||
latest_build.has_external_agent as latest_build_has_external_agent,
|
||||
latest_build.provisioner_job_id as latest_build_provisioner_job_id
|
||||
FROM
|
||||
workspaces_expanded as workspaces
|
||||
JOIN
|
||||
@@ -462,7 +463,8 @@ WHERE
|
||||
'', -- latest_build_error
|
||||
'start'::workspace_transition, -- latest_build_transition
|
||||
'unknown'::provisioner_job_status, -- latest_build_status
|
||||
false -- latest_build_has_external_agent
|
||||
false, -- latest_build_has_external_agent
|
||||
'00000000-0000-0000-0000-000000000000'::uuid -- latest_build_provisioner_job_id
|
||||
WHERE
|
||||
@with_summary :: boolean = true
|
||||
), total_count AS (
|
||||
@@ -473,6 +475,55 @@ WHERE
|
||||
)
|
||||
SELECT
|
||||
fwos.*,
|
||||
-- agent_metadata expands the response with the requested agent
|
||||
-- metadata keys for the latest build's agents. The CASE keeps the
|
||||
-- subquery unevaluated for every caller that does not opt in, and
|
||||
-- it only runs for the returned page. Each element carries the
|
||||
-- workspace_agent_id so multi-agent workspaces can map values onto
|
||||
-- the right agent.
|
||||
CASE WHEN cardinality(@include_agent_metadata :: text[]) > 0 THEN
|
||||
COALESCE((
|
||||
SELECT
|
||||
jsonb_agg(jsonb_build_object(
|
||||
'workspace_agent_id', workspace_agents.id,
|
||||
'display_name', workspace_agent_metadata.display_name,
|
||||
'key', workspace_agent_metadata.key,
|
||||
-- script is deliberately omitted: it can be long and
|
||||
-- list consumers want values, not collection commands.
|
||||
'value', workspace_agent_metadata.value,
|
||||
'error', workspace_agent_metadata.error,
|
||||
'timeout', workspace_agent_metadata.timeout,
|
||||
'interval', workspace_agent_metadata.interval,
|
||||
'collected_at', workspace_agent_metadata.collected_at,
|
||||
'display_order', workspace_agent_metadata.display_order
|
||||
))
|
||||
FROM
|
||||
workspace_agents
|
||||
JOIN
|
||||
workspace_resources
|
||||
ON
|
||||
workspace_resources.id = workspace_agents.resource_id
|
||||
JOIN
|
||||
workspace_agent_metadata
|
||||
ON
|
||||
workspace_agent_metadata.workspace_agent_id = workspace_agents.id
|
||||
WHERE
|
||||
-- The latest build's job was already resolved by the
|
||||
-- latest_build lateral; resources hang off its job.
|
||||
workspace_resources.job_id = fwos.latest_build_provisioner_job_id
|
||||
-- Filter out deleted sub agents.
|
||||
AND workspace_agents.deleted = FALSE
|
||||
-- Both sides are lowercased so matching is
|
||||
-- case-insensitive regardless of how the caller cased
|
||||
-- the requested keys.
|
||||
AND LOWER(workspace_agent_metadata.key) = ANY(ARRAY(
|
||||
SELECT LOWER(key) FROM unnest(@include_agent_metadata :: text[]) AS k(key)
|
||||
))
|
||||
), '[]'::jsonb)
|
||||
ELSE
|
||||
-- Never NULL: lib/pq cannot scan NULL into json.RawMessage.
|
||||
'[]'::jsonb
|
||||
END :: jsonb AS agent_metadata,
|
||||
tc.count
|
||||
FROM
|
||||
filtered_workspaces_order_with_summary fwos
|
||||
|
||||
Reference in New Issue
Block a user