chore: allow multiple agent subsystems, add exectrace (#8933)

This commit is contained in:
Dean Sheather
2023-08-08 22:10:28 -07:00
committed by GitHub
parent 70bd23a40a
commit 07fd73c4a0
26 changed files with 285 additions and 119 deletions

22
coderd/apidoc/docs.go generated
View File

@ -6447,8 +6447,11 @@ const docTemplate = `{
"expanded_directory": {
"type": "string"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
"subsystems": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
}
},
"version": {
"type": "string"
@ -6903,10 +6906,14 @@ const docTemplate = `{
"codersdk.AgentSubsystem": {
"type": "string",
"enum": [
"envbox"
"envbox",
"envbuilder",
"exectrace"
],
"x-enum-varnames": [
"AgentSubsystemEnvbox"
"AgentSubsystemEnvbox",
"AgentSubsystemEnvbuilder",
"AgentSubsystemExectrace"
]
},
"codersdk.AppHostResponse": {
@ -10556,8 +10563,11 @@ const docTemplate = `{
"status": {
"$ref": "#/definitions/codersdk.WorkspaceAgentStatus"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
"subsystems": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
}
},
"troubleshooting_url": {
"type": "string"

View File

@ -5697,8 +5697,11 @@
"expanded_directory": {
"type": "string"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
"subsystems": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
}
},
"version": {
"type": "string"
@ -6130,8 +6133,12 @@
},
"codersdk.AgentSubsystem": {
"type": "string",
"enum": ["envbox"],
"x-enum-varnames": ["AgentSubsystemEnvbox"]
"enum": ["envbox", "envbuilder", "exectrace"],
"x-enum-varnames": [
"AgentSubsystemEnvbox",
"AgentSubsystemEnvbuilder",
"AgentSubsystemExectrace"
]
},
"codersdk.AppHostResponse": {
"type": "object",
@ -9571,8 +9578,11 @@
"status": {
"$ref": "#/definitions/codersdk.WorkspaceAgentStatus"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
"subsystems": {
"type": "array",
"items": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
}
},
"troubleshooting_url": {
"type": "string"

View File

@ -1064,8 +1064,10 @@ func (s *MethodTestSuite) TestWorkspace() {
res := dbgen.WorkspaceResource(s.T(), db, database.WorkspaceResource{JobID: build.JobID})
agt := dbgen.WorkspaceAgent(s.T(), db, database.WorkspaceAgent{ResourceID: res.ID})
check.Args(database.UpdateWorkspaceAgentStartupByIDParams{
ID: agt.ID,
Subsystem: database.WorkspaceAgentSubsystemNone,
ID: agt.ID,
Subsystems: []database.WorkspaceAgentSubsystem{
database.WorkspaceAgentSubsystemEnvbox,
},
}).Asserts(ws, rbac.ActionUpdate).Returns()
}))
s.Run("GetWorkspaceAgentLogsAfter", s.Subtest(func(db database.Store, check *expects) {

View File

@ -5203,6 +5203,23 @@ func (q *FakeQuerier) UpdateWorkspaceAgentStartupByID(_ context.Context, arg dat
return err
}
if len(arg.Subsystems) > 0 {
seen := map[database.WorkspaceAgentSubsystem]struct{}{
arg.Subsystems[0]: {},
}
for i := 1; i < len(arg.Subsystems); i++ {
s := arg.Subsystems[i]
if _, ok := seen[s]; ok {
return xerrors.Errorf("duplicate subsystem %q", s)
}
seen[s] = struct{}{}
if arg.Subsystems[i-1] > arg.Subsystems[i] {
return xerrors.Errorf("subsystems not sorted: %q > %q", arg.Subsystems[i-1], arg.Subsystems[i])
}
}
}
q.mutex.Lock()
defer q.mutex.Unlock()
@ -5213,7 +5230,7 @@ func (q *FakeQuerier) UpdateWorkspaceAgentStartupByID(_ context.Context, arg dat
agent.Version = arg.Version
agent.ExpandedDirectory = arg.ExpandedDirectory
agent.Subsystem = arg.Subsystem
agent.Subsystems = arg.Subsystems
q.workspaceAgents[index] = agent
return nil
}

View File

@ -148,7 +148,8 @@ CREATE TYPE workspace_agent_log_source AS ENUM (
CREATE TYPE workspace_agent_subsystem AS ENUM (
'envbuilder',
'envbox',
'none'
'none',
'exectrace'
);
CREATE TYPE workspace_app_health AS ENUM (
@ -775,11 +776,12 @@ CREATE TABLE workspace_agents (
shutdown_script_timeout_seconds integer DEFAULT 0 NOT NULL,
logs_length integer DEFAULT 0 NOT NULL,
logs_overflowed boolean DEFAULT false NOT NULL,
subsystem workspace_agent_subsystem DEFAULT 'none'::workspace_agent_subsystem NOT NULL,
startup_script_behavior startup_script_behavior DEFAULT 'non-blocking'::startup_script_behavior NOT NULL,
started_at timestamp with time zone,
ready_at timestamp with time zone,
CONSTRAINT max_logs_length CHECK ((logs_length <= 1048576))
subsystems workspace_agent_subsystem[] DEFAULT '{}'::workspace_agent_subsystem[],
CONSTRAINT max_logs_length CHECK ((logs_length <= 1048576)),
CONSTRAINT subsystems_not_none CHECK ((NOT ('none'::workspace_agent_subsystem = ANY (subsystems))))
);
COMMENT ON COLUMN workspace_agents.version IS 'Version tracks the version of the currently running workspace agent. Workspace agents register their version upon start.';

View File

@ -0,0 +1,17 @@
BEGIN;
-- Bring back the subsystem column.
ALTER TABLE workspace_agents ADD COLUMN subsystem workspace_agent_subsystem NOT NULL DEFAULT 'none';
-- Update all existing workspace_agents to have subsystem = subsystems[0] unless
-- subsystems is empty.
UPDATE workspace_agents SET subsystem = subsystems[1] WHERE cardinality(subsystems) > 0;
-- Drop the subsystems column from workspace_agents.
ALTER TABLE workspace_agents DROP COLUMN subsystems;
-- We cannot drop the "exectrace" value from the workspace_agent_subsystem type
-- because you cannot drop values from an enum type.
UPDATE workspace_agents SET subsystem = 'none' WHERE subsystem = 'exectrace';
COMMIT;

View File

@ -0,0 +1,21 @@
BEGIN;
-- Add "exectrace" to workspace_agent_subsystem type.
ALTER TYPE workspace_agent_subsystem ADD VALUE 'exectrace';
-- Create column subsystems in workspace_agents table, with default value being
-- an empty array.
ALTER TABLE workspace_agents ADD COLUMN subsystems workspace_agent_subsystem[] DEFAULT '{}';
-- Add a constraint that the subsystems cannot contain the deprecated value
-- 'none'.
ALTER TABLE workspace_agents ADD CONSTRAINT subsystems_not_none CHECK (NOT ('none' = ANY (subsystems)));
-- Update all existing workspace_agents to have subsystems = [subsystem] unless
-- the subsystem is 'none'.
UPDATE workspace_agents SET subsystems = ARRAY[subsystem] WHERE subsystem != 'none';
-- Drop the subsystem column from workspace_agents.
ALTER TABLE workspace_agents DROP COLUMN subsystem;
COMMIT;

View File

@ -1307,6 +1307,7 @@ const (
WorkspaceAgentSubsystemEnvbuilder WorkspaceAgentSubsystem = "envbuilder"
WorkspaceAgentSubsystemEnvbox WorkspaceAgentSubsystem = "envbox"
WorkspaceAgentSubsystemNone WorkspaceAgentSubsystem = "none"
WorkspaceAgentSubsystemExectrace WorkspaceAgentSubsystem = "exectrace"
)
func (e *WorkspaceAgentSubsystem) Scan(src interface{}) error {
@ -1348,7 +1349,8 @@ func (e WorkspaceAgentSubsystem) Valid() bool {
switch e {
case WorkspaceAgentSubsystemEnvbuilder,
WorkspaceAgentSubsystemEnvbox,
WorkspaceAgentSubsystemNone:
WorkspaceAgentSubsystemNone,
WorkspaceAgentSubsystemExectrace:
return true
}
return false
@ -1359,6 +1361,7 @@ func AllWorkspaceAgentSubsystemValues() []WorkspaceAgentSubsystem {
WorkspaceAgentSubsystemEnvbuilder,
WorkspaceAgentSubsystemEnvbox,
WorkspaceAgentSubsystemNone,
WorkspaceAgentSubsystemExectrace,
}
}
@ -1944,14 +1947,14 @@ type WorkspaceAgent struct {
// Total length of startup logs
LogsLength int32 `db:"logs_length" json:"logs_length"`
// Whether the startup logs overflowed in length
LogsOverflowed bool `db:"logs_overflowed" json:"logs_overflowed"`
Subsystem WorkspaceAgentSubsystem `db:"subsystem" json:"subsystem"`
LogsOverflowed bool `db:"logs_overflowed" json:"logs_overflowed"`
// When startup script behavior is non-blocking, the workspace will be ready and accessible upon agent connection, when it is blocking, workspace will wait for the startup script to complete before becoming ready and accessible.
StartupScriptBehavior StartupScriptBehavior `db:"startup_script_behavior" json:"startup_script_behavior"`
// The time the agent entered the starting lifecycle state
StartedAt sql.NullTime `db:"started_at" json:"started_at"`
// The time the agent entered the ready or start_error lifecycle state
ReadyAt sql.NullTime `db:"ready_at" json:"ready_at"`
ReadyAt sql.NullTime `db:"ready_at" json:"ready_at"`
Subsystems []WorkspaceAgentSubsystem `db:"subsystems" json:"subsystems"`
}
type WorkspaceAgentLog struct {

View File

@ -6239,7 +6239,7 @@ func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context) error {
const getWorkspaceAgentByAuthToken = `-- name: GetWorkspaceAgentByAuthToken :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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems
FROM
workspace_agents
WHERE
@ -6281,17 +6281,17 @@ func (q *sqlQuerier) GetWorkspaceAgentByAuthToken(ctx context.Context, authToken
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
)
return i, err
}
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems
FROM
workspace_agents
WHERE
@ -6331,17 +6331,17 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
)
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems
FROM
workspace_agents
WHERE
@ -6383,10 +6383,10 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
)
return i, err
}
@ -6506,7 +6506,7 @@ func (q *sqlQuerier) GetWorkspaceAgentMetadata(ctx context.Context, workspaceAge
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems
FROM
workspace_agents
WHERE
@ -6552,10 +6552,10 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
); err != nil {
return nil, err
}
@ -6571,7 +6571,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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at 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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems FROM workspace_agents WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) {
@ -6613,10 +6613,10 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
); err != nil {
return nil, err
}
@ -6633,7 +6633,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.startup_script, 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.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.subsystem, workspace_agents.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_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.startup_script, 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.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.logs_length, workspace_agents.logs_overflowed, workspace_agents.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems
FROM
workspace_agents
JOIN
@ -6691,10 +6691,10 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
); err != nil {
return nil, err
}
@ -6735,7 +6735,7 @@ INSERT INTO
shutdown_script_timeout_seconds
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) 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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, subsystem, startup_script_behavior, started_at, ready_at
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) 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, startup_script, instance_metadata, resource_metadata, directory, version, last_connected_replica_id, connection_timeout_seconds, troubleshooting_url, motd_file, lifecycle_state, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, logs_length, logs_overflowed, startup_script_behavior, started_at, ready_at, subsystems
`
type InsertWorkspaceAgentParams struct {
@ -6817,10 +6817,10 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
&i.ShutdownScriptTimeoutSeconds,
&i.LogsLength,
&i.LogsOverflowed,
&i.Subsystem,
&i.StartupScriptBehavior,
&i.StartedAt,
&i.ReadyAt,
pq.Array(&i.Subsystems),
)
return i, err
}
@ -7040,16 +7040,16 @@ UPDATE
SET
version = $2,
expanded_directory = $3,
subsystem = $4
subsystems = $4
WHERE
id = $1
`
type UpdateWorkspaceAgentStartupByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Version string `db:"version" json:"version"`
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
Subsystem WorkspaceAgentSubsystem `db:"subsystem" json:"subsystem"`
ID uuid.UUID `db:"id" json:"id"`
Version string `db:"version" json:"version"`
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
Subsystems []WorkspaceAgentSubsystem `db:"subsystems" json:"subsystems"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error {
@ -7057,7 +7057,7 @@ func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg Up
arg.ID,
arg.Version,
arg.ExpandedDirectory,
arg.Subsystem,
pq.Array(arg.Subsystems),
)
return err
}

View File

@ -83,7 +83,7 @@ UPDATE
SET
version = $2,
expanded_directory = $3,
subsystem = $4
subsystems = $4
WHERE
id = $1;

View File

@ -544,6 +544,11 @@ func ConvertProvisionerJob(job database.ProvisionerJob) ProvisionerJob {
// ConvertWorkspaceAgent anonymizes a workspace agent.
func ConvertWorkspaceAgent(agent database.WorkspaceAgent) WorkspaceAgent {
subsystems := []string{}
for _, subsystem := range agent.Subsystems {
subsystems = append(subsystems, string(subsystem))
}
snapAgent := WorkspaceAgent{
ID: agent.ID,
CreatedAt: agent.CreatedAt,
@ -556,7 +561,7 @@ func ConvertWorkspaceAgent(agent database.WorkspaceAgent) WorkspaceAgent {
Directory: agent.Directory != "",
ConnectionTimeoutSeconds: agent.ConnectionTimeoutSeconds,
ShutdownScript: agent.ShutdownScript.Valid,
Subsystem: string(agent.Subsystem),
Subsystems: subsystems,
}
if agent.FirstConnectedAt.Valid {
snapAgent.FirstConnectedAt = &agent.FirstConnectedAt.Time
@ -792,7 +797,7 @@ type WorkspaceAgent struct {
DisconnectedAt *time.Time `json:"disconnected_at"`
ConnectionTimeoutSeconds int32 `json:"connection_timeout_seconds"`
ShutdownScript bool `json:"shutdown_script"`
Subsystem string `json:"subsystem"`
Subsystems []string `json:"subsystems"`
}
type WorkspaceAgentStat struct {

View File

@ -54,15 +54,16 @@ func TestTelemetry(t *testing.T) {
SharingLevel: database.AppSharingLevelOwner,
Health: database.WorkspaceAppHealthDisabled,
})
wsagent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
Subsystem: database.WorkspaceAgentSubsystemEnvbox,
})
wsagent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{})
// Update the workspace agent to have a valid subsystem.
err = db.UpdateWorkspaceAgentStartupByID(ctx, database.UpdateWorkspaceAgentStartupByIDParams{
ID: wsagent.ID,
Version: wsagent.Version,
ExpandedDirectory: wsagent.ExpandedDirectory,
Subsystem: database.WorkspaceAgentSubsystemEnvbox,
Subsystems: []database.WorkspaceAgentSubsystem{
database.WorkspaceAgentSubsystemEnvbox,
database.WorkspaceAgentSubsystemExectrace,
},
})
require.NoError(t, err)
@ -98,7 +99,9 @@ func TestTelemetry(t *testing.T) {
require.Len(t, snapshot.WorkspaceProxies, 1)
wsa := snapshot.WorkspaceAgents[0]
require.Equal(t, string(database.WorkspaceAgentSubsystemEnvbox), wsa.Subsystem)
require.Len(t, wsa.Subsystems, 2)
require.Equal(t, string(database.WorkspaceAgentSubsystemEnvbox), wsa.Subsystems[0])
require.Equal(t, string(database.WorkspaceAgentSubsystemExectrace), wsa.Subsystems[1])
})
t.Run("HashedEmail", func(t *testing.T) {
t.Parallel()

View File

@ -14,6 +14,7 @@ import (
"net/netip"
"net/url"
"runtime/pprof"
"sort"
"strconv"
"strings"
"sync"
@ -219,11 +220,31 @@ func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Reques
return
}
// Validate subsystems.
seen := make(map[codersdk.AgentSubsystem]bool)
for _, s := range req.Subsystems {
if !s.Valid() {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid workspace agent subsystem provided.",
Detail: fmt.Sprintf("invalid subsystem: %q", s),
})
return
}
if seen[s] {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: "Invalid workspace agent subsystem provided.",
Detail: fmt.Sprintf("duplicate subsystem: %q", s),
})
return
}
seen[s] = true
}
if err := api.Database.UpdateWorkspaceAgentStartupByID(ctx, database.UpdateWorkspaceAgentStartupByIDParams{
ID: apiAgent.ID,
Version: req.Version,
ExpandedDirectory: req.ExpandedDirectory,
Subsystem: convertWorkspaceAgentSubsystem(req.Subsystem),
Subsystems: convertWorkspaceAgentSubsystems(req.Subsystems),
}); err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Error setting agent version",
@ -1277,6 +1298,11 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
if dbAgent.TroubleshootingURL != "" {
troubleshootingURL = dbAgent.TroubleshootingURL
}
subsystems := make([]codersdk.AgentSubsystem, len(dbAgent.Subsystems))
for i, subsystem := range dbAgent.Subsystems {
subsystems[i] = codersdk.AgentSubsystem(subsystem)
}
workspaceAgent := codersdk.WorkspaceAgent{
ID: dbAgent.ID,
CreatedAt: dbAgent.CreatedAt,
@ -1302,7 +1328,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
LoginBeforeReady: dbAgent.StartupScriptBehavior != database.StartupScriptBehaviorBlocking,
ShutdownScript: dbAgent.ShutdownScript.String,
ShutdownScriptTimeoutSeconds: dbAgent.ShutdownScriptTimeoutSeconds,
Subsystem: codersdk.AgentSubsystem(dbAgent.Subsystem),
Subsystems: subsystems,
}
node := coordinator.Node(dbAgent.ID)
if node != nil {
@ -2114,11 +2140,23 @@ func convertWorkspaceAgentLog(logEntry database.WorkspaceAgentLog) codersdk.Work
}
}
func convertWorkspaceAgentSubsystem(ss codersdk.AgentSubsystem) database.WorkspaceAgentSubsystem {
switch ss {
case codersdk.AgentSubsystemEnvbox:
return database.WorkspaceAgentSubsystemEnvbox
default:
return database.WorkspaceAgentSubsystemNone
func convertWorkspaceAgentSubsystems(ss []codersdk.AgentSubsystem) []database.WorkspaceAgentSubsystem {
out := make([]database.WorkspaceAgentSubsystem, 0, len(ss))
for _, s := range ss {
switch s {
case codersdk.AgentSubsystemEnvbox:
out = append(out, database.WorkspaceAgentSubsystemEnvbox)
case codersdk.AgentSubsystemEnvbuilder:
out = append(out, database.WorkspaceAgentSubsystemEnvbuilder)
case codersdk.AgentSubsystemExectrace:
out = append(out, database.WorkspaceAgentSubsystemExectrace)
default:
// Invalid, drop it.
}
}
sort.Slice(out, func(i, j int) bool {
return out[i] < out[j]
})
return out
}

View File

@ -1195,16 +1195,23 @@ func TestWorkspaceAgent_Startup(t *testing.T) {
ctx := testutil.Context(t, testutil.WaitMedium)
const (
expectedVersion = "v1.2.3"
expectedDir = "/home/coder"
expectedSubsystem = codersdk.AgentSubsystemEnvbox
var (
expectedVersion = "v1.2.3"
expectedDir = "/home/coder"
expectedSubsystems = []codersdk.AgentSubsystem{
codersdk.AgentSubsystemEnvbox,
codersdk.AgentSubsystemExectrace,
}
)
err := agentClient.PostStartup(ctx, agentsdk.PostStartupRequest{
Version: expectedVersion,
ExpandedDirectory: expectedDir,
Subsystem: expectedSubsystem,
Subsystems: []codersdk.AgentSubsystem{
// Not sorted.
expectedSubsystems[1],
expectedSubsystems[0],
},
})
require.NoError(t, err)
@ -1215,7 +1222,8 @@ func TestWorkspaceAgent_Startup(t *testing.T) {
require.NoError(t, err)
require.Equal(t, expectedVersion, wsagent.Version)
require.Equal(t, expectedDir, wsagent.ExpandedDirectory)
require.Equal(t, expectedSubsystem, wsagent.Subsystem)
// Sorted
require.Equal(t, expectedSubsystems, wsagent.Subsystems)
})
t.Run("InvalidSemver", func(t *testing.T) {