mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
feat: allow configuring display apps from template (#9100)
This commit is contained in:
23
coderd/apidoc/docs.go
generated
23
coderd/apidoc/docs.go
generated
@ -8068,6 +8068,23 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.DisplayApp": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"vscode",
|
||||
"vscode_insiders",
|
||||
"web_terminal",
|
||||
"port_forwarding_helper",
|
||||
"ssh_helper"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"DisplayAppVSCodeDesktop",
|
||||
"DisplayAppVSCodeInsiders",
|
||||
"DisplayAppWebTerminal",
|
||||
"DisplayAppPortForward",
|
||||
"DisplayAppSSH"
|
||||
]
|
||||
},
|
||||
"codersdk.Entitlement": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
@ -10611,6 +10628,12 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"display_apps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.DisplayApp"
|
||||
}
|
||||
},
|
||||
"environment_variables": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
|
23
coderd/apidoc/swagger.json
generated
23
coderd/apidoc/swagger.json
generated
@ -7223,6 +7223,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.DisplayApp": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"vscode",
|
||||
"vscode_insiders",
|
||||
"web_terminal",
|
||||
"port_forwarding_helper",
|
||||
"ssh_helper"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"DisplayAppVSCodeDesktop",
|
||||
"DisplayAppVSCodeInsiders",
|
||||
"DisplayAppWebTerminal",
|
||||
"DisplayAppPortForward",
|
||||
"DisplayAppSSH"
|
||||
]
|
||||
},
|
||||
"codersdk.Entitlement": {
|
||||
"type": "string",
|
||||
"enum": ["entitled", "grace_period", "not_entitled"],
|
||||
@ -9613,6 +9630,12 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"display_apps": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.DisplayApp"
|
||||
}
|
||||
},
|
||||
"environment_variables": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
|
@ -4428,6 +4428,7 @@ func (q *FakeQuerier) InsertWorkspaceAgent(_ context.Context, arg database.Inser
|
||||
MOTDFile: arg.MOTDFile,
|
||||
LifecycleState: database.WorkspaceAgentLifecycleStateCreated,
|
||||
ShutdownScript: arg.ShutdownScript,
|
||||
DisplayApps: arg.DisplayApps,
|
||||
}
|
||||
|
||||
q.workspaceAgents = append(q.workspaceAgents, agent)
|
||||
|
9
coderd/database/dump.sql
generated
9
coderd/database/dump.sql
generated
@ -31,6 +31,14 @@ CREATE TYPE build_reason AS ENUM (
|
||||
'autodelete'
|
||||
);
|
||||
|
||||
CREATE TYPE display_app AS ENUM (
|
||||
'vscode',
|
||||
'vscode_insiders',
|
||||
'web_terminal',
|
||||
'ssh_helper',
|
||||
'port_forwarding_helper'
|
||||
);
|
||||
|
||||
CREATE TYPE group_source AS ENUM (
|
||||
'user',
|
||||
'oidc'
|
||||
@ -780,6 +788,7 @@ CREATE TABLE workspace_agents (
|
||||
started_at timestamp with time zone,
|
||||
ready_at timestamp with time zone,
|
||||
subsystems workspace_agent_subsystem[] DEFAULT '{}'::workspace_agent_subsystem[],
|
||||
display_apps display_app[] DEFAULT '{vscode,vscode_insiders,web_terminal,ssh_helper,port_forwarding_helper}'::display_app[],
|
||||
CONSTRAINT max_logs_length CHECK ((logs_length <= 1048576)),
|
||||
CONSTRAINT subsystems_not_none CHECK ((NOT ('none'::workspace_agent_subsystem = ANY (subsystems))))
|
||||
);
|
||||
|
@ -0,0 +1,5 @@
|
||||
BEGIN;
|
||||
ALTER TABLE workspace_agents DROP COLUMN display_apps;
|
||||
DROP TYPE display_app;
|
||||
COMMIT;
|
||||
|
@ -0,0 +1,4 @@
|
||||
BEGIN;
|
||||
CREATE TYPE display_app AS ENUM ('vscode', 'vscode_insiders', 'web_terminal', 'ssh_helper', 'port_forwarding_helper');
|
||||
ALTER TABLE workspace_agents ADD column display_apps display_app[] DEFAULT '{vscode, vscode_insiders, web_terminal, ssh_helper, port_forwarding_helper}';
|
||||
COMMIT;
|
@ -281,6 +281,73 @@ func AllBuildReasonValues() []BuildReason {
|
||||
}
|
||||
}
|
||||
|
||||
type DisplayApp string
|
||||
|
||||
const (
|
||||
DisplayAppVscode DisplayApp = "vscode"
|
||||
DisplayAppVscodeInsiders DisplayApp = "vscode_insiders"
|
||||
DisplayAppWebTerminal DisplayApp = "web_terminal"
|
||||
DisplayAppSSHHelper DisplayApp = "ssh_helper"
|
||||
DisplayAppPortForwardingHelper DisplayApp = "port_forwarding_helper"
|
||||
)
|
||||
|
||||
func (e *DisplayApp) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = DisplayApp(s)
|
||||
case string:
|
||||
*e = DisplayApp(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for DisplayApp: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullDisplayApp struct {
|
||||
DisplayApp DisplayApp `json:"display_app"`
|
||||
Valid bool `json:"valid"` // Valid is true if DisplayApp is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullDisplayApp) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.DisplayApp, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.DisplayApp.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullDisplayApp) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.DisplayApp), nil
|
||||
}
|
||||
|
||||
func (e DisplayApp) Valid() bool {
|
||||
switch e {
|
||||
case DisplayAppVscode,
|
||||
DisplayAppVscodeInsiders,
|
||||
DisplayAppWebTerminal,
|
||||
DisplayAppSSHHelper,
|
||||
DisplayAppPortForwardingHelper:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllDisplayAppValues() []DisplayApp {
|
||||
return []DisplayApp{
|
||||
DisplayAppVscode,
|
||||
DisplayAppVscodeInsiders,
|
||||
DisplayAppWebTerminal,
|
||||
DisplayAppSSHHelper,
|
||||
DisplayAppPortForwardingHelper,
|
||||
}
|
||||
}
|
||||
|
||||
type GroupSource string
|
||||
|
||||
const (
|
||||
@ -1953,8 +2020,9 @@ type WorkspaceAgent struct {
|
||||
// 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"`
|
||||
Subsystems []WorkspaceAgentSubsystem `db:"subsystems" json:"subsystems"`
|
||||
ReadyAt sql.NullTime `db:"ready_at" json:"ready_at"`
|
||||
Subsystems []WorkspaceAgentSubsystem `db:"subsystems" json:"subsystems"`
|
||||
DisplayApps []DisplayApp `db:"display_apps" json:"display_apps"`
|
||||
}
|
||||
|
||||
type WorkspaceAgentLog struct {
|
||||
|
@ -6362,7 +6362,7 @@ func (q *sqlQuerier) DeleteOldWorkspaceAgentLogs(ctx context.Context) error {
|
||||
|
||||
const getWorkspaceAgentAndOwnerByAuthToken = `-- name: GetWorkspaceAgentAndOwnerByAuthToken :one
|
||||
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.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems,
|
||||
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, workspace_agents.display_apps,
|
||||
workspaces.id AS workspace_id,
|
||||
users.id AS owner_id,
|
||||
users.username AS owner_name,
|
||||
@ -6461,6 +6461,7 @@ func (q *sqlQuerier) GetWorkspaceAgentAndOwnerByAuthToken(ctx context.Context, a
|
||||
&i.WorkspaceAgent.StartedAt,
|
||||
&i.WorkspaceAgent.ReadyAt,
|
||||
pq.Array(&i.WorkspaceAgent.Subsystems),
|
||||
pq.Array(&i.WorkspaceAgent.DisplayApps),
|
||||
&i.WorkspaceID,
|
||||
&i.OwnerID,
|
||||
&i.OwnerName,
|
||||
@ -6473,7 +6474,7 @@ func (q *sqlQuerier) GetWorkspaceAgentAndOwnerByAuthToken(ctx context.Context, a
|
||||
|
||||
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, startup_script_behavior, started_at, ready_at, subsystems
|
||||
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, display_apps
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
@ -6517,13 +6518,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W
|
||||
&i.StartedAt,
|
||||
&i.ReadyAt,
|
||||
pq.Array(&i.Subsystems),
|
||||
pq.Array(&i.DisplayApps),
|
||||
)
|
||||
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, startup_script_behavior, started_at, ready_at, subsystems
|
||||
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, display_apps
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
@ -6569,6 +6571,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst
|
||||
&i.StartedAt,
|
||||
&i.ReadyAt,
|
||||
pq.Array(&i.Subsystems),
|
||||
pq.Array(&i.DisplayApps),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -6688,7 +6691,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, startup_script_behavior, started_at, ready_at, subsystems
|
||||
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, display_apps
|
||||
FROM
|
||||
workspace_agents
|
||||
WHERE
|
||||
@ -6738,6 +6741,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
|
||||
&i.StartedAt,
|
||||
&i.ReadyAt,
|
||||
pq.Array(&i.Subsystems),
|
||||
pq.Array(&i.DisplayApps),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -6753,7 +6757,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, startup_script_behavior, started_at, ready_at, subsystems 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, display_apps FROM workspace_agents WHERE created_at > $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) {
|
||||
@ -6799,6 +6803,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
|
||||
&i.StartedAt,
|
||||
&i.ReadyAt,
|
||||
pq.Array(&i.Subsystems),
|
||||
pq.Array(&i.DisplayApps),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -6815,7 +6820,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.startup_script_behavior, workspace_agents.started_at, workspace_agents.ready_at, workspace_agents.subsystems
|
||||
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, workspace_agents.display_apps
|
||||
FROM
|
||||
workspace_agents
|
||||
JOIN
|
||||
@ -6877,6 +6882,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co
|
||||
&i.StartedAt,
|
||||
&i.ReadyAt,
|
||||
pq.Array(&i.Subsystems),
|
||||
pq.Array(&i.DisplayApps),
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -6914,10 +6920,11 @@ INSERT INTO
|
||||
startup_script_behavior,
|
||||
startup_script_timeout_seconds,
|
||||
shutdown_script,
|
||||
shutdown_script_timeout_seconds
|
||||
shutdown_script_timeout_seconds,
|
||||
display_apps
|
||||
)
|
||||
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, startup_script_behavior, started_at, ready_at, subsystems
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) 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, display_apps
|
||||
`
|
||||
|
||||
type InsertWorkspaceAgentParams struct {
|
||||
@ -6942,6 +6949,7 @@ type InsertWorkspaceAgentParams struct {
|
||||
StartupScriptTimeoutSeconds int32 `db:"startup_script_timeout_seconds" json:"startup_script_timeout_seconds"`
|
||||
ShutdownScript sql.NullString `db:"shutdown_script" json:"shutdown_script"`
|
||||
ShutdownScriptTimeoutSeconds int32 `db:"shutdown_script_timeout_seconds" json:"shutdown_script_timeout_seconds"`
|
||||
DisplayApps []DisplayApp `db:"display_apps" json:"display_apps"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspaceAgentParams) (WorkspaceAgent, error) {
|
||||
@ -6967,6 +6975,7 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
|
||||
arg.StartupScriptTimeoutSeconds,
|
||||
arg.ShutdownScript,
|
||||
arg.ShutdownScriptTimeoutSeconds,
|
||||
pq.Array(arg.DisplayApps),
|
||||
)
|
||||
var i WorkspaceAgent
|
||||
err := row.Scan(
|
||||
@ -7003,6 +7012,7 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
|
||||
&i.StartedAt,
|
||||
&i.ReadyAt,
|
||||
pq.Array(&i.Subsystems),
|
||||
pq.Array(&i.DisplayApps),
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
@ -50,10 +50,11 @@ INSERT INTO
|
||||
startup_script_behavior,
|
||||
startup_script_timeout_seconds,
|
||||
shutdown_script,
|
||||
shutdown_script_timeout_seconds
|
||||
shutdown_script_timeout_seconds,
|
||||
display_apps
|
||||
)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21) RETURNING *;
|
||||
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) RETURNING *;
|
||||
|
||||
-- name: UpdateWorkspaceAgentConnectionByID :exec
|
||||
UPDATE
|
||||
|
@ -71,6 +71,7 @@ overrides:
|
||||
eof: EOF
|
||||
template_ids: TemplateIDs
|
||||
active_user_ids: ActiveUserIDs
|
||||
display_app_ssh_helper: DisplayAppSSHHelper
|
||||
|
||||
sql:
|
||||
- schema: "./dump.sql"
|
||||
|
@ -1274,6 +1274,7 @@ func InsertWorkspaceResource(ctx context.Context, db database.Store, jobID uuid.
|
||||
Valid: prAgent.ShutdownScript != "",
|
||||
},
|
||||
ShutdownScriptTimeoutSeconds: prAgent.GetShutdownScriptTimeoutSeconds(),
|
||||
DisplayApps: convertDisplayApps(prAgent.GetDisplayApps()),
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("insert agent: %w", err)
|
||||
@ -1611,3 +1612,28 @@ func redactTemplateVariable(templateVariable *sdkproto.TemplateVariable) *sdkpro
|
||||
}
|
||||
return maybeRedacted
|
||||
}
|
||||
|
||||
func convertDisplayApps(apps *sdkproto.DisplayApps) []database.DisplayApp {
|
||||
// This shouldn't happen but let's avoid panicking. It also makes
|
||||
// writing tests a bit easier.
|
||||
if apps == nil {
|
||||
return nil
|
||||
}
|
||||
dapps := make([]database.DisplayApp, 0, 5)
|
||||
if apps.Vscode {
|
||||
dapps = append(dapps, database.DisplayAppVscode)
|
||||
}
|
||||
if apps.VscodeInsiders {
|
||||
dapps = append(dapps, database.DisplayAppVscodeInsiders)
|
||||
}
|
||||
if apps.SshHelper {
|
||||
dapps = append(dapps, database.DisplayAppSSHHelper)
|
||||
}
|
||||
if apps.PortForwardingHelper {
|
||||
dapps = append(dapps, database.DisplayAppPortForwardingHelper)
|
||||
}
|
||||
if apps.WebTerminal {
|
||||
dapps = append(dapps, database.DisplayAppWebTerminal)
|
||||
}
|
||||
return dapps
|
||||
}
|
||||
|
@ -1565,6 +1565,11 @@ func TestInsertWorkspaceResource(t *testing.T) {
|
||||
Slug: "a",
|
||||
}},
|
||||
ShutdownScript: "shutdown",
|
||||
DisplayApps: &sdkproto.DisplayApps{
|
||||
Vscode: true,
|
||||
PortForwardingHelper: true,
|
||||
SshHelper: true,
|
||||
},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@ -1587,6 +1592,63 @@ func TestInsertWorkspaceResource(t *testing.T) {
|
||||
got, err := agent.EnvironmentVariables.RawMessage.MarshalJSON()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, want, got)
|
||||
require.ElementsMatch(t, []database.DisplayApp{
|
||||
database.DisplayAppPortForwardingHelper,
|
||||
database.DisplayAppSSHHelper,
|
||||
database.DisplayAppVscode,
|
||||
}, agent.DisplayApps)
|
||||
})
|
||||
|
||||
t.Run("AllDisplayApps", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := dbfake.New()
|
||||
job := uuid.New()
|
||||
err := insert(db, job, &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
Agents: []*sdkproto.Agent{{
|
||||
DisplayApps: &sdkproto.DisplayApps{
|
||||
Vscode: true,
|
||||
VscodeInsiders: true,
|
||||
SshHelper: true,
|
||||
PortForwardingHelper: true,
|
||||
WebTerminal: true,
|
||||
},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resources, err := db.GetWorkspaceResourcesByJobID(ctx, job)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resources, 1)
|
||||
agents, err := db.GetWorkspaceAgentsByResourceIDs(ctx, []uuid.UUID{resources[0].ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, agents, 1)
|
||||
agent := agents[0]
|
||||
require.ElementsMatch(t, database.AllDisplayAppValues(), agent.DisplayApps)
|
||||
})
|
||||
|
||||
t.Run("DisableDefaultApps", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
db := dbfake.New()
|
||||
job := uuid.New()
|
||||
err := insert(db, job, &sdkproto.Resource{
|
||||
Name: "something",
|
||||
Type: "aws_instance",
|
||||
Agents: []*sdkproto.Agent{{
|
||||
DisplayApps: &sdkproto.DisplayApps{},
|
||||
}},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
resources, err := db.GetWorkspaceResourcesByJobID(ctx, job)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resources, 1)
|
||||
agents, err := db.GetWorkspaceAgentsByResourceIDs(ctx, []uuid.UUID{resources[0].ID})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, agents, 1)
|
||||
agent := agents[0]
|
||||
// An empty array (as opposed to nil) should be returned to indicate
|
||||
// that all apps are disabled.
|
||||
require.Equal(t, []database.DisplayApp{}, agent.DisplayApps)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1360,6 +1360,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
|
||||
ShutdownScript: dbAgent.ShutdownScript.String,
|
||||
ShutdownScriptTimeoutSeconds: dbAgent.ShutdownScriptTimeoutSeconds,
|
||||
Subsystems: subsystems,
|
||||
DisplayApps: convertDisplayApps(dbAgent.DisplayApps),
|
||||
}
|
||||
node := coordinator.Node(dbAgent.ID)
|
||||
if node != nil {
|
||||
@ -1421,6 +1422,18 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
|
||||
return workspaceAgent, nil
|
||||
}
|
||||
|
||||
func convertDisplayApps(apps []database.DisplayApp) []codersdk.DisplayApp {
|
||||
dapps := make([]codersdk.DisplayApp, 0, len(apps))
|
||||
for _, app := range apps {
|
||||
switch codersdk.DisplayApp(app) {
|
||||
case codersdk.DisplayAppVSCodeDesktop, codersdk.DisplayAppVSCodeInsiders, codersdk.DisplayAppPortForward, codersdk.DisplayAppWebTerminal, codersdk.DisplayAppSSH:
|
||||
dapps = append(dapps, codersdk.DisplayApp(app))
|
||||
}
|
||||
}
|
||||
|
||||
return dapps
|
||||
}
|
||||
|
||||
// @Summary Submit workspace agent stats
|
||||
// @ID submit-workspace-agent-stats
|
||||
// @Security CoderSessionToken
|
||||
|
@ -174,6 +174,97 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||
require.False(t, workspace.LatestBuild.Resources[0].Agents[0].Health.Healthy)
|
||||
require.NotEmpty(t, workspace.LatestBuild.Resources[0].Agents[0].Health.Reason)
|
||||
})
|
||||
|
||||
t.Run("DisplayApps", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := coderdtest.New(t, &coderdtest.Options{
|
||||
IncludeProvisionerDaemon: true,
|
||||
})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
authToken := uuid.NewString()
|
||||
tmpDir := t.TempDir()
|
||||
apps := &proto.DisplayApps{
|
||||
Vscode: true,
|
||||
VscodeInsiders: true,
|
||||
WebTerminal: true,
|
||||
PortForwardingHelper: true,
|
||||
SshHelper: true,
|
||||
}
|
||||
|
||||
echoResp := &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionPlan: echo.PlanComplete,
|
||||
ProvisionApply: []*proto.Response{{
|
||||
Type: &proto.Response_Apply{
|
||||
Apply: &proto.ApplyComplete{
|
||||
Resources: []*proto.Resource{
|
||||
{
|
||||
Name: "example",
|
||||
Type: "aws_instance",
|
||||
Agents: []*proto.Agent{
|
||||
{
|
||||
Id: uuid.NewString(),
|
||||
Directory: tmpDir,
|
||||
Auth: &proto.Agent_Token{
|
||||
Token: authToken,
|
||||
},
|
||||
DisplayApps: apps,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResp)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||
defer cancel()
|
||||
|
||||
workspace, err := client.Workspace(ctx, workspace.ID)
|
||||
require.NoError(t, err)
|
||||
agent, err := client.WorkspaceAgent(ctx, workspace.LatestBuild.Resources[0].Agents[0].ID)
|
||||
require.NoError(t, err)
|
||||
expectedApps := []codersdk.DisplayApp{
|
||||
codersdk.DisplayAppPortForward,
|
||||
codersdk.DisplayAppSSH,
|
||||
codersdk.DisplayAppVSCodeDesktop,
|
||||
codersdk.DisplayAppVSCodeInsiders,
|
||||
codersdk.DisplayAppWebTerminal,
|
||||
}
|
||||
require.ElementsMatch(t, expectedApps, agent.DisplayApps)
|
||||
|
||||
// Flips all the apps to false.
|
||||
apps.PortForwardingHelper = false
|
||||
apps.Vscode = false
|
||||
apps.VscodeInsiders = false
|
||||
apps.SshHelper = false
|
||||
apps.WebTerminal = false
|
||||
|
||||
version = coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, echoResp,
|
||||
func(req *codersdk.CreateTemplateVersionRequest) {
|
||||
req.TemplateID = template.ID
|
||||
})
|
||||
|
||||
err = client.UpdateActiveTemplateVersion(ctx, template.ID, codersdk.UpdateActiveTemplateVersion{
|
||||
ID: version.ID,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||
// Creating another workspace is just easier.
|
||||
workspace = coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||
build := coderdtest.AwaitWorkspaceBuildJob(t, client, workspace.LatestBuild.ID)
|
||||
require.NoError(t, err)
|
||||
agent, err = client.WorkspaceAgent(ctx, build.Resources[0].Agents[0].ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, agent.DisplayApps, 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user