feat: add telemetry support for workspace agent subsystem (#7579)

This commit is contained in:
Jon Ayers
2023-05-17 22:49:25 -05:00
committed by GitHub
parent 52bb84a26f
commit 00a2413c03
26 changed files with 366 additions and 27 deletions

15
coderd/apidoc/docs.go generated
View File

@ -5966,6 +5966,9 @@ const docTemplate = `{
"expanded_directory": {
"type": "string"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
},
"version": {
"type": "string"
}
@ -6406,6 +6409,15 @@ const docTemplate = `{
}
}
},
"codersdk.AgentSubsystem": {
"type": "string",
"enum": [
"envbox"
],
"x-enum-varnames": [
"AgentSubsystemEnvbox"
]
},
"codersdk.AppHostResponse": {
"type": "object",
"properties": {
@ -9627,6 +9639,9 @@ const docTemplate = `{
"status": {
"$ref": "#/definitions/codersdk.WorkspaceAgentStatus"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
},
"troubleshooting_url": {
"type": "string"
},

View File

@ -5258,6 +5258,9 @@
"expanded_directory": {
"type": "string"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
},
"version": {
"type": "string"
}
@ -5676,6 +5679,11 @@
}
}
},
"codersdk.AgentSubsystem": {
"type": "string",
"enum": ["envbox"],
"x-enum-varnames": ["AgentSubsystemEnvbox"]
},
"codersdk.AppHostResponse": {
"type": "object",
"properties": {
@ -8665,6 +8673,9 @@
"status": {
"$ref": "#/definitions/codersdk.WorkspaceAgentStatus"
},
"subsystem": {
"$ref": "#/definitions/codersdk.AgentSubsystem"
},
"troubleshooting_url": {
"type": "string"
},

View File

@ -1029,7 +1029,8 @@ 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,
ID: agt.ID,
Subsystem: database.WorkspaceAgentSubsystemNone,
}).Asserts(ws, rbac.ActionUpdate).Returns()
}))
s.Run("GetWorkspaceAgentStartupLogsAfter", s.Subtest(func(db database.Store, check *expects) {

View File

@ -3702,6 +3702,7 @@ func (q *fakeQuerier) UpdateWorkspaceAgentStartupByID(_ context.Context, arg dat
agent.Version = arg.Version
agent.ExpandedDirectory = arg.ExpandedDirectory
agent.Subsystem = arg.Subsystem
q.workspaceAgents[index] = agent
return nil
}

View File

@ -116,6 +116,12 @@ CREATE TYPE workspace_agent_lifecycle_state AS ENUM (
'off'
);
CREATE TYPE workspace_agent_subsystem AS ENUM (
'envbuilder',
'envbox',
'none'
);
CREATE TYPE workspace_app_health AS ENUM (
'disabled',
'initializing',
@ -597,6 +603,7 @@ CREATE TABLE workspace_agents (
shutdown_script_timeout_seconds integer DEFAULT 0 NOT NULL,
startup_logs_length integer DEFAULT 0 NOT NULL,
startup_logs_overflowed boolean DEFAULT false NOT NULL,
subsystem workspace_agent_subsystem DEFAULT 'none'::workspace_agent_subsystem NOT NULL,
CONSTRAINT max_startup_logs_length CHECK ((startup_logs_length <= 1048576))
);

View File

@ -0,0 +1,4 @@
BEGIN;
ALTER TABLE workspace_agents DROP COLUMN subsystem;
DROP TYPE workspace_agent_subsystem;
COMMIT;

View File

@ -0,0 +1,4 @@
BEGIN;
CREATE TYPE workspace_agent_subsystem AS ENUM ('envbuilder', 'envbox', 'none');
ALTER TABLE workspace_agents ADD COLUMN subsystem workspace_agent_subsystem NOT NULL default 'none';
COMMIT;

View File

@ -1096,6 +1096,67 @@ func AllWorkspaceAgentLifecycleStateValues() []WorkspaceAgentLifecycleState {
}
}
type WorkspaceAgentSubsystem string
const (
WorkspaceAgentSubsystemEnvbuilder WorkspaceAgentSubsystem = "envbuilder"
WorkspaceAgentSubsystemEnvbox WorkspaceAgentSubsystem = "envbox"
WorkspaceAgentSubsystemNone WorkspaceAgentSubsystem = "none"
)
func (e *WorkspaceAgentSubsystem) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = WorkspaceAgentSubsystem(s)
case string:
*e = WorkspaceAgentSubsystem(s)
default:
return fmt.Errorf("unsupported scan type for WorkspaceAgentSubsystem: %T", src)
}
return nil
}
type NullWorkspaceAgentSubsystem struct {
WorkspaceAgentSubsystem WorkspaceAgentSubsystem
Valid bool // Valid is true if WorkspaceAgentSubsystem is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullWorkspaceAgentSubsystem) Scan(value interface{}) error {
if value == nil {
ns.WorkspaceAgentSubsystem, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.WorkspaceAgentSubsystem.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullWorkspaceAgentSubsystem) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.WorkspaceAgentSubsystem), nil
}
func (e WorkspaceAgentSubsystem) Valid() bool {
switch e {
case WorkspaceAgentSubsystemEnvbuilder,
WorkspaceAgentSubsystemEnvbox,
WorkspaceAgentSubsystemNone:
return true
}
return false
}
func AllWorkspaceAgentSubsystemValues() []WorkspaceAgentSubsystem {
return []WorkspaceAgentSubsystem{
WorkspaceAgentSubsystemEnvbuilder,
WorkspaceAgentSubsystemEnvbox,
WorkspaceAgentSubsystemNone,
}
}
type WorkspaceAppHealth string
const (
@ -1587,7 +1648,8 @@ type WorkspaceAgent struct {
// Total length of startup logs
StartupLogsLength int32 `db:"startup_logs_length" json:"startup_logs_length"`
// Whether the startup logs overflowed in length
StartupLogsOverflowed bool `db:"startup_logs_overflowed" json:"startup_logs_overflowed"`
StartupLogsOverflowed bool `db:"startup_logs_overflowed" json:"startup_logs_overflowed"`
Subsystem WorkspaceAgentSubsystem `db:"subsystem" json:"subsystem"`
}
type WorkspaceAgentMetadatum struct {

View File

@ -5553,7 +5553,7 @@ func (q *sqlQuerier) DeleteOldWorkspaceAgentStartupLogs(ctx context.Context) err
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem
FROM
workspace_agents
WHERE
@ -5596,13 +5596,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByAuthToken(ctx context.Context, authToken
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
)
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem
FROM
workspace_agents
WHERE
@ -5643,13 +5644,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
)
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem
FROM
workspace_agents
WHERE
@ -5692,6 +5694,7 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
)
return i, err
}
@ -5786,7 +5789,7 @@ func (q *sqlQuerier) GetWorkspaceAgentStartupLogsAfter(ctx context.Context, arg
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed
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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem
FROM
workspace_agents
WHERE
@ -5833,6 +5836,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
); err != nil {
return nil, err
}
@ -5848,7 +5852,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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed 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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem FROM workspace_agents WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) {
@ -5891,6 +5895,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
); err != nil {
return nil, err
}
@ -5907,7 +5912,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.login_before_ready, workspace_agents.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.startup_logs_length, workspace_agents.startup_logs_overflowed
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.login_before_ready, workspace_agents.startup_script_timeout_seconds, workspace_agents.expanded_directory, workspace_agents.shutdown_script, workspace_agents.shutdown_script_timeout_seconds, workspace_agents.startup_logs_length, workspace_agents.startup_logs_overflowed, workspace_agents.subsystem
FROM
workspace_agents
JOIN
@ -5966,6 +5971,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Co
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
); err != nil {
return nil, err
}
@ -6006,7 +6012,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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed
($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, login_before_ready, startup_script_timeout_seconds, expanded_directory, shutdown_script, shutdown_script_timeout_seconds, startup_logs_length, startup_logs_overflowed, subsystem
`
type InsertWorkspaceAgentParams struct {
@ -6089,6 +6095,7 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
&i.ShutdownScriptTimeoutSeconds,
&i.StartupLogsLength,
&i.StartupLogsOverflowed,
&i.Subsystem,
)
return i, err
}
@ -6275,19 +6282,26 @@ UPDATE
workspace_agents
SET
version = $2,
expanded_directory = $3
expanded_directory = $3,
subsystem = $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"`
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"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentStartupByID, arg.ID, arg.Version, arg.ExpandedDirectory)
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentStartupByID,
arg.ID,
arg.Version,
arg.ExpandedDirectory,
arg.Subsystem,
)
return err
}

View File

@ -82,7 +82,8 @@ UPDATE
workspace_agents
SET
version = $2,
expanded_directory = $3
expanded_directory = $3,
subsystem = $4
WHERE
id = $1;

View File

@ -562,6 +562,7 @@ func ConvertWorkspaceAgent(agent database.WorkspaceAgent) WorkspaceAgent {
Directory: agent.Directory != "",
ConnectionTimeoutSeconds: agent.ConnectionTimeoutSeconds,
ShutdownScript: agent.ShutdownScript.Valid,
Subsystem: string(agent.Subsystem),
}
if agent.FirstConnectedAt.Valid {
snapAgent.FirstConnectedAt = &agent.FirstConnectedAt.Time
@ -783,6 +784,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"`
}
type WorkspaceAgentStat struct {

View File

@ -1,7 +1,6 @@
package telemetry_test
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
@ -22,6 +21,7 @@ import (
"github.com/coder/coder/coderd/database/dbfake"
"github.com/coder/coder/coderd/database/dbgen"
"github.com/coder/coder/coderd/telemetry"
"github.com/coder/coder/testutil"
)
func TestMain(m *testing.M) {
@ -37,7 +37,7 @@ func TestTelemetry(t *testing.T) {
db := dbfake.New()
ctx := context.Background()
ctx := testutil.Context(t, testutil.WaitMedium)
_, _ = dbgen.APIKey(t, db, database.APIKey{})
_ = dbgen.ParameterSchema(t, db, database.ParameterSchema{
DefaultSourceScheme: database.ParameterSourceSchemeNone,
@ -59,7 +59,18 @@ func TestTelemetry(t *testing.T) {
SharingLevel: database.AppSharingLevelOwner,
Health: database.WorkspaceAppHealthDisabled,
})
_ = dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{})
wsagent := dbgen.WorkspaceAgent(t, db, database.WorkspaceAgent{
Subsystem: database.WorkspaceAgentSubsystemEnvbox,
})
// 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,
})
require.NoError(t, err)
_ = dbgen.WorkspaceBuild(t, db, database.WorkspaceBuild{
Transition: database.WorkspaceTransitionStart,
Reason: database.BuildReasonAutostart,
@ -88,6 +99,9 @@ func TestTelemetry(t *testing.T) {
require.Len(t, snapshot.WorkspaceBuilds, 1)
require.Len(t, snapshot.WorkspaceResources, 1)
require.Len(t, snapshot.WorkspaceAgentStats, 1)
wsa := snapshot.WorkspaceAgents[0]
require.Equal(t, string(database.WorkspaceAgentSubsystemEnvbox), wsa.Subsystem)
})
t.Run("HashedEmail", func(t *testing.T) {
t.Parallel()

View File

@ -220,6 +220,7 @@ func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Reques
ID: apiAgent.ID,
Version: req.Version,
ExpandedDirectory: req.ExpandedDirectory,
Subsystem: convertWorkspaceAgentSubsystem(req.Subsystem),
}); err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Error setting agent version",
@ -1129,6 +1130,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
StartupScriptTimeoutSeconds: dbAgent.StartupScriptTimeoutSeconds,
ShutdownScript: dbAgent.ShutdownScript.String,
ShutdownScriptTimeoutSeconds: dbAgent.ShutdownScriptTimeoutSeconds,
Subsystem: codersdk.AgentSubsystem(dbAgent.Subsystem),
}
node := coordinator.Node(dbAgent.ID)
if node != nil {
@ -1983,3 +1985,12 @@ func convertWorkspaceAgentStartupLog(logEntry database.WorkspaceAgentStartupLog)
Level: codersdk.LogLevel(logEntry.Level),
}
}
func convertWorkspaceAgentSubsystem(ss codersdk.AgentSubsystem) database.WorkspaceAgentSubsystem {
switch ss {
case codersdk.AgentSubsystemEnvbox:
return database.WorkspaceAgentSubsystemEnvbox
default:
return database.WorkspaceAgentSubsystemNone
}
}

View File

@ -1326,3 +1326,85 @@ func TestWorkspaceAgent_Metadata(t *testing.T) {
err = agentClient.PostMetadata(ctx, "unknown", unknownKeyMetadata)
require.NoError(t, err)
}
func TestWorkspaceAgent_Startup(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
})
user := coderdtest.CreateFirstUser(t, client)
authToken := uuid.NewString()
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionPlan: echo.ProvisionComplete,
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
})
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)
agentClient := agentsdk.New(client.URL)
agentClient.SetSessionToken(authToken)
ctx := testutil.Context(t, testutil.WaitMedium)
const (
expectedVersion = "v1.2.3"
expectedDir = "/home/coder"
expectedSubsystem = codersdk.AgentSubsystemEnvbox
)
err := agentClient.PostStartup(ctx, agentsdk.PostStartupRequest{
Version: expectedVersion,
ExpandedDirectory: expectedDir,
Subsystem: expectedSubsystem,
})
require.NoError(t, err)
workspace, err = client.Workspace(ctx, workspace.ID)
require.NoError(t, err)
wsagent, err := client.WorkspaceAgent(ctx, workspace.LatestBuild.Resources[0].Agents[0].ID)
require.NoError(t, err)
require.Equal(t, expectedVersion, wsagent.Version)
require.Equal(t, expectedDir, wsagent.ExpandedDirectory)
require.Equal(t, expectedSubsystem, wsagent.Subsystem)
})
t.Run("InvalidSemver", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true,
})
user := coderdtest.CreateFirstUser(t, client)
authToken := uuid.NewString()
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
Parse: echo.ParseComplete,
ProvisionPlan: echo.ProvisionComplete,
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
})
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)
agentClient := agentsdk.New(client.URL)
agentClient.SetSessionToken(authToken)
ctx := testutil.Context(t, testutil.WaitMedium)
err := agentClient.PostStartup(ctx, agentsdk.PostStartupRequest{
Version: "1.2.3",
})
require.Error(t, err)
cerr, ok := codersdk.AsError(err)
require.True(t, ok)
require.Equal(t, http.StatusBadRequest, cerr.StatusCode())
})
}