From 92b35ffe0c25288b1f373d6df847356c91535dcf Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 10:08:23 +0000 Subject: [PATCH 01/11] feat: add `parent_id` column to `workspace_agents` table Adds a new nullable column `parent_id` to `workspace_agents` table. This lays the groundwork for having child agents. --- coderd/apidoc/docs.go | 20 +++++++ coderd/apidoc/swagger.json | 20 +++++++ coderd/database/dump.sql | 4 ++ coderd/database/foreign_key_constraint.go | 1 + ...add_parent_id_to_workspace_agents.down.sql | 2 + ...0_add_parent_id_to_workspace_agents.up.sql | 2 + coderd/database/models.go | 3 +- coderd/database/queries.sql.go | 21 ++++--- codersdk/workspaceagents.go | 1 + docs/admin/security/audit-logs.md | 2 +- docs/reference/api/agents.md | 4 ++ docs/reference/api/builds.md | 30 ++++++++++ docs/reference/api/schemas.md | 37 +++++++++++++ docs/reference/api/templates.md | 14 +++++ docs/reference/api/workspaces.md | 24 ++++++++ enterprise/audit/table.go | 1 + site/src/api/typesGenerated.ts | 1 + .../pages/WorkspacePage/Workspace.stories.tsx | 27 +++++++++ site/src/pages/WorkspacePage/Workspace.tsx | 55 ++++++++++--------- site/src/testHelpers/entities.ts | 41 ++++++++++++++ 20 files changed, 276 insertions(+), 34 deletions(-) create mode 100644 coderd/database/migrations/000320_add_parent_id_to_workspace_agents.down.sql create mode 100644 coderd/database/migrations/000320_add_parent_id_to_workspace_agents.up.sql diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index fb5ae20e448c8..dc5724f77b020 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -17021,6 +17021,14 @@ const docTemplate = `{ "operating_system": { "type": "string" }, + "parent_id": { + "format": "uuid", + "allOf": [ + { + "$ref": "#/definitions/uuid.NullUUID" + } + ] + }, "ready_at": { "type": "string", "format": "date-time" @@ -19033,6 +19041,18 @@ const docTemplate = `{ "url.Userinfo": { "type": "object" }, + "uuid.NullUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string" + }, + "valid": { + "description": "Valid is true if UUID is not NULL", + "type": "boolean" + } + } + }, "workspaceapps.AccessMethod": { "type": "string", "enum": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index 8420c9ea0f812..1628797d85ffc 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -15538,6 +15538,14 @@ "operating_system": { "type": "string" }, + "parent_id": { + "format": "uuid", + "allOf": [ + { + "$ref": "#/definitions/uuid.NullUUID" + } + ] + }, "ready_at": { "type": "string", "format": "date-time" @@ -17442,6 +17450,18 @@ "url.Userinfo": { "type": "object" }, + "uuid.NullUUID": { + "type": "object", + "properties": { + "uuid": { + "type": "string" + }, + "valid": { + "description": "Valid is true if UUID is not NULL", + "type": "boolean" + } + } + }, "workspaceapps.AccessMethod": { "type": "string", "enum": ["path", "subdomain", "terminal"], diff --git a/coderd/database/dump.sql b/coderd/database/dump.sql index 9ce3b0171d2d4..b8cbc6ee18456 100644 --- a/coderd/database/dump.sql +++ b/coderd/database/dump.sql @@ -1832,6 +1832,7 @@ CREATE TABLE workspace_agents ( display_apps display_app[] DEFAULT '{vscode,vscode_insiders,web_terminal,ssh_helper,port_forwarding_helper}'::display_app[], api_version text DEFAULT ''::text NOT NULL, display_order integer DEFAULT 0 NOT NULL, + parent_id uuid, CONSTRAINT max_logs_length CHECK ((logs_length <= 1048576)), CONSTRAINT subsystems_not_none CHECK ((NOT ('none'::workspace_agent_subsystem = ANY (subsystems)))) ); @@ -2922,6 +2923,9 @@ ALTER TABLE ONLY workspace_agent_logs ALTER TABLE ONLY workspace_agent_volume_resource_monitors ADD CONSTRAINT workspace_agent_volume_resource_monitors_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; +ALTER TABLE ONLY workspace_agents + ADD CONSTRAINT workspace_agents_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; + ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE; diff --git a/coderd/database/foreign_key_constraint.go b/coderd/database/foreign_key_constraint.go index 0db3e9522547e..3ccf093d85ce6 100644 --- a/coderd/database/foreign_key_constraint.go +++ b/coderd/database/foreign_key_constraint.go @@ -70,6 +70,7 @@ const ( ForeignKeyWorkspaceAgentScriptsWorkspaceAgentID ForeignKeyConstraint = "workspace_agent_scripts_workspace_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_scripts ADD CONSTRAINT workspace_agent_scripts_workspace_agent_id_fkey FOREIGN KEY (workspace_agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentStartupLogsAgentID ForeignKeyConstraint = "workspace_agent_startup_logs_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_logs ADD CONSTRAINT workspace_agent_startup_logs_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentVolumeResourceMonitorsAgentID ForeignKeyConstraint = "workspace_agent_volume_resource_monitors_agent_id_fkey" // ALTER TABLE ONLY workspace_agent_volume_resource_monitors ADD CONSTRAINT workspace_agent_volume_resource_monitors_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; + ForeignKeyWorkspaceAgentsParentID ForeignKeyConstraint = "workspace_agents_parent_id_fkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAgentsResourceID ForeignKeyConstraint = "workspace_agents_resource_id_fkey" // ALTER TABLE ONLY workspace_agents ADD CONSTRAINT workspace_agents_resource_id_fkey FOREIGN KEY (resource_id) REFERENCES workspace_resources(id) ON DELETE CASCADE; ForeignKeyWorkspaceAppAuditSessionsAgentID ForeignKeyConstraint = "workspace_app_audit_sessions_agent_id_fkey" // ALTER TABLE ONLY workspace_app_audit_sessions ADD CONSTRAINT workspace_app_audit_sessions_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE; ForeignKeyWorkspaceAppStatsAgentID ForeignKeyConstraint = "workspace_app_stats_agent_id_fkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id); diff --git a/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.down.sql b/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.down.sql new file mode 100644 index 0000000000000..ab810126ad60e --- /dev/null +++ b/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.down.sql @@ -0,0 +1,2 @@ +ALTER TABLE workspace_agents +DROP COLUMN IF EXISTS parent_id; diff --git a/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.up.sql b/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.up.sql new file mode 100644 index 0000000000000..f2fd7a8c1cd10 --- /dev/null +++ b/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE workspace_agents +ADD COLUMN parent_id UUID REFERENCES workspace_agents (id) ON DELETE CASCADE; diff --git a/coderd/database/models.go b/coderd/database/models.go index c8ac71e8b9398..b3c3e2c7e6eaa 100644 --- a/coderd/database/models.go +++ b/coderd/database/models.go @@ -3401,7 +3401,8 @@ type WorkspaceAgent struct { DisplayApps []DisplayApp `db:"display_apps" json:"display_apps"` APIVersion string `db:"api_version" json:"api_version"` // Specifies the order in which to display agents in user interfaces. - DisplayOrder int32 `db:"display_order" json:"display_order"` + DisplayOrder int32 `db:"display_order" json:"display_order"` + ParentID uuid.NullUUID `db:"parent_id" json:"parent_id"` } // Workspace agent devcontainer configuration diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index cd5b297c85e07..9d46821cfe7d8 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -13913,7 +13913,7 @@ func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context, threshold const getWorkspaceAgentAndLatestBuildByAuthToken = `-- name: GetWorkspaceAgentAndLatestBuildByAuthToken :one SELECT workspaces.id, workspaces.created_at, workspaces.updated_at, workspaces.owner_id, workspaces.organization_id, workspaces.template_id, workspaces.deleted, workspaces.name, workspaces.autostart_schedule, workspaces.ttl, workspaces.last_used_at, workspaces.dormant_at, workspaces.deleting_at, workspaces.automatic_updates, workspaces.favorite, workspaces.next_start_at, - workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, + workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id, workspace_build_with_user.id, workspace_build_with_user.created_at, workspace_build_with_user.updated_at, workspace_build_with_user.workspace_id, workspace_build_with_user.template_version_id, workspace_build_with_user.build_number, workspace_build_with_user.transition, workspace_build_with_user.initiator_id, workspace_build_with_user.provisioner_state, workspace_build_with_user.job_id, workspace_build_with_user.deadline, workspace_build_with_user.reason, workspace_build_with_user.daily_cost, workspace_build_with_user.max_deadline, workspace_build_with_user.template_version_preset_id, workspace_build_with_user.initiator_by_avatar_url, workspace_build_with_user.initiator_by_username FROM workspace_agents @@ -14003,6 +14003,7 @@ func (q *sqlQuerier) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Cont pq.Array(&i.WorkspaceAgent.DisplayApps), &i.WorkspaceAgent.APIVersion, &i.WorkspaceAgent.DisplayOrder, + &i.WorkspaceAgent.ParentID, &i.WorkspaceBuild.ID, &i.WorkspaceBuild.CreatedAt, &i.WorkspaceBuild.UpdatedAt, @@ -14026,7 +14027,7 @@ func (q *sqlQuerier) GetWorkspaceAgentAndLatestBuildByAuthToken(ctx context.Cont const getWorkspaceAgentByID = `-- name: GetWorkspaceAgentByID :one SELECT - id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order + id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id FROM workspace_agents WHERE @@ -14068,13 +14069,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W pq.Array(&i.DisplayApps), &i.APIVersion, &i.DisplayOrder, + &i.ParentID, ) return i, err } const getWorkspaceAgentByInstanceID = `-- name: GetWorkspaceAgentByInstanceID :one SELECT - id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order + id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id FROM workspace_agents WHERE @@ -14118,6 +14120,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst pq.Array(&i.DisplayApps), &i.APIVersion, &i.DisplayOrder, + &i.ParentID, ) return i, err } @@ -14337,7 +14340,7 @@ func (q *sqlQuerier) GetWorkspaceAgentScriptTimingsByBuildID(ctx context.Context const getWorkspaceAgentsByResourceIDs = `-- name: GetWorkspaceAgentsByResourceIDs :many SELECT - id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order + id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id FROM workspace_agents WHERE @@ -14385,6 +14388,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids [] pq.Array(&i.DisplayApps), &i.APIVersion, &i.DisplayOrder, + &i.ParentID, ); err != nil { return nil, err } @@ -14400,7 +14404,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids [] } const getWorkspaceAgentsCreatedAfter = `-- name: GetWorkspaceAgentsCreatedAfter :many -SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order FROM workspace_agents WHERE created_at > $1 +SELECT id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id FROM workspace_agents WHERE created_at > $1 ` func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) { @@ -14444,6 +14448,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created pq.Array(&i.DisplayApps), &i.APIVersion, &i.DisplayOrder, + &i.ParentID, ); err != nil { return nil, err } @@ -14460,7 +14465,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created const getWorkspaceAgentsInLatestBuildByWorkspaceID = `-- name: GetWorkspaceAgentsInLatestBuildByWorkspaceID :many SELECT - workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order + workspace_agents.id, workspace_agents.created_at, workspace_agents.updated_at, workspace_agents.name, workspace_agents.first_connected_at, workspace_agents.last_connected_at, workspace_agents.disconnected_at, workspace_agents.resource_id, workspace_agents.auth_token, workspace_agents.auth_instance_id, workspace_agents.architecture, workspace_agents.environment_variables, workspace_agents.operating_system, workspace_agents.instance_metadata, workspace_agents.resource_metadata, workspace_agents.directory, workspace_agents.version, workspace_agents.last_connected_replica_id, workspace_agents.connection_timeout_seconds, workspace_agents.troubleshooting_url, workspace_agents.motd_file, workspace_agents.lifecycle_state, workspace_agents.expanded_directory, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems, workspace_agents.display_apps, workspace_agents.api_version, workspace_agents.display_order, workspace_agents.parent_id FROM workspace_agents JOIN @@ -14520,6 +14525,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co pq.Array(&i.DisplayApps), &i.APIVersion, &i.DisplayOrder, + &i.ParentID, ); err != nil { return nil, err } @@ -14557,7 +14563,7 @@ INSERT INTO display_order ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id ` type InsertWorkspaceAgentParams struct { @@ -14635,6 +14641,7 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa pq.Array(&i.DisplayApps), &i.APIVersion, &i.DisplayOrder, + &i.ParentID, ) return i, err } diff --git a/codersdk/workspaceagents.go b/codersdk/workspaceagents.go index 5c7171f70a627..f58338a209901 100644 --- a/codersdk/workspaceagents.go +++ b/codersdk/workspaceagents.go @@ -139,6 +139,7 @@ const ( type WorkspaceAgent struct { ID uuid.UUID `json:"id" format:"uuid"` + ParentID uuid.NullUUID `json:"parent_id" format:"uuid"` CreatedAt time.Time `json:"created_at" format:"date-time"` UpdatedAt time.Time `json:"updated_at" format:"date-time"` FirstConnectedAt *time.Time `json:"first_connected_at,omitempty" format:"date-time"` diff --git a/docs/admin/security/audit-logs.md b/docs/admin/security/audit-logs.md index c9124efa14bf0..2ab7d454ca71b 100644 --- a/docs/admin/security/audit-logs.md +++ b/docs/admin/security/audit-logs.md @@ -29,7 +29,7 @@ We track the following resources: | Template
write, delete | |
FieldTracked
active_version_idtrue
activity_bumptrue
allow_user_autostarttrue
allow_user_autostoptrue
allow_user_cancel_workspace_jobstrue
autostart_block_days_of_weektrue
autostop_requirement_days_of_weektrue
autostop_requirement_weekstrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
default_ttltrue
deletedfalse
deprecatedtrue
descriptiontrue
display_nametrue
failure_ttltrue
group_acltrue
icontrue
idtrue
max_port_sharing_leveltrue
nametrue
organization_display_namefalse
organization_iconfalse
organization_idfalse
organization_namefalse
provisionertrue
require_active_versiontrue
time_til_dormanttrue
time_til_dormant_autodeletetrue
updated_atfalse
user_acltrue
| | TemplateVersion
create, write | |
FieldTracked
archivedtrue
created_atfalse
created_bytrue
created_by_avatar_urlfalse
created_by_usernamefalse
external_auth_providersfalse
idtrue
job_idfalse
messagefalse
nametrue
organization_idfalse
readmetrue
source_example_idfalse
template_idtrue
updated_atfalse
| | User
create, write, delete | |
FieldTracked
avatar_urlfalse
created_atfalse
deletedtrue
emailtrue
github_com_user_idfalse
hashed_one_time_passcodefalse
hashed_passwordtrue
idtrue
is_systemtrue
last_seen_atfalse
login_typetrue
nametrue
one_time_passcode_expires_attrue
quiet_hours_scheduletrue
rbac_rolestrue
statustrue
updated_atfalse
usernametrue
| -| WorkspaceAgent
connect, disconnect | |
FieldTracked
api_versionfalse
architecturefalse
auth_instance_idfalse
auth_tokenfalse
connection_timeout_secondsfalse
created_atfalse
directoryfalse
disconnected_atfalse
display_appsfalse
display_orderfalse
environment_variablesfalse
expanded_directoryfalse
first_connected_atfalse
idfalse
instance_metadatafalse
last_connected_atfalse
last_connected_replica_idfalse
lifecycle_statefalse
logs_lengthfalse
logs_overflowedfalse
motd_filefalse
namefalse
operating_systemfalse
ready_atfalse
resource_idfalse
resource_metadatafalse
started_atfalse
subsystemsfalse
troubleshooting_urlfalse
updated_atfalse
versionfalse
| +| WorkspaceAgent
connect, disconnect | |
FieldTracked
api_versionfalse
architecturefalse
auth_instance_idfalse
auth_tokenfalse
connection_timeout_secondsfalse
created_atfalse
directoryfalse
disconnected_atfalse
display_appsfalse
display_orderfalse
environment_variablesfalse
expanded_directoryfalse
first_connected_atfalse
idfalse
instance_metadatafalse
last_connected_atfalse
last_connected_replica_idfalse
lifecycle_statefalse
logs_lengthfalse
logs_overflowedfalse
motd_filefalse
namefalse
operating_systemfalse
parent_idfalse
ready_atfalse
resource_idfalse
resource_metadatafalse
started_atfalse
subsystemsfalse
troubleshooting_urlfalse
updated_atfalse
versionfalse
| | WorkspaceApp
open, close | |
FieldTracked
agent_idfalse
commandfalse
created_atfalse
display_namefalse
display_orderfalse
externalfalse
healthfalse
healthcheck_intervalfalse
healthcheck_thresholdfalse
healthcheck_urlfalse
hiddenfalse
iconfalse
idfalse
open_infalse
sharing_levelfalse
slugfalse
subdomainfalse
urlfalse
| | WorkspaceBuild
start, stop | |
FieldTracked
build_numberfalse
created_atfalse
daily_costfalse
deadlinefalse
idfalse
initiator_by_avatar_urlfalse
initiator_by_usernamefalse
initiator_idfalse
job_idfalse
max_deadlinefalse
provisioner_statefalse
reasonfalse
template_version_idtrue
template_version_preset_idfalse
transitionfalse
updated_atfalse
workspace_idfalse
| | WorkspaceProxy
| |
FieldTracked
created_attrue
deletedfalse
derp_enabledtrue
derp_onlytrue
display_nametrue
icontrue
idtrue
nametrue
region_idtrue
token_hashed_secrettrue
updated_atfalse
urltrue
versiontrue
wildcard_hostnametrue
| diff --git a/docs/reference/api/agents.md b/docs/reference/api/agents.md index 853cb67e38bfd..c0ddd79cd2052 100644 --- a/docs/reference/api/agents.md +++ b/docs/reference/api/agents.md @@ -577,6 +577,10 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ diff --git a/docs/reference/api/builds.md b/docs/reference/api/builds.md index 1f795c3d7d313..8e88df96c1d29 100644 --- a/docs/reference/api/builds.md +++ b/docs/reference/api/builds.md @@ -164,6 +164,10 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -393,6 +397,10 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -737,6 +745,10 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -859,6 +871,9 @@ Status Code **200** | `»» logs_overflowed` | boolean | false | | | | `»» name` | string | false | | | | `»» operating_system` | string | false | | | +| `»» parent_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | | +| `»»» uuid` | string | false | | | +| `»»» valid` | boolean | false | | Valid is true if UUID is not NULL | | `»» ready_at` | string(date-time) | false | | | | `»» resource_id` | string(uuid) | false | | | | `»» scripts` | array | false | | | @@ -1092,6 +1107,10 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -1394,6 +1413,10 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -1573,6 +1596,9 @@ Status Code **200** | `»»» logs_overflowed` | boolean | false | | | | `»»» name` | string | false | | | | `»»» operating_system` | string | false | | | +| `»»» parent_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | | +| `»»»» uuid` | string | false | | | +| `»»»» valid` | boolean | false | | Valid is true if UUID is not NULL | | `»»» ready_at` | string(date-time) | false | | | | `»»» resource_id` | string(uuid) | false | | | | `»»» scripts` | array | false | | | @@ -1867,6 +1893,10 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index 6ca005b4ec69c..8c1c86167b9d4 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -8304,6 +8304,10 @@ If the schedule is empty, the user will be updated to use the default schedule.| "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -8508,6 +8512,10 @@ If the schedule is empty, the user will be updated to use the default schedule.| "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -8564,6 +8572,7 @@ If the schedule is empty, the user will be updated to use the default schedule.| | `logs_overflowed` | boolean | false | | | | `name` | string | false | | | | `operating_system` | string | false | | | +| `parent_id` | [uuid.NullUUID](#uuidnulluuid) | false | | | | `ready_at` | string | false | | | | `resource_id` | string | false | | | | `scripts` | array of [codersdk.WorkspaceAgentScript](#codersdkworkspaceagentscript) | false | | | @@ -9256,6 +9265,10 @@ If the schedule is empty, the user will be updated to use the default schedule.| "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -9672,6 +9685,10 @@ If the schedule is empty, the user will be updated to use the default schedule.| "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -9954,6 +9971,10 @@ If the schedule is empty, the user will be updated to use the default schedule.| "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -11941,6 +11962,22 @@ RegionIDs in range 900-999 are reserved for end users to run their own DERP node None +## uuid.NullUUID + +```json +{ + "uuid": "string", + "valid": true +} +``` + +### Properties + +| Name | Type | Required | Restrictions | Description | +|---------|---------|----------|--------------|-----------------------------------| +| `uuid` | string | false | | | +| `valid` | boolean | false | | Valid is true if UUID is not NULL | + ## workspaceapps.AccessMethod ```json diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md index ef136764bf2c5..b1beeb64a7116 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -2348,6 +2348,10 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -2470,6 +2474,9 @@ Status Code **200** | `»» logs_overflowed` | boolean | false | | | | `»» name` | string | false | | | | `»» operating_system` | string | false | | | +| `»» parent_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | | +| `»»» uuid` | string | false | | | +| `»»» valid` | boolean | false | | Valid is true if UUID is not NULL | | `»» ready_at` | string(date-time) | false | | | | `»» resource_id` | string(uuid) | false | | | | `»» scripts` | array | false | | | @@ -2869,6 +2876,10 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -2991,6 +3002,9 @@ Status Code **200** | `»» logs_overflowed` | boolean | false | | | | `»» name` | string | false | | | | `»» operating_system` | string | false | | | +| `»» parent_id` | [uuid.NullUUID](schemas.md#uuidnulluuid) | false | | | +| `»»» uuid` | string | false | | | +| `»»» valid` | boolean | false | | Valid is true if UUID is not NULL | | `»» ready_at` | string(date-time) | false | | | | `»» resource_id` | string(uuid) | false | | | | `»» scripts` | array | false | | | diff --git a/docs/reference/api/workspaces.md b/docs/reference/api/workspaces.md index 5d09c46a01d30..8e25cd0bd58e6 100644 --- a/docs/reference/api/workspaces.md +++ b/docs/reference/api/workspaces.md @@ -219,6 +219,10 @@ of the template will be used. "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -496,6 +500,10 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -799,6 +807,10 @@ of the template will be used. "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -1062,6 +1074,10 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -1340,6 +1356,10 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ @@ -1733,6 +1753,10 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \ "logs_overflowed": true, "name": "string", "operating_system": "string", + "parent_id": { + "uuid": "string", + "valid": true + }, "ready_at": "2019-08-24T14:15:22Z", "resource_id": "4d5215ed-38bb-48ed-879a-fdb9ca58522f", "scripts": [ diff --git a/enterprise/audit/table.go b/enterprise/audit/table.go index 84cc7d451b4f1..d5bf22df9ff5e 100644 --- a/enterprise/audit/table.go +++ b/enterprise/audit/table.go @@ -342,6 +342,7 @@ var auditableResourcesTypes = map[any]map[string]Action{ "display_apps": ActionIgnore, "api_version": ActionIgnore, "display_order": ActionIgnore, + "parent_id": ActionIgnore, }, &database.WorkspaceApp{}: { "id": ActionIgnore, diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index e471e12b240b6..63dabab5bd793 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -3254,6 +3254,7 @@ export interface Workspace { // From codersdk/workspaceagents.go export interface WorkspaceAgent { readonly id: string; + readonly parent_id: string | null; readonly created_at: string; readonly updated_at: string; readonly first_connected_at?: string; diff --git a/site/src/pages/WorkspacePage/Workspace.stories.tsx b/site/src/pages/WorkspacePage/Workspace.stories.tsx index 957a651788d2c..ca782d73b68a0 100644 --- a/site/src/pages/WorkspacePage/Workspace.stories.tsx +++ b/site/src/pages/WorkspacePage/Workspace.stories.tsx @@ -103,6 +103,33 @@ export const Running: Story = { }, }; +export const RunningWithChildAgent: Story = { + args: { + ...Running.args, + workspace: { + ...Mocks.MockWorkspace, + latest_build: { + ...Mocks.MockWorkspace.latest_build, + resources: [ + { + ...Mocks.MockWorkspaceResource, + agents: [ + { + ...Mocks.MockWorkspaceAgent, + lifecycle_state: "ready", + }, + { + ...Mocks.MockWorkspaceChildAgent, + lifecycle_state: "ready", + }, + ], + }, + ], + }, + }, + }, +}; + export const RunningWithAppStatuses: Story = { args: { workspace: { diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 69ce29ed0e7d1..85c749596176a 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -269,22 +269,29 @@ export const Workspace: FC = ({ minWidth: 0 /* Prevent overflow */, }} > - {selectedResource.agents?.map((agent) => ( - - ))} + {selectedResource.agents?.map((agent) => { + // We do not want to display child agents at the top-level. + if (agent.parent_id !== null) { + return; + } + + return ( + + ); + })} {(!selectedResource.agents || selectedResource.agents?.length === 0) && ( @@ -348,15 +355,13 @@ export const Workspace: FC = ({ color: theme.palette.text.secondary, }} > - { - // Calculate total status count - selectedResource.agents - ?.flatMap((agent) => agent.apps ?? []) - .reduce( - (count, app) => count + (app.statuses?.length ?? 0), - 0, - ) - }{" "} + {// Calculate total status count + selectedResource.agents + ?.flatMap((agent) => agent.apps ?? []) + .reduce( + (count, app) => count + (app.statuses?.length ?? 0), + 0, + )}{" "} Total diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index a8db5f55879c4..dc0883bdbbdd5 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -966,6 +966,47 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { ], }; +export const MockWorkspaceChildAgent: TypesGen.WorkspaceAgent = { + apps: [MockWorkspaceApp], + architecture: "amd64", + created_at: "", + environment_variables: {}, + id: "test-workspace-child-agent", + parent_id: "test-workspace-agent", + name: "a-workspace-child-agent", + operating_system: "linux", + resource_id: "", + status: "connected", + updated_at: "", + version: MockBuildInfo.version, + api_version: MockBuildInfo.agent_api_version, + latency: { + "Coder Embedded DERP": { + latency_ms: 32.55, + preferred: true, + }, + }, + connection_timeout_seconds: 120, + troubleshooting_url: "https://coder.com/troubleshoot", + lifecycle_state: "starting", + logs_length: 0, + logs_overflowed: false, + log_sources: [MockWorkspaceAgentLogSource], + scripts: [], + startup_script_behavior: "non-blocking", + subsystems: ["envbox", "exectrace"], + health: { + healthy: true, + }, + display_apps: [ + "ssh_helper", + "port_forwarding_helper", + "vscode", + "vscode_insiders", + "web_terminal", + ], +}; + export const MockWorkspaceAppStatus: TypesGen.WorkspaceAppStatus = { id: "test-app-status", created_at: "2022-05-17T17:39:01.382927298Z", From 79de869b8c25b4ead300b60a010aeed16fe7ea63 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 10:16:11 +0000 Subject: [PATCH 02/11] fix: add missing parent_id --- site/src/testHelpers/entities.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index dc0883bdbbdd5..182e03c6e7e72 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -932,6 +932,7 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { created_at: "", environment_variables: {}, id: "test-workspace-agent", + parent_id: null, name: "a-workspace-agent", operating_system: "linux", resource_id: "", From 548deaa7095046f38d064ec72f2ab8cbc9d56611 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 10:51:14 +0000 Subject: [PATCH 03/11] chore: `make fmt` --- site/src/pages/WorkspacePage/Workspace.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 85c749596176a..68ba271898204 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -355,13 +355,15 @@ export const Workspace: FC = ({ color: theme.palette.text.secondary, }} > - {// Calculate total status count - selectedResource.agents - ?.flatMap((agent) => agent.apps ?? []) - .reduce( - (count, app) => count + (app.statuses?.length ?? 0), - 0, - )}{" "} + { + // Calculate total status count + selectedResource.agents + ?.flatMap((agent) => agent.apps ?? []) + .reduce( + (count, app) => count + (app.statuses?.length ?? 0), + 0, + ) + }{" "} Total From 742b7f21bc77b4739b28f624715ccf06b788360f Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 11:11:10 +0000 Subject: [PATCH 04/11] chore: update `InsertWorkspaceAgent` query Update the `InsertWorkspaceAgent` query to insert the `parent_id` field. Also updates the `dbmem` implementation, as well as updates the `dbgen` package to handle this new field as well. --- coderd/database/dbgen/dbgen.go | 1 + coderd/database/dbmem/dbmem.go | 1 + coderd/database/queries.sql.go | 5 ++++- coderd/database/queries/workspaceagents.sql | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/coderd/database/dbgen/dbgen.go b/coderd/database/dbgen/dbgen.go index 55c2fe4cf6965..966e3365839ca 100644 --- a/coderd/database/dbgen/dbgen.go +++ b/coderd/database/dbgen/dbgen.go @@ -181,6 +181,7 @@ func WorkspaceAgentPortShare(t testing.TB, db database.Store, orig database.Work func WorkspaceAgent(t testing.TB, db database.Store, orig database.WorkspaceAgent) database.WorkspaceAgent { agt, err := db.InsertWorkspaceAgent(genCtx, database.InsertWorkspaceAgentParams{ ID: takeFirst(orig.ID, uuid.New()), + ParentID: takeFirst(orig.ParentID, uuid.NullUUID{}), CreatedAt: takeFirst(orig.CreatedAt, dbtime.Now()), UpdatedAt: takeFirst(orig.UpdatedAt, dbtime.Now()), Name: takeFirst(orig.Name, testutil.GetRandomName(t)), diff --git a/coderd/database/dbmem/dbmem.go b/coderd/database/dbmem/dbmem.go index 6bae4455a89ef..eecf5f8b26329 100644 --- a/coderd/database/dbmem/dbmem.go +++ b/coderd/database/dbmem/dbmem.go @@ -9569,6 +9569,7 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser agent := database.WorkspaceAgent{ ID: arg.ID, + ParentID: arg.ParentID, CreatedAt: arg.CreatedAt, UpdatedAt: arg.UpdatedAt, ResourceID: arg.ResourceID, diff --git a/coderd/database/queries.sql.go b/coderd/database/queries.sql.go index 9d46821cfe7d8..6422005da46e9 100644 --- a/coderd/database/queries.sql.go +++ b/coderd/database/queries.sql.go @@ -14544,6 +14544,7 @@ const insertWorkspaceAgent = `-- name: InsertWorkspaceAgent :one INSERT INTO workspace_agents ( id, + parent_id, created_at, updated_at, name, @@ -14563,11 +14564,12 @@ INSERT INTO display_order ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) RETURNING id, created_at, updated_at, name, first_connected_at, last_connected_at, disconnected_at, resource_id, auth_token, auth_instance_id, architecture, environment_variables, operating_system, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, expanded_directory, logs_length, logs_overflowed, started_at, ready_at, subsystems, display_apps, api_version, display_order, parent_id ` type InsertWorkspaceAgentParams struct { ID uuid.UUID `db:"id" json:"id"` + ParentID uuid.NullUUID `db:"parent_id" json:"parent_id"` CreatedAt time.Time `db:"created_at" json:"created_at"` UpdatedAt time.Time `db:"updated_at" json:"updated_at"` Name string `db:"name" json:"name"` @@ -14590,6 +14592,7 @@ type InsertWorkspaceAgentParams struct { func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) { row := q.db.QueryRowContext(ctx, insertWorkspaceAgent, arg.ID, + arg.ParentID, arg.CreatedAt, arg.UpdatedAt, arg.Name, diff --git a/coderd/database/queries/workspaceagents.sql b/coderd/database/queries/workspaceagents.sql index 52d8b5275fc97..2e186461931b2 100644 --- a/coderd/database/queries/workspaceagents.sql +++ b/coderd/database/queries/workspaceagents.sql @@ -31,6 +31,7 @@ SELECT * FROM workspace_agents WHERE created_at > $1; INSERT INTO workspace_agents ( id, + parent_id, created_at, updated_at, name, @@ -50,7 +51,7 @@ INSERT INTO display_order ) VALUES - ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18) RETURNING *; + ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) RETURNING *; -- name: UpdateWorkspaceAgentConnectionByID :exec UPDATE From 2f9844a3f55ae21329780e4461cdfbd28f428919 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 11:18:11 +0000 Subject: [PATCH 05/11] fix: explicitly set `ParentID` to `NullUUID{}` --- coderd/provisionerdserver/provisionerdserver.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coderd/provisionerdserver/provisionerdserver.go b/coderd/provisionerdserver/provisionerdserver.go index 9362d2f3e5a85..df3bdc6dc5457 100644 --- a/coderd/provisionerdserver/provisionerdserver.go +++ b/coderd/provisionerdserver/provisionerdserver.go @@ -2006,6 +2006,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid. agentID := uuid.New() dbAgent, err := db.InsertWorkspaceAgent(ctx, database.InsertWorkspaceAgentParams{ ID: agentID, + ParentID: uuid.NullUUID{}, CreatedAt: dbtime.Now(), UpdatedAt: dbtime.Now(), ResourceID: resource.ID, From cb4e5b23a147278b481e44486d5e1c8847e89326 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 15:12:43 +0000 Subject: [PATCH 06/11] chore: test for `ParentID` being `uuid.NullUUID{}` --- coderd/provisionerdserver/provisionerdserver_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/coderd/provisionerdserver/provisionerdserver_test.go b/coderd/provisionerdserver/provisionerdserver_test.go index caeef8a9793b7..7a4b7640ed60a 100644 --- a/coderd/provisionerdserver/provisionerdserver_test.go +++ b/coderd/provisionerdserver/provisionerdserver_test.go @@ -2153,6 +2153,7 @@ func TestInsertWorkspaceResource(t *testing.T) { require.NoError(t, err) require.Len(t, agents, 1) agent := agents[0] + require.Equal(t, uuid.NullUUID{}, agent.ParentID) require.Equal(t, "amd64", agent.Architecture) require.Equal(t, "linux", agent.OperatingSystem) want, err := json.Marshal(map[string]string{ From 20da6e47368c2cb1cf3007d2e1610e95e6397e8d Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 15:12:59 +0000 Subject: [PATCH 07/11] chore: improve comment --- site/src/pages/WorkspacePage/Workspace.tsx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 68ba271898204..87a2b7f0ecf39 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -269,13 +269,13 @@ export const Workspace: FC = ({ minWidth: 0 /* Prevent overflow */, }} > - {selectedResource.agents?.map((agent) => { - // We do not want to display child agents at the top-level. - if (agent.parent_id !== null) { - return; - } - - return ( + {selectedResource.agents + // If an agent has a `parent_id`, that means it is + // child of another agent. We do not want these agents + // to be displayed at the top-level on this page. We + // want them to display _as children_ of their parents. + ?.filter((agent) => agent.parent_id !== null) + .map((agent) => ( = ({ serverAPIVersion={buildInfo?.agent_api_version || ""} onUpdateAgent={handleUpdate} // On updating the workspace the agent version is also updated /> - ); - })} + ))} {(!selectedResource.agents || selectedResource.agents?.length === 0) && ( From 579b9c0bcf711588d1b0e716fdc7df4ea1ea07bc Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 15:13:08 +0000 Subject: [PATCH 08/11] chore: remove app from `MockWorkspaceChildAgent` --- site/src/testHelpers/entities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/src/testHelpers/entities.ts b/site/src/testHelpers/entities.ts index 182e03c6e7e72..7fa69c006b8e7 100644 --- a/site/src/testHelpers/entities.ts +++ b/site/src/testHelpers/entities.ts @@ -968,7 +968,7 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = { }; export const MockWorkspaceChildAgent: TypesGen.WorkspaceAgent = { - apps: [MockWorkspaceApp], + apps: [], architecture: "amd64", created_at: "", environment_variables: {}, From 91c22e1e5b6eb409dd9b0924192a860da243d78b Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 22:24:11 +0000 Subject: [PATCH 09/11] fix: invert condition --- site/src/pages/WorkspacePage/Workspace.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 87a2b7f0ecf39..38a41051d3dee 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -274,7 +274,7 @@ export const Workspace: FC = ({ // child of another agent. We do not want these agents // to be displayed at the top-level on this page. We // want them to display _as children_ of their parents. - ?.filter((agent) => agent.parent_id !== null) + ?.filter((agent) => agent.parent_id === null) .map((agent) => ( = ({ color: theme.palette.text.secondary, }} > - { - // Calculate total status count - selectedResource.agents - ?.flatMap((agent) => agent.apps ?? []) - .reduce( - (count, app) => count + (app.statuses?.length ?? 0), - 0, - ) - }{" "} + {// Calculate total status count + selectedResource.agents + ?.flatMap((agent) => agent.apps ?? []) + .reduce( + (count, app) => count + (app.statuses?.length ?? 0), + 0, + )}{" "} Total From a8e9c7b65be72c94557bcd3b155ede6f7340b920 Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 22:28:32 +0000 Subject: [PATCH 10/11] fix: bump migration number --- ...down.sql => 000321_add_parent_id_to_workspace_agents.down.sql} | 0 ...nts.up.sql => 000321_add_parent_id_to_workspace_agents.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename coderd/database/migrations/{000320_add_parent_id_to_workspace_agents.down.sql => 000321_add_parent_id_to_workspace_agents.down.sql} (100%) rename coderd/database/migrations/{000320_add_parent_id_to_workspace_agents.up.sql => 000321_add_parent_id_to_workspace_agents.up.sql} (100%) diff --git a/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.down.sql b/coderd/database/migrations/000321_add_parent_id_to_workspace_agents.down.sql similarity index 100% rename from coderd/database/migrations/000320_add_parent_id_to_workspace_agents.down.sql rename to coderd/database/migrations/000321_add_parent_id_to_workspace_agents.down.sql diff --git a/coderd/database/migrations/000320_add_parent_id_to_workspace_agents.up.sql b/coderd/database/migrations/000321_add_parent_id_to_workspace_agents.up.sql similarity index 100% rename from coderd/database/migrations/000320_add_parent_id_to_workspace_agents.up.sql rename to coderd/database/migrations/000321_add_parent_id_to_workspace_agents.up.sql From 8df43c4a2182a83a5923d5d0f5376ff32939191b Mon Sep 17 00:00:00 2001 From: Danielle Maywood Date: Mon, 12 May 2025 22:32:52 +0000 Subject: [PATCH 11/11] chore: `make fmt` --- site/src/pages/WorkspacePage/Workspace.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/site/src/pages/WorkspacePage/Workspace.tsx b/site/src/pages/WorkspacePage/Workspace.tsx index 38a41051d3dee..1c60b8b86b50b 100644 --- a/site/src/pages/WorkspacePage/Workspace.tsx +++ b/site/src/pages/WorkspacePage/Workspace.tsx @@ -354,13 +354,15 @@ export const Workspace: FC = ({ color: theme.palette.text.secondary, }} > - {// Calculate total status count - selectedResource.agents - ?.flatMap((agent) => agent.apps ?? []) - .reduce( - (count, app) => count + (app.statuses?.length ?? 0), - 0, - )}{" "} + { + // Calculate total status count + selectedResource.agents + ?.flatMap((agent) => agent.apps ?? []) + .reduce( + (count, app) => count + (app.statuses?.length ?? 0), + 0, + ) + }{" "} Total pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy