mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
chore: allow multiple agent subsystems, add exectrace (#8933)
This commit is contained in:
22
coderd/apidoc/docs.go
generated
22
coderd/apidoc/docs.go
generated
@ -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"
|
||||
|
22
coderd/apidoc/swagger.json
generated
22
coderd/apidoc/swagger.json
generated
@ -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"
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
}
|
||||
|
8
coderd/database/dump.sql
generated
8
coderd/database/dump.sql
generated
@ -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.';
|
||||
|
@ -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;
|
@ -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;
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ UPDATE
|
||||
SET
|
||||
version = $2,
|
||||
expanded_directory = $3,
|
||||
subsystem = $4
|
||||
subsystems = $4
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user