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
25 changed files with 220 additions and 59 deletions

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
}