feat: allow configuring display apps from template (#9100)

This commit is contained in:
Jon Ayers
2023-08-30 14:53:42 -05:00
committed by GitHub
parent 9c9d035354
commit ee24260614
68 changed files with 1975 additions and 564 deletions

23
coderd/apidoc/docs.go generated
View File

@ -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": {

View File

@ -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": {

View File

@ -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)

View File

@ -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))))
);

View File

@ -0,0 +1,5 @@
BEGIN;
ALTER TABLE workspace_agents DROP COLUMN display_apps;
DROP TYPE display_app;
COMMIT;

View File

@ -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;

View File

@ -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 {

View File

@ -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
}

View File

@ -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

View File

@ -71,6 +71,7 @@ overrides:
eof: EOF
template_ids: TemplateIDs
active_user_ids: ActiveUserIDs
display_app_ssh_helper: DisplayAppSSHHelper
sql:
- schema: "./dump.sql"

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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

View File

@ -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) {