feat: add `expanded_directory` to the agent for extension support (#6087)

This will enable opening the default `dir` of an agent in
the VS Code extension!
This commit is contained in:
Kyle Carberry 2023-02-07 15:35:09 -06:00 committed by GitHub
parent f6effdb63e
commit 691495d761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 220 additions and 59 deletions

View File

@ -75,7 +75,7 @@ type Client interface {
ReportStats(ctx context.Context, log slog.Logger, stats func() *agentsdk.Stats) (io.Closer, error)
PostLifecycle(ctx context.Context, state agentsdk.PostLifecycleRequest) error
PostAppHealth(ctx context.Context, req agentsdk.PostAppHealthsRequest) error
PostVersion(ctx context.Context, version string) error
PostStartup(ctx context.Context, req agentsdk.PostStartupRequest) error
}
func New(options Options) io.Closer {
@ -236,16 +236,29 @@ func (a *agent) run(ctx context.Context) error {
}
a.sessionToken.Store(&sessionToken)
err = a.client.PostVersion(ctx, buildinfo.Version())
if err != nil {
return xerrors.Errorf("update workspace agent version: %w", err)
}
metadata, err := a.client.Metadata(ctx)
if err != nil {
return xerrors.Errorf("fetch metadata: %w", err)
}
a.logger.Info(ctx, "fetched metadata")
// Expand the directory and send it back to coderd so external
// applications that rely on the directory can use it.
//
// An example is VS Code Remote, which must know the directory
// before initializing a connection.
metadata.Directory, err = expandDirectory(metadata.Directory)
if err != nil {
return xerrors.Errorf("expand directory: %w", err)
}
err = a.client.PostStartup(ctx, agentsdk.PostStartupRequest{
Version: buildinfo.Version(),
ExpandedDirectory: metadata.Directory,
})
if err != nil {
return xerrors.Errorf("update workspace agent version: %w", err)
}
oldMetadata := a.metadata.Swap(metadata)
// The startup script should only execute on the first run!
@ -1318,3 +1331,20 @@ func userHomeDir() (string, error) {
}
return u.HomeDir, nil
}
// expandDirectory converts a directory path to an absolute path.
// It primarily resolves the home directory and any environment
// variables that may be set
func expandDirectory(dir string) (string, error) {
if dir == "" {
return "", nil
}
if dir[0] == '~' {
home, err := userHomeDir()
if err != nil {
return "", err
}
dir = filepath.Join(home, dir[1:])
}
return os.ExpandEnv(dir), nil
}

View File

@ -787,6 +787,56 @@ func TestAgent_Lifecycle(t *testing.T) {
})
}
func TestAgent_Startup(t *testing.T) {
t.Parallel()
t.Run("EmptyDirectory", func(t *testing.T) {
t.Parallel()
_, client, _, _ := setupAgent(t, agentsdk.Metadata{
StartupScript: "true",
StartupScriptTimeout: 30 * time.Second,
Directory: "",
}, 0)
assert.Eventually(t, func() bool {
return client.getStartup().Version != ""
}, testutil.WaitShort, testutil.IntervalFast)
require.Equal(t, "", client.getStartup().ExpandedDirectory)
})
t.Run("HomeDirectory", func(t *testing.T) {
t.Parallel()
_, client, _, _ := setupAgent(t, agentsdk.Metadata{
StartupScript: "true",
StartupScriptTimeout: 30 * time.Second,
Directory: "~",
}, 0)
assert.Eventually(t, func() bool {
return client.getStartup().Version != ""
}, testutil.WaitShort, testutil.IntervalFast)
homeDir, err := os.UserHomeDir()
require.NoError(t, err)
require.Equal(t, homeDir, client.getStartup().ExpandedDirectory)
})
t.Run("HomeEnvironmentVariable", func(t *testing.T) {
t.Parallel()
_, client, _, _ := setupAgent(t, agentsdk.Metadata{
StartupScript: "true",
StartupScriptTimeout: 30 * time.Second,
Directory: "$HOME",
}, 0)
assert.Eventually(t, func() bool {
return client.getStartup().Version != ""
}, testutil.WaitShort, testutil.IntervalFast)
homeDir, err := os.UserHomeDir()
require.NoError(t, err)
require.Equal(t, homeDir, client.getStartup().ExpandedDirectory)
})
}
func TestAgent_ReconnectingPTY(t *testing.T) {
t.Parallel()
if runtime.GOOS == "windows" {
@ -1178,6 +1228,7 @@ type client struct {
mu sync.Mutex // Protects following.
lifecycleStates []codersdk.WorkspaceAgentLifecycle
startup agentsdk.PostStartupRequest
}
func (c *client) Metadata(_ context.Context) (agentsdk.Metadata, error) {
@ -1250,7 +1301,16 @@ func (*client) PostAppHealth(_ context.Context, _ agentsdk.PostAppHealthsRequest
return nil
}
func (*client) PostVersion(_ context.Context, _ string) error {
func (c *client) getStartup() agentsdk.PostStartupRequest {
c.mu.Lock()
defer c.mu.Unlock()
return c.startup
}
func (c *client) PostStartup(_ context.Context, startup agentsdk.PostStartupRequest) error {
c.mu.Lock()
defer c.mu.Unlock()
c.startup = startup
return nil
}

18
coderd/apidoc/docs.go generated
View File

@ -4104,7 +4104,7 @@ const docTemplate = `{
}
}
},
"/workspaceagents/me/version": {
"/workspaceagents/me/startup": {
"post": {
"security": [
{
@ -4120,16 +4120,16 @@ const docTemplate = `{
"tags": [
"Agents"
],
"summary": "Submit workspace agent version",
"operationId": "submit-workspace-agent-version",
"summary": "Submit workspace agent startup",
"operationId": "submit-workspace-agent-startup",
"parameters": [
{
"description": "Version request",
"description": "Startup request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/agentsdk.PostVersionRequest"
"$ref": "#/definitions/agentsdk.PostStartupRequest"
}
}
],
@ -5099,9 +5099,12 @@ const docTemplate = `{
}
}
},
"agentsdk.PostVersionRequest": {
"agentsdk.PostStartupRequest": {
"type": "object",
"properties": {
"expanded_directory": {
"type": "string"
},
"version": {
"type": "string"
}
@ -7945,6 +7948,9 @@ const docTemplate = `{
"type": "string"
}
},
"expanded_directory": {
"type": "string"
},
"first_connected_at": {
"type": "string",
"format": "date-time"

View File

@ -3606,7 +3606,7 @@
}
}
},
"/workspaceagents/me/version": {
"/workspaceagents/me/startup": {
"post": {
"security": [
{
@ -3616,16 +3616,16 @@
"consumes": ["application/json"],
"produces": ["application/json"],
"tags": ["Agents"],
"summary": "Submit workspace agent version",
"operationId": "submit-workspace-agent-version",
"summary": "Submit workspace agent startup",
"operationId": "submit-workspace-agent-startup",
"parameters": [
{
"description": "Version request",
"description": "Startup request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/agentsdk.PostVersionRequest"
"$ref": "#/definitions/agentsdk.PostStartupRequest"
}
}
],
@ -4502,9 +4502,12 @@
}
}
},
"agentsdk.PostVersionRequest": {
"agentsdk.PostStartupRequest": {
"type": "object",
"properties": {
"expanded_directory": {
"type": "string"
},
"version": {
"type": "string"
}
@ -7150,6 +7153,9 @@
"type": "string"
}
},
"expanded_directory": {
"type": "string"
},
"first_connected_at": {
"type": "string",
"format": "date-time"

View File

@ -553,7 +553,7 @@ func New(options *Options) *API {
r.Route("/me", func(r chi.Router) {
r.Use(httpmw.ExtractWorkspaceAgent(options.Database))
r.Get("/metadata", api.workspaceAgentMetadata)
r.Post("/version", api.postWorkspaceAgentVersion)
r.Post("/startup", api.postWorkspaceAgentStartup)
r.Post("/app-health", api.postWorkspaceAppHealth)
r.Get("/gitauth", api.workspaceAgentsGitAuth)
r.Get("/gitsshkey", api.agentGitSSHKey)

View File

@ -73,7 +73,7 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
"GET:/api/v2/workspaceagents/me/gitsshkey": {NoAuthorize: true},
"GET:/api/v2/workspaceagents/me/metadata": {NoAuthorize: true},
"GET:/api/v2/workspaceagents/me/coordinate": {NoAuthorize: true},
"POST:/api/v2/workspaceagents/me/version": {NoAuthorize: true},
"POST:/api/v2/workspaceagents/me/startup": {NoAuthorize: true},
"POST:/api/v2/workspaceagents/me/app-health": {NoAuthorize: true},
"POST:/api/v2/workspaceagents/me/report-stats": {NoAuthorize: true},
"POST:/api/v2/workspaceagents/me/report-lifecycle": {NoAuthorize: true},

View File

@ -344,7 +344,7 @@ func assertProduce(t *testing.T, comment SwaggerComment) {
assert.Contains(t, allowedProduceTypes, comment.produce, "@Produce value is limited to specific types: %s", strings.Join(allowedProduceTypes, ","))
} else {
if (comment.router == "/workspaceagents/me/app-health" && comment.method == "post") ||
(comment.router == "/workspaceagents/me/version" && comment.method == "post") ||
(comment.router == "/workspaceagents/me/startup" && comment.method == "post") ||
(comment.router == "/licenses/{id}" && comment.method == "delete") ||
(comment.router == "/debug/coordinator" && comment.method == "get") {
return // Exception: HTTP 200 is returned without response entity

View File

@ -3192,7 +3192,7 @@ func (q *fakeQuerier) UpdateWorkspaceAgentConnectionByID(_ context.Context, arg
return sql.ErrNoRows
}
func (q *fakeQuerier) UpdateWorkspaceAgentVersionByID(_ context.Context, arg database.UpdateWorkspaceAgentVersionByIDParams) error {
func (q *fakeQuerier) UpdateWorkspaceAgentStartupByID(_ context.Context, arg database.UpdateWorkspaceAgentStartupByIDParams) error {
if err := validateDatabaseType(arg); err != nil {
return err
}
@ -3206,6 +3206,7 @@ func (q *fakeQuerier) UpdateWorkspaceAgentVersionByID(_ context.Context, arg dat
}
agent.Version = arg.Version
agent.ExpandedDirectory = arg.ExpandedDirectory
q.workspaceAgents[index] = agent
return nil
}

View File

@ -468,7 +468,8 @@ CREATE TABLE workspace_agents (
motd_file text DEFAULT ''::text NOT NULL,
lifecycle_state workspace_agent_lifecycle_state DEFAULT 'created'::workspace_agent_lifecycle_state NOT NULL,
login_before_ready boolean DEFAULT true NOT NULL,
startup_script_timeout_seconds integer DEFAULT 0 NOT NULL
startup_script_timeout_seconds integer DEFAULT 0 NOT NULL,
expanded_directory character varying(4096) DEFAULT ''::character varying NOT NULL
);
COMMENT ON COLUMN workspace_agents.version IS 'Version tracks the version of the currently running workspace agent. Workspace agents register their version upon start.';
@ -485,6 +486,8 @@ COMMENT ON COLUMN workspace_agents.login_before_ready IS 'If true, the agent wil
COMMENT ON COLUMN workspace_agents.startup_script_timeout_seconds IS 'The number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout.';
COMMENT ON COLUMN workspace_agents.expanded_directory IS 'The resolved path of a user-specified directory. e.g. ~/coder -> /home/coder/coder';
CREATE TABLE workspace_apps (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,

View File

@ -0,0 +1,6 @@
BEGIN;
ALTER TABLE ONLY workspace_agents
DROP COLUMN IF EXISTS expanded_directory;
COMMIT;

View File

@ -0,0 +1,9 @@
BEGIN;
ALTER TABLE ONLY workspace_agents
ADD COLUMN IF NOT EXISTS expanded_directory varchar(4096) DEFAULT '' NOT NULL;
COMMENT ON COLUMN workspace_agents.expanded_directory
IS 'The resolved path of a user-specified directory. e.g. ~/coder -> /home/coder/coder';
COMMIT;

View File

@ -1531,6 +1531,8 @@ type WorkspaceAgent struct {
LoginBeforeReady bool `db:"login_before_ready" json:"login_before_ready"`
// The number of seconds to wait for the startup script to complete. If the script does not complete within this time, the agent lifecycle will be marked as start_timeout.
StartupScriptTimeoutSeconds int32 `db:"startup_script_timeout_seconds" json:"startup_script_timeout_seconds"`
// The resolved path of a user-specified directory. e.g. ~/coder -> /home/coder/coder
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
}
type WorkspaceApp struct {

View File

@ -196,7 +196,7 @@ type sqlcQuerier interface {
UpdateWorkspace(ctx context.Context, arg UpdateWorkspaceParams) (Workspace, error)
UpdateWorkspaceAgentConnectionByID(ctx context.Context, arg UpdateWorkspaceAgentConnectionByIDParams) error
UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, arg UpdateWorkspaceAgentLifecycleStateByIDParams) error
UpdateWorkspaceAgentVersionByID(ctx context.Context, arg UpdateWorkspaceAgentVersionByIDParams) error
UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error
UpdateWorkspaceAppHealthByID(ctx context.Context, arg UpdateWorkspaceAppHealthByIDParams) error
UpdateWorkspaceAutostart(ctx context.Context, arg UpdateWorkspaceAutostartParams) error
UpdateWorkspaceBuildByID(ctx context.Context, arg UpdateWorkspaceBuildByIDParams) (WorkspaceBuild, error)

View File

@ -4825,7 +4825,7 @@ func (q *sqlQuerier) UpdateUserStatus(ctx context.Context, arg UpdateUserStatusP
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
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
FROM
workspace_agents
WHERE
@ -4863,13 +4863,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByAuthToken(ctx context.Context, authToken
&i.LifecycleState,
&i.LoginBeforeReady,
&i.StartupScriptTimeoutSeconds,
&i.ExpandedDirectory,
)
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
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
FROM
workspace_agents
WHERE
@ -4905,13 +4906,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByID(ctx context.Context, id uuid.UUID) (W
&i.LifecycleState,
&i.LoginBeforeReady,
&i.StartupScriptTimeoutSeconds,
&i.ExpandedDirectory,
)
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
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
FROM
workspace_agents
WHERE
@ -4949,13 +4951,14 @@ func (q *sqlQuerier) GetWorkspaceAgentByInstanceID(ctx context.Context, authInst
&i.LifecycleState,
&i.LoginBeforeReady,
&i.StartupScriptTimeoutSeconds,
&i.ExpandedDirectory,
)
return i, err
}
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
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
FROM
workspace_agents
WHERE
@ -4997,6 +5000,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsByResourceIDs(ctx context.Context, ids []
&i.LifecycleState,
&i.LoginBeforeReady,
&i.StartupScriptTimeoutSeconds,
&i.ExpandedDirectory,
); err != nil {
return nil, err
}
@ -5012,7 +5016,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 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 FROM workspace_agents WHERE created_at > $1
`
func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error) {
@ -5050,6 +5054,7 @@ func (q *sqlQuerier) GetWorkspaceAgentsCreatedAfter(ctx context.Context, created
&i.LifecycleState,
&i.LoginBeforeReady,
&i.StartupScriptTimeoutSeconds,
&i.ExpandedDirectory,
); err != nil {
return nil, err
}
@ -5088,7 +5093,7 @@ INSERT INTO
startup_script_timeout_seconds
)
VALUES
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) 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
($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19) 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
`
type InsertWorkspaceAgentParams struct {
@ -5162,6 +5167,7 @@ func (q *sqlQuerier) InsertWorkspaceAgent(ctx context.Context, arg InsertWorkspa
&i.LifecycleState,
&i.LoginBeforeReady,
&i.StartupScriptTimeoutSeconds,
&i.ExpandedDirectory,
)
return i, err
}
@ -5219,22 +5225,24 @@ func (q *sqlQuerier) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context,
return err
}
const updateWorkspaceAgentVersionByID = `-- name: UpdateWorkspaceAgentVersionByID :exec
const updateWorkspaceAgentStartupByID = `-- name: UpdateWorkspaceAgentStartupByID :exec
UPDATE
workspace_agents
SET
version = $2
version = $2,
expanded_directory = $3
WHERE
id = $1
`
type UpdateWorkspaceAgentVersionByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Version string `db:"version" json:"version"`
type UpdateWorkspaceAgentStartupByIDParams struct {
ID uuid.UUID `db:"id" json:"id"`
Version string `db:"version" json:"version"`
ExpandedDirectory string `db:"expanded_directory" json:"expanded_directory"`
}
func (q *sqlQuerier) UpdateWorkspaceAgentVersionByID(ctx context.Context, arg UpdateWorkspaceAgentVersionByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentVersionByID, arg.ID, arg.Version)
func (q *sqlQuerier) UpdateWorkspaceAgentStartupByID(ctx context.Context, arg UpdateWorkspaceAgentStartupByIDParams) error {
_, err := q.db.ExecContext(ctx, updateWorkspaceAgentStartupByID, arg.ID, arg.Version, arg.ExpandedDirectory)
return err
}

View File

@ -75,11 +75,12 @@ SET
WHERE
id = $1;
-- name: UpdateWorkspaceAgentVersionByID :exec
-- name: UpdateWorkspaceAgentStartupByID :exec
UPDATE
workspace_agents
SET
version = $2
version = $2,
expanded_directory = $3
WHERE
id = $1;

View File

@ -155,17 +155,17 @@ func (api *API) workspaceAgentMetadata(rw http.ResponseWriter, r *http.Request)
})
}
// @Summary Submit workspace agent version
// @ID submit-workspace-agent-version
// @Summary Submit workspace agent startup
// @ID submit-workspace-agent-startup
// @Security CoderSessionToken
// @Accept json
// @Produce json
// @Tags Agents
// @Param request body agentsdk.PostVersionRequest true "Version request"
// @Param request body agentsdk.PostStartupRequest true "Startup request"
// @Success 200
// @Router /workspaceagents/me/version [post]
// @Router /workspaceagents/me/startup [post]
// @x-apidocgen {"skip": true}
func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Request) {
func (api *API) postWorkspaceAgentStartup(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
workspaceAgent := httpmw.WorkspaceAgent(r)
apiAgent, err := convertWorkspaceAgent(api.DERPMap, *api.TailnetCoordinator.Load(), workspaceAgent, nil, api.AgentInactiveDisconnectTimeout, api.DeploymentConfig.AgentFallbackTroubleshootingURL.Value)
@ -177,7 +177,7 @@ func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Reques
return
}
var req agentsdk.PostVersionRequest
var req agentsdk.PostStartupRequest
if !httpapi.Read(ctx, rw, r, &req) {
return
}
@ -192,9 +192,10 @@ func (api *API) postWorkspaceAgentVersion(rw http.ResponseWriter, r *http.Reques
return
}
if err := api.Database.UpdateWorkspaceAgentVersionByID(ctx, database.UpdateWorkspaceAgentVersionByIDParams{
ID: apiAgent.ID,
Version: req.Version,
if err := api.Database.UpdateWorkspaceAgentStartupByID(ctx, database.UpdateWorkspaceAgentStartupByIDParams{
ID: apiAgent.ID,
Version: req.Version,
ExpandedDirectory: req.ExpandedDirectory,
}); err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Error setting agent version",
@ -782,6 +783,7 @@ func convertWorkspaceAgent(derpMap *tailcfg.DERPMap, coordinator tailnet.Coordin
Version: dbAgent.Version,
EnvironmentVariables: envs,
Directory: dbAgent.Directory,
ExpandedDirectory: dbAgent.ExpandedDirectory,
Apps: apps,
ConnectionTimeoutSeconds: dbAgent.ConnectionTimeoutSeconds,
TroubleshootingURL: troubleshootingURL,

View File

@ -226,6 +226,6 @@ func (*client) PostAppHealth(_ context.Context, _ agentsdk.PostAppHealthsRequest
return nil
}
func (*client) PostVersion(_ context.Context, _ string) error {
func (*client) PostStartup(_ context.Context, _ agentsdk.PostStartupRequest) error {
return nil
}

View File

@ -469,13 +469,13 @@ func (c *Client) PostLifecycle(ctx context.Context, req PostLifecycleRequest) er
return nil
}
type PostVersionRequest struct {
Version string `json:"version"`
type PostStartupRequest struct {
Version string `json:"version"`
ExpandedDirectory string `json:"expanded_directory"`
}
func (c *Client) PostVersion(ctx context.Context, version string) error {
versionReq := PostVersionRequest{Version: version}
res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/version", versionReq)
func (c *Client) PostStartup(ctx context.Context, req PostStartupRequest) error {
res, err := c.SDK.Request(ctx, http.MethodPost, "/api/v2/workspaceagents/me/startup", req)
if err != nil {
return err
}

View File

@ -68,6 +68,7 @@ type WorkspaceAgent struct {
OperatingSystem string `json:"operating_system"`
StartupScript string `json:"startup_script,omitempty"`
Directory string `json:"directory,omitempty"`
ExpandedDirectory string `json:"expanded_directory,omitempty"`
Version string `json:"version"`
Apps []WorkspaceApp `json:"apps"`
// DERPLatency is mapped by region name (e.g. "New York City", "Seattle").

View File

@ -490,6 +490,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",

View File

@ -82,6 +82,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -227,6 +228,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -515,6 +517,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -598,6 +601,7 @@ Status Code **200**
| `»» disconnected_at` | string(date-time) | false | | |
| `»» environment_variables` | object | false | | |
| `»»» [any property]` | string | false | | |
| `»» expanded_directory` | string | false | | |
| `»» first_connected_at` | string(date-time) | false | | |
| `»» id` | string(uuid) | false | | |
| `»» instance_id` | string | false | | |
@ -737,6 +741,7 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -887,6 +892,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -1002,6 +1008,7 @@ Status Code **200**
| `»»» disconnected_at` | string(date-time) | false | | |
| `»»» environment_variables` | object | false | | |
| `»»»» [any property]` | string | false | | |
| `»»» expanded_directory` | string | false | | |
| `»»» first_connected_at` | string(date-time) | false | | |
| `»»» id` | string(uuid) | false | | |
| `»»» instance_id` | string | false | | |
@ -1201,6 +1208,7 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",

View File

@ -228,19 +228,21 @@
| ------- | -------------------------------------------------------------------- | -------- | ------------ | ----------- |
| `state` | [codersdk.WorkspaceAgentLifecycle](#codersdkworkspaceagentlifecycle) | false | | |
## agentsdk.PostVersionRequest
## agentsdk.PostStartupRequest
```json
{
"expanded_directory": "string",
"version": "string"
}
```
### Properties
| Name | Type | Required | Restrictions | Description |
| --------- | ------ | -------- | ------------ | ----------- |
| `version` | string | false | | |
| Name | Type | Required | Restrictions | Description |
| -------------------- | ------ | -------- | ------------ | ----------- |
| `expanded_directory` | string | false | | |
| `version` | string | false | | |
## agentsdk.Stats
@ -4942,6 +4944,7 @@ Parameter represents a set value for the scope.
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -5064,6 +5067,7 @@ Parameter represents a set value for the scope.
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -5104,6 +5108,7 @@ Parameter represents a set value for the scope.
| `disconnected_at` | string | false | | |
| `environment_variables` | object | false | | |
| » `[any property]` | string | false | | |
| `expanded_directory` | string | false | | |
| `first_connected_at` | string | false | | |
| `id` | string | false | | |
| `instance_id` | string | false | | |
@ -5397,6 +5402,7 @@ Parameter represents a set value for the scope.
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -5565,6 +5571,7 @@ Parameter represents a set value for the scope.
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -5755,6 +5762,7 @@ Parameter represents a set value for the scope.
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",

View File

@ -1566,6 +1566,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -1649,6 +1650,7 @@ Status Code **200**
| `»» disconnected_at` | string(date-time) | false | | |
| `»» environment_variables` | object | false | | |
| `»»» [any property]` | string | false | | |
| `»» expanded_directory` | string | false | | |
| `»» first_connected_at` | string(date-time) | false | | |
| `»» id` | string(uuid) | false | | |
| `»» instance_id` | string | false | | |
@ -1918,6 +1920,7 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -2001,6 +2004,7 @@ Status Code **200**
| `»» disconnected_at` | string(date-time) | false | | |
| `»» environment_variables` | object | false | | |
| `»»» [any property]` | string | false | | |
| `»» expanded_directory` | string | false | | |
| `»» first_connected_at` | string(date-time) | false | | |
| `»» id` | string(uuid) | false | | |
| `»» instance_id` | string | false | | |

View File

@ -86,6 +86,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/member
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -250,6 +251,7 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -433,6 +435,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",
@ -598,6 +601,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
"property1": "string",
"property2": "string"
},
"expanded_directory": "string",
"first_connected_at": "2019-08-24T14:15:22Z",
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
"instance_id": "string",

View File

@ -923,6 +923,7 @@ export interface WorkspaceAgent {
readonly operating_system: string
readonly startup_script?: string
readonly directory?: string
readonly expanded_directory?: string
readonly version: string
readonly apps: WorkspaceApp[]
readonly latency?: Record<string, DERPRegion>