mirror of https://github.com/coder/coder.git
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:
parent
f6effdb63e
commit
691495d761
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
BEGIN;
|
||||
|
||||
ALTER TABLE ONLY workspace_agents
|
||||
DROP COLUMN IF EXISTS expanded_directory;
|
||||
|
||||
COMMIT;
|
|
@ -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;
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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").
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 | | |
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue