mirror of
https://github.com/coder/coder.git
synced 2026-11-14 18:21:32 +08:00
feat: add app status tracking to the backend (#17163)
This does ~95% of the backend work required to integrate the AI work. Most left to integrate from the tasks branch is just frontend, which will be a lot smaller I believe. The real difference between this branch and that one is the abstraction -- this now attaches statuses to apps, and returns the latest status reported as part of a workspace. This change enables us to have a similar UX to in the tasks branch, but for agents other than Claude Code as well. Any app can report status now.
This commit is contained in:
@@ -69,6 +69,7 @@
|
||||
"most_recently_seen": null
|
||||
}
|
||||
},
|
||||
"latest_app_status": null,
|
||||
"outdated": false,
|
||||
"name": "test-workspace",
|
||||
"autostart_schedule": "CRON_TZ=US/Central 30 9 * * 1-5",
|
||||
|
||||
Generated
+127
@@ -8057,6 +8057,45 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/app-status": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Agents"
|
||||
],
|
||||
"summary": "Patch workspace agent app status",
|
||||
"operationId": "patch-workspace-agent-app-status",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "app status",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchAppStatus"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/external-auth": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -10245,6 +10284,29 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchAppStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs_user_attention": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatusState"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchLogs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -16273,6 +16335,9 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"latest_app_status": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatus"
|
||||
},
|
||||
"latest_build": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceBuild"
|
||||
},
|
||||
@@ -16872,6 +16937,13 @@ const docTemplate = `{
|
||||
"description": "Slug is a unique identifier within the agent.",
|
||||
"type": "string"
|
||||
},
|
||||
"statuses": {
|
||||
"description": "Statuses is a list of statuses for the app.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatus"
|
||||
}
|
||||
},
|
||||
"subdomain": {
|
||||
"description": "Subdomain denotes whether the app should be accessed via a path on the\n` + "`" + `coder server` + "`" + ` or via a hostname-based dev URL. If this is set to true\nand there is no app wildcard configured on the server, the app will not\nbe accessible in the UI.",
|
||||
"type": "boolean"
|
||||
@@ -16925,6 +16997,61 @@ const docTemplate = `{
|
||||
"WorkspaceAppSharingLevelPublic"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAppStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Icon is an external URL to an icon that will be rendered in the UI.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs_user_attention": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatusState"
|
||||
},
|
||||
"uri": {
|
||||
"description": "URI is the URI of the resource that the status is for.\ne.g. https://github.com/org/repo/pull/123\ne.g. file:///path/to/file",
|
||||
"type": "string"
|
||||
},
|
||||
"workspace_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAppStatusState": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"working",
|
||||
"complete",
|
||||
"failure"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"WorkspaceAppStatusStateWorking",
|
||||
"WorkspaceAppStatusStateComplete",
|
||||
"WorkspaceAppStatusStateFailure"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceBuild": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Generated
+117
@@ -7122,6 +7122,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/app-status": {
|
||||
"patch": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Agents"],
|
||||
"summary": "Patch workspace agent app status",
|
||||
"operationId": "patch-workspace-agent-app-status",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "app status",
|
||||
"name": "request",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/agentsdk.PatchAppStatus"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.Response"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/workspaceagents/me/external-auth": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -9080,6 +9113,29 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchAppStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"app_slug": {
|
||||
"type": "string"
|
||||
},
|
||||
"icon": {
|
||||
"type": "string"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs_user_attention": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatusState"
|
||||
},
|
||||
"uri": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"agentsdk.PatchLogs": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -14819,6 +14875,9 @@
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"latest_app_status": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatus"
|
||||
},
|
||||
"latest_build": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceBuild"
|
||||
},
|
||||
@@ -15392,6 +15451,13 @@
|
||||
"description": "Slug is a unique identifier within the agent.",
|
||||
"type": "string"
|
||||
},
|
||||
"statuses": {
|
||||
"description": "Statuses is a list of statuses for the app.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatus"
|
||||
}
|
||||
},
|
||||
"subdomain": {
|
||||
"description": "Subdomain denotes whether the app should be accessed via a path on the\n`coder server` or via a hostname-based dev URL. If this is set to true\nand there is no app wildcard configured on the server, the app will not\nbe accessible in the UI.",
|
||||
"type": "boolean"
|
||||
@@ -15433,6 +15499,57 @@
|
||||
"WorkspaceAppSharingLevelPublic"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceAppStatus": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"agent_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"app_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"icon": {
|
||||
"description": "Icon is an external URL to an icon that will be rendered in the UI.",
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"needs_user_attention": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"state": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceAppStatusState"
|
||||
},
|
||||
"uri": {
|
||||
"description": "URI is the URI of the resource that the status is for.\ne.g. https://github.com/org/repo/pull/123\ne.g. file:///path/to/file",
|
||||
"type": "string"
|
||||
},
|
||||
"workspace_id": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.WorkspaceAppStatusState": {
|
||||
"type": "string",
|
||||
"enum": ["working", "complete", "failure"],
|
||||
"x-enum-varnames": [
|
||||
"WorkspaceAppStatusStateWorking",
|
||||
"WorkspaceAppStatusStateComplete",
|
||||
"WorkspaceAppStatusStateFailure"
|
||||
]
|
||||
},
|
||||
"codersdk.WorkspaceBuild": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1228,6 +1228,7 @@ func New(options *Options) *API {
|
||||
}))
|
||||
r.Get("/rpc", api.workspaceAgentRPC)
|
||||
r.Patch("/logs", api.patchWorkspaceAgentLogs)
|
||||
r.Patch("/app-status", api.patchWorkspaceAgentAppStatus)
|
||||
// Deprecated: Required to support legacy agents
|
||||
r.Get("/gitauth", api.workspaceAgentsGitAuth)
|
||||
r.Get("/external-auth", api.workspaceAgentsExternalAuth)
|
||||
|
||||
@@ -487,7 +487,7 @@ func AppSubdomain(dbApp database.WorkspaceApp, agentName, workspaceName, ownerNa
|
||||
}.String()
|
||||
}
|
||||
|
||||
func Apps(dbApps []database.WorkspaceApp, agent database.WorkspaceAgent, ownerName string, workspace database.Workspace) []codersdk.WorkspaceApp {
|
||||
func Apps(dbApps []database.WorkspaceApp, statuses []database.WorkspaceAppStatus, agent database.WorkspaceAgent, ownerName string, workspace database.Workspace) []codersdk.WorkspaceApp {
|
||||
sort.Slice(dbApps, func(i, j int) bool {
|
||||
if dbApps[i].DisplayOrder != dbApps[j].DisplayOrder {
|
||||
return dbApps[i].DisplayOrder < dbApps[j].DisplayOrder
|
||||
@@ -498,8 +498,14 @@ func Apps(dbApps []database.WorkspaceApp, agent database.WorkspaceAgent, ownerNa
|
||||
return dbApps[i].Slug < dbApps[j].Slug
|
||||
})
|
||||
|
||||
statusesByAppID := map[uuid.UUID][]database.WorkspaceAppStatus{}
|
||||
for _, status := range statuses {
|
||||
statusesByAppID[status.AppID] = append(statusesByAppID[status.AppID], status)
|
||||
}
|
||||
|
||||
apps := make([]codersdk.WorkspaceApp, 0)
|
||||
for _, dbApp := range dbApps {
|
||||
statuses := statusesByAppID[dbApp.ID]
|
||||
apps = append(apps, codersdk.WorkspaceApp{
|
||||
ID: dbApp.ID,
|
||||
URL: dbApp.Url.String,
|
||||
@@ -516,14 +522,33 @@ func Apps(dbApps []database.WorkspaceApp, agent database.WorkspaceAgent, ownerNa
|
||||
Interval: dbApp.HealthcheckInterval,
|
||||
Threshold: dbApp.HealthcheckThreshold,
|
||||
},
|
||||
Health: codersdk.WorkspaceAppHealth(dbApp.Health),
|
||||
Hidden: dbApp.Hidden,
|
||||
OpenIn: codersdk.WorkspaceAppOpenIn(dbApp.OpenIn),
|
||||
Health: codersdk.WorkspaceAppHealth(dbApp.Health),
|
||||
Hidden: dbApp.Hidden,
|
||||
OpenIn: codersdk.WorkspaceAppOpenIn(dbApp.OpenIn),
|
||||
Statuses: WorkspaceAppStatuses(statuses),
|
||||
})
|
||||
}
|
||||
return apps
|
||||
}
|
||||
|
||||
func WorkspaceAppStatuses(statuses []database.WorkspaceAppStatus) []codersdk.WorkspaceAppStatus {
|
||||
return List(statuses, WorkspaceAppStatus)
|
||||
}
|
||||
|
||||
func WorkspaceAppStatus(status database.WorkspaceAppStatus) codersdk.WorkspaceAppStatus {
|
||||
return codersdk.WorkspaceAppStatus{
|
||||
ID: status.ID,
|
||||
CreatedAt: status.CreatedAt,
|
||||
AgentID: status.AgentID,
|
||||
AppID: status.AppID,
|
||||
NeedsUserAttention: status.NeedsUserAttention,
|
||||
URI: status.Uri.String,
|
||||
Icon: status.Icon.String,
|
||||
Message: status.Message,
|
||||
State: codersdk.WorkspaceAppStatusState(status.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProvisionerDaemon(dbDaemon database.ProvisionerDaemon) codersdk.ProvisionerDaemon {
|
||||
result := codersdk.ProvisionerDaemon{
|
||||
ID: dbDaemon.ID,
|
||||
|
||||
@@ -1840,6 +1840,13 @@ func (q *querier) GetLatestCryptoKeyByFeature(ctx context.Context, feature datab
|
||||
return q.db.GetLatestCryptoKeyByFeature(ctx, feature)
|
||||
}
|
||||
|
||||
func (q *querier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx, ids)
|
||||
}
|
||||
|
||||
func (q *querier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
if _, err := q.GetWorkspaceByID(ctx, workspaceID); err != nil {
|
||||
return database.WorkspaceBuild{}, err
|
||||
@@ -2854,6 +2861,13 @@ func (q *querier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg datab
|
||||
return q.db.GetWorkspaceAppByAgentIDAndSlug(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceSystem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q.db.GetWorkspaceAppStatusesByAppIDs(ctx, ids)
|
||||
}
|
||||
|
||||
func (q *querier) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]database.WorkspaceApp, error) {
|
||||
if _, err := q.GetWorkspaceByAgentID(ctx, agentID); err != nil {
|
||||
return nil, err
|
||||
@@ -3547,6 +3561,13 @@ func (q *querier) InsertWorkspaceAppStats(ctx context.Context, arg database.Inse
|
||||
return q.db.InsertWorkspaceAppStats(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceAppStatus(ctx context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionCreate, rbac.ResourceSystem); err != nil {
|
||||
return database.WorkspaceAppStatus{}, err
|
||||
}
|
||||
return q.db.InsertWorkspaceAppStatus(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
w, err := q.db.GetWorkspaceByID(ctx, arg.WorkspaceID)
|
||||
if err != nil {
|
||||
|
||||
@@ -3706,6 +3706,12 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
LoginType: database.LoginTypeGithub,
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionUpdate).Returns(l)
|
||||
}))
|
||||
s.Run("GetLatestWorkspaceAppStatusesByWorkspaceIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args([]uuid.UUID{}).Asserts(rbac.ResourceSystem, policy.ActionRead)
|
||||
}))
|
||||
s.Run("GetWorkspaceAppStatusesByAppIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args([]uuid.UUID{}).Asserts(rbac.ResourceSystem, policy.ActionRead)
|
||||
}))
|
||||
s.Run("GetLatestWorkspaceBuildsByWorkspaceIDs", s.Subtest(func(db database.Store, check *expects) {
|
||||
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)
|
||||
ws := dbgen.Workspace(s.T(), db, database.WorkspaceTable{})
|
||||
@@ -4135,6 +4141,13 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
Options: json.RawMessage("{}"),
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("InsertWorkspaceAppStatus", s.Subtest(func(db database.Store, check *expects) {
|
||||
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)
|
||||
check.Args(database.InsertWorkspaceAppStatusParams{
|
||||
ID: uuid.New(),
|
||||
State: "working",
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionCreate)
|
||||
}))
|
||||
s.Run("InsertWorkspaceResource", s.Subtest(func(db database.Store, check *expects) {
|
||||
dbtestutil.DisableForeignKeysAndTriggers(s.T(), db)
|
||||
check.Args(database.InsertWorkspaceResourceParams{
|
||||
|
||||
@@ -259,6 +259,7 @@ type data struct {
|
||||
workspaceAgentVolumeResourceMonitors []database.WorkspaceAgentVolumeResourceMonitor
|
||||
workspaceAgentDevcontainers []database.WorkspaceAgentDevcontainer
|
||||
workspaceApps []database.WorkspaceApp
|
||||
workspaceAppStatuses []database.WorkspaceAppStatus
|
||||
workspaceAppAuditSessions []database.WorkspaceAppAuditSession
|
||||
workspaceAppStatsLastInsertID int64
|
||||
workspaceAppStats []database.WorkspaceAppStat
|
||||
@@ -3697,6 +3698,34 @@ func (q *FakeQuerier) GetLatestCryptoKeyByFeature(_ context.Context, feature dat
|
||||
return latestKey, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
// Map to track latest status per workspace ID
|
||||
latestByWorkspace := make(map[uuid.UUID]database.WorkspaceAppStatus)
|
||||
|
||||
// Find latest status for each workspace ID
|
||||
for _, appStatus := range q.workspaceAppStatuses {
|
||||
if !slices.Contains(ids, appStatus.WorkspaceID) {
|
||||
continue
|
||||
}
|
||||
|
||||
current, exists := latestByWorkspace[appStatus.WorkspaceID]
|
||||
if !exists || appStatus.CreatedAt.After(current.CreatedAt) {
|
||||
latestByWorkspace[appStatus.WorkspaceID] = appStatus
|
||||
}
|
||||
}
|
||||
|
||||
// Convert map to slice
|
||||
appStatuses := make([]database.WorkspaceAppStatus, 0, len(latestByWorkspace))
|
||||
for _, status := range latestByWorkspace {
|
||||
appStatuses = append(appStatuses, status)
|
||||
}
|
||||
|
||||
return appStatuses, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -7488,6 +7517,21 @@ func (q *FakeQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg d
|
||||
return q.getWorkspaceAppByAgentIDAndSlugNoLock(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAppStatusesByAppIDs(_ context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
statuses := make([]database.WorkspaceAppStatus, 0)
|
||||
for _, status := range q.workspaceAppStatuses {
|
||||
for _, id := range ids {
|
||||
if status.AppID == id {
|
||||
statuses = append(statuses, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
return statuses, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspaceAppsByAgentID(_ context.Context, id uuid.UUID) ([]database.WorkspaceApp, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -9584,6 +9628,31 @@ InsertWorkspaceAppStatsLoop:
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceAppStatus(_ context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.WorkspaceAppStatus{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
status := database.WorkspaceAppStatus{
|
||||
ID: arg.ID,
|
||||
CreatedAt: arg.CreatedAt,
|
||||
WorkspaceID: arg.WorkspaceID,
|
||||
AgentID: arg.AgentID,
|
||||
AppID: arg.AppID,
|
||||
NeedsUserAttention: arg.NeedsUserAttention,
|
||||
State: arg.State,
|
||||
Message: arg.Message,
|
||||
Uri: arg.Uri,
|
||||
Icon: arg.Icon,
|
||||
}
|
||||
q.workspaceAppStatuses = append(q.workspaceAppStatuses, status)
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertWorkspaceBuild(_ context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return err
|
||||
|
||||
@@ -858,6 +858,13 @@ func (m queryMetricsStore) GetLatestCryptoKeyByFeature(ctx context.Context, feat
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx, ids)
|
||||
m.queryLatencies.WithLabelValues("GetLatestWorkspaceAppStatusesByWorkspaceIDs").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
start := time.Now()
|
||||
build, err := m.s.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspaceID)
|
||||
@@ -1670,6 +1677,13 @@ func (m queryMetricsStore) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context,
|
||||
return app, err
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetWorkspaceAppStatusesByAppIDs(ctx, ids)
|
||||
m.queryLatencies.WithLabelValues("GetWorkspaceAppStatusesByAppIDs").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]database.WorkspaceApp, error) {
|
||||
start := time.Now()
|
||||
apps, err := m.s.GetWorkspaceAppsByAgentID(ctx, agentID)
|
||||
@@ -2265,6 +2279,13 @@ func (m queryMetricsStore) InsertWorkspaceAppStats(ctx context.Context, arg data
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertWorkspaceAppStatus(ctx context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertWorkspaceAppStatus(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertWorkspaceAppStatus").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.InsertWorkspaceBuild(ctx, arg)
|
||||
|
||||
@@ -1729,6 +1729,21 @@ func (mr *MockStoreMockRecorder) GetLatestCryptoKeyByFeature(ctx, feature any) *
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestCryptoKeyByFeature", reflect.TypeOf((*MockStore)(nil).GetLatestCryptoKeyByFeature), ctx, feature)
|
||||
}
|
||||
|
||||
// GetLatestWorkspaceAppStatusesByWorkspaceIDs mocks base method.
|
||||
func (m *MockStore) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetLatestWorkspaceAppStatusesByWorkspaceIDs", ctx, ids)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAppStatus)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetLatestWorkspaceAppStatusesByWorkspaceIDs indicates an expected call of GetLatestWorkspaceAppStatusesByWorkspaceIDs.
|
||||
func (mr *MockStoreMockRecorder) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx, ids any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLatestWorkspaceAppStatusesByWorkspaceIDs", reflect.TypeOf((*MockStore)(nil).GetLatestWorkspaceAppStatusesByWorkspaceIDs), ctx, ids)
|
||||
}
|
||||
|
||||
// GetLatestWorkspaceBuildByWorkspaceID mocks base method.
|
||||
func (m *MockStore) GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (database.WorkspaceBuild, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -3499,6 +3514,21 @@ func (mr *MockStoreMockRecorder) GetWorkspaceAppByAgentIDAndSlug(ctx, arg any) *
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAppByAgentIDAndSlug", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAppByAgentIDAndSlug), ctx, arg)
|
||||
}
|
||||
|
||||
// GetWorkspaceAppStatusesByAppIDs mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]database.WorkspaceAppStatus, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetWorkspaceAppStatusesByAppIDs", ctx, ids)
|
||||
ret0, _ := ret[0].([]database.WorkspaceAppStatus)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetWorkspaceAppStatusesByAppIDs indicates an expected call of GetWorkspaceAppStatusesByAppIDs.
|
||||
func (mr *MockStoreMockRecorder) GetWorkspaceAppStatusesByAppIDs(ctx, ids any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWorkspaceAppStatusesByAppIDs", reflect.TypeOf((*MockStore)(nil).GetWorkspaceAppStatusesByAppIDs), ctx, ids)
|
||||
}
|
||||
|
||||
// GetWorkspaceAppsByAgentID mocks base method.
|
||||
func (m *MockStore) GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]database.WorkspaceApp, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -4776,6 +4806,21 @@ func (mr *MockStoreMockRecorder) InsertWorkspaceAppStats(ctx, arg any) *gomock.C
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAppStats", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAppStats), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertWorkspaceAppStatus mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceAppStatus(ctx context.Context, arg database.InsertWorkspaceAppStatusParams) (database.WorkspaceAppStatus, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertWorkspaceAppStatus", ctx, arg)
|
||||
ret0, _ := ret[0].(database.WorkspaceAppStatus)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertWorkspaceAppStatus indicates an expected call of InsertWorkspaceAppStatus.
|
||||
func (mr *MockStoreMockRecorder) InsertWorkspaceAppStatus(ctx, arg any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertWorkspaceAppStatus", reflect.TypeOf((*MockStore)(nil).InsertWorkspaceAppStatus), ctx, arg)
|
||||
}
|
||||
|
||||
// InsertWorkspaceBuild mocks base method.
|
||||
func (m *MockStore) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Generated
+33
@@ -293,6 +293,12 @@ CREATE TYPE workspace_app_open_in AS ENUM (
|
||||
'slim-window'
|
||||
);
|
||||
|
||||
CREATE TYPE workspace_app_status_state AS ENUM (
|
||||
'working',
|
||||
'complete',
|
||||
'failure'
|
||||
);
|
||||
|
||||
CREATE TYPE workspace_transition AS ENUM (
|
||||
'start',
|
||||
'stop',
|
||||
@@ -1896,6 +1902,19 @@ CREATE SEQUENCE workspace_app_stats_id_seq
|
||||
|
||||
ALTER SEQUENCE workspace_app_stats_id_seq OWNED BY workspace_app_stats.id;
|
||||
|
||||
CREATE TABLE workspace_app_statuses (
|
||||
id uuid DEFAULT gen_random_uuid() NOT NULL,
|
||||
created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
agent_id uuid NOT NULL,
|
||||
app_id uuid NOT NULL,
|
||||
workspace_id uuid NOT NULL,
|
||||
state workspace_app_status_state NOT NULL,
|
||||
needs_user_attention boolean NOT NULL,
|
||||
message text NOT NULL,
|
||||
uri text,
|
||||
icon text
|
||||
);
|
||||
|
||||
CREATE TABLE workspace_apps (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
@@ -2359,6 +2378,9 @@ ALTER TABLE ONLY workspace_app_stats
|
||||
ALTER TABLE ONLY workspace_app_stats
|
||||
ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id);
|
||||
|
||||
ALTER TABLE ONLY workspace_app_statuses
|
||||
ADD CONSTRAINT workspace_app_statuses_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY workspace_apps
|
||||
ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug);
|
||||
|
||||
@@ -2451,6 +2473,8 @@ CREATE UNIQUE INDEX idx_users_email ON users USING btree (email) WHERE (deleted
|
||||
|
||||
CREATE UNIQUE INDEX idx_users_username ON users USING btree (username) WHERE (deleted = false);
|
||||
|
||||
CREATE INDEX idx_workspace_app_statuses_workspace_id_created_at ON workspace_app_statuses USING btree (workspace_id, created_at DESC);
|
||||
|
||||
CREATE UNIQUE INDEX notification_messages_dedupe_hash_idx ON notification_messages USING btree (dedupe_hash);
|
||||
|
||||
CREATE UNIQUE INDEX organizations_single_default_org ON organizations USING btree (is_default) WHERE (is_default = true);
|
||||
@@ -2802,6 +2826,15 @@ ALTER TABLE ONLY workspace_app_stats
|
||||
ALTER TABLE ONLY workspace_app_stats
|
||||
ADD CONSTRAINT workspace_app_stats_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id);
|
||||
|
||||
ALTER TABLE ONLY workspace_app_statuses
|
||||
ADD CONSTRAINT workspace_app_statuses_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id);
|
||||
|
||||
ALTER TABLE ONLY workspace_app_statuses
|
||||
ADD CONSTRAINT workspace_app_statuses_app_id_fkey FOREIGN KEY (app_id) REFERENCES workspace_apps(id);
|
||||
|
||||
ALTER TABLE ONLY workspace_app_statuses
|
||||
ADD CONSTRAINT workspace_app_statuses_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id);
|
||||
|
||||
ALTER TABLE ONLY workspace_apps
|
||||
ADD CONSTRAINT workspace_apps_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@ const (
|
||||
ForeignKeyWorkspaceAppStatsAgentID ForeignKeyConstraint = "workspace_app_stats_agent_id_fkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id);
|
||||
ForeignKeyWorkspaceAppStatsUserID ForeignKeyConstraint = "workspace_app_stats_user_id_fkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id);
|
||||
ForeignKeyWorkspaceAppStatsWorkspaceID ForeignKeyConstraint = "workspace_app_stats_workspace_id_fkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id);
|
||||
ForeignKeyWorkspaceAppStatusesAgentID ForeignKeyConstraint = "workspace_app_statuses_agent_id_fkey" // ALTER TABLE ONLY workspace_app_statuses ADD CONSTRAINT workspace_app_statuses_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id);
|
||||
ForeignKeyWorkspaceAppStatusesAppID ForeignKeyConstraint = "workspace_app_statuses_app_id_fkey" // ALTER TABLE ONLY workspace_app_statuses ADD CONSTRAINT workspace_app_statuses_app_id_fkey FOREIGN KEY (app_id) REFERENCES workspace_apps(id);
|
||||
ForeignKeyWorkspaceAppStatusesWorkspaceID ForeignKeyConstraint = "workspace_app_statuses_workspace_id_fkey" // ALTER TABLE ONLY workspace_app_statuses ADD CONSTRAINT workspace_app_statuses_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id);
|
||||
ForeignKeyWorkspaceAppsAgentID ForeignKeyConstraint = "workspace_apps_agent_id_fkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceBuildParametersWorkspaceBuildID ForeignKeyConstraint = "workspace_build_parameters_workspace_build_id_fkey" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_fkey FOREIGN KEY (workspace_build_id) REFERENCES workspace_builds(id) ON DELETE CASCADE;
|
||||
ForeignKeyWorkspaceBuildsJobID ForeignKeyConstraint = "workspace_builds_job_id_fkey" // ALTER TABLE ONLY workspace_builds ADD CONSTRAINT workspace_builds_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
DROP TABLE workspace_app_statuses;
|
||||
|
||||
DROP TYPE workspace_app_status_state;
|
||||
@@ -0,0 +1,28 @@
|
||||
CREATE TYPE workspace_app_status_state AS ENUM ('working', 'complete', 'failure');
|
||||
|
||||
-- Workspace app statuses allow agents to report statuses per-app in the UI.
|
||||
CREATE TABLE workspace_app_statuses (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
-- The agent that the status is for.
|
||||
agent_id UUID NOT NULL REFERENCES workspace_agents(id),
|
||||
-- The slug of the app that the status is for. This will be used
|
||||
-- to reference the app in the UI - with an icon.
|
||||
app_id UUID NOT NULL REFERENCES workspace_apps(id),
|
||||
-- workspace_id is the workspace that the status is for.
|
||||
workspace_id UUID NOT NULL REFERENCES workspaces(id),
|
||||
-- The status determines how the status is displayed in the UI.
|
||||
state workspace_app_status_state NOT NULL,
|
||||
-- Whether the status needs user attention.
|
||||
needs_user_attention BOOLEAN NOT NULL,
|
||||
-- The message is the main text that will be displayed in the UI.
|
||||
message TEXT NOT NULL,
|
||||
-- The URI of the resource that the status is for.
|
||||
-- e.g. https://github.com/org/repo/pull/123
|
||||
-- e.g. file:///path/to/file
|
||||
uri TEXT,
|
||||
-- Icon is an external URL to an icon that will be rendered in the UI.
|
||||
icon TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX idx_workspace_app_statuses_workspace_id_created_at ON workspace_app_statuses(workspace_id, created_at DESC);
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
INSERT INTO workspace_app_statuses (
|
||||
id,
|
||||
created_at,
|
||||
agent_id,
|
||||
app_id,
|
||||
workspace_id,
|
||||
state,
|
||||
needs_user_attention,
|
||||
message
|
||||
) VALUES (
|
||||
gen_random_uuid(),
|
||||
NOW(),
|
||||
'7a1ce5f8-8d00-431c-ad1b-97a846512804',
|
||||
'36b65d0c-042b-4653-863a-655ee739861c',
|
||||
'3a9a1feb-e89d-457c-9d53-ac751b198ebe',
|
||||
'working',
|
||||
false,
|
||||
'Creating SQL queries for test data!'
|
||||
);
|
||||
@@ -2414,6 +2414,67 @@ func AllWorkspaceAppOpenInValues() []WorkspaceAppOpenIn {
|
||||
}
|
||||
}
|
||||
|
||||
type WorkspaceAppStatusState string
|
||||
|
||||
const (
|
||||
WorkspaceAppStatusStateWorking WorkspaceAppStatusState = "working"
|
||||
WorkspaceAppStatusStateComplete WorkspaceAppStatusState = "complete"
|
||||
WorkspaceAppStatusStateFailure WorkspaceAppStatusState = "failure"
|
||||
)
|
||||
|
||||
func (e *WorkspaceAppStatusState) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = WorkspaceAppStatusState(s)
|
||||
case string:
|
||||
*e = WorkspaceAppStatusState(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for WorkspaceAppStatusState: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullWorkspaceAppStatusState struct {
|
||||
WorkspaceAppStatusState WorkspaceAppStatusState `json:"workspace_app_status_state"`
|
||||
Valid bool `json:"valid"` // Valid is true if WorkspaceAppStatusState is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullWorkspaceAppStatusState) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.WorkspaceAppStatusState, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.WorkspaceAppStatusState.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullWorkspaceAppStatusState) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.WorkspaceAppStatusState), nil
|
||||
}
|
||||
|
||||
func (e WorkspaceAppStatusState) Valid() bool {
|
||||
switch e {
|
||||
case WorkspaceAppStatusStateWorking,
|
||||
WorkspaceAppStatusStateComplete,
|
||||
WorkspaceAppStatusStateFailure:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AllWorkspaceAppStatusStateValues() []WorkspaceAppStatusState {
|
||||
return []WorkspaceAppStatusState{
|
||||
WorkspaceAppStatusStateWorking,
|
||||
WorkspaceAppStatusStateComplete,
|
||||
WorkspaceAppStatusStateFailure,
|
||||
}
|
||||
}
|
||||
|
||||
type WorkspaceTransition string
|
||||
|
||||
const (
|
||||
@@ -3515,6 +3576,19 @@ type WorkspaceAppStat struct {
|
||||
Requests int32 `db:"requests" json:"requests"`
|
||||
}
|
||||
|
||||
type WorkspaceAppStatus struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
State WorkspaceAppStatusState `db:"state" json:"state"`
|
||||
NeedsUserAttention bool `db:"needs_user_attention" json:"needs_user_attention"`
|
||||
Message string `db:"message" json:"message"`
|
||||
Uri sql.NullString `db:"uri" json:"uri"`
|
||||
Icon sql.NullString `db:"icon" json:"icon"`
|
||||
}
|
||||
|
||||
// Joins in the username + avatar url of the initiated by user.
|
||||
type WorkspaceBuild struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
|
||||
@@ -198,6 +198,7 @@ type sqlcQuerier interface {
|
||||
GetJFrogXrayScanByWorkspaceAndAgentID(ctx context.Context, arg GetJFrogXrayScanByWorkspaceAndAgentIDParams) (JfrogXrayScan, error)
|
||||
GetLastUpdateCheck(ctx context.Context) (string, error)
|
||||
GetLatestCryptoKeyByFeature(ctx context.Context, feature CryptoKeyFeature) (CryptoKey, error)
|
||||
GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAppStatus, error)
|
||||
GetLatestWorkspaceBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) (WorkspaceBuild, error)
|
||||
GetLatestWorkspaceBuilds(ctx context.Context) ([]WorkspaceBuild, error)
|
||||
GetLatestWorkspaceBuildsByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceBuild, error)
|
||||
@@ -369,6 +370,7 @@ type sqlcQuerier interface {
|
||||
GetWorkspaceAgentsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceAgent, error)
|
||||
GetWorkspaceAgentsInLatestBuildByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]WorkspaceAgent, error)
|
||||
GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg GetWorkspaceAppByAgentIDAndSlugParams) (WorkspaceApp, error)
|
||||
GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAppStatus, error)
|
||||
GetWorkspaceAppsByAgentID(ctx context.Context, agentID uuid.UUID) ([]WorkspaceApp, error)
|
||||
GetWorkspaceAppsByAgentIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceApp, error)
|
||||
GetWorkspaceAppsCreatedAfter(ctx context.Context, createdAt time.Time) ([]WorkspaceApp, error)
|
||||
@@ -474,6 +476,7 @@ type sqlcQuerier interface {
|
||||
InsertWorkspaceAgentStats(ctx context.Context, arg InsertWorkspaceAgentStatsParams) error
|
||||
InsertWorkspaceApp(ctx context.Context, arg InsertWorkspaceAppParams) (WorkspaceApp, error)
|
||||
InsertWorkspaceAppStats(ctx context.Context, arg InsertWorkspaceAppStatsParams) error
|
||||
InsertWorkspaceAppStatus(ctx context.Context, arg InsertWorkspaceAppStatusParams) (WorkspaceAppStatus, error)
|
||||
InsertWorkspaceBuild(ctx context.Context, arg InsertWorkspaceBuildParams) error
|
||||
InsertWorkspaceBuildParameters(ctx context.Context, arg InsertWorkspaceBuildParametersParams) error
|
||||
InsertWorkspaceModule(ctx context.Context, arg InsertWorkspaceModuleParams) (WorkspaceModule, error)
|
||||
|
||||
@@ -15108,6 +15108,48 @@ func (q *sqlQuerier) UpsertWorkspaceAppAuditSession(ctx context.Context, arg Ups
|
||||
return new_or_stale, err
|
||||
}
|
||||
|
||||
const getLatestWorkspaceAppStatusesByWorkspaceIDs = `-- name: GetLatestWorkspaceAppStatusesByWorkspaceIDs :many
|
||||
SELECT DISTINCT ON (workspace_id)
|
||||
id, created_at, agent_id, app_id, workspace_id, state, needs_user_attention, message, uri, icon
|
||||
FROM workspace_app_statuses
|
||||
WHERE workspace_id = ANY($1 :: uuid[])
|
||||
ORDER BY workspace_id, created_at DESC
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetLatestWorkspaceAppStatusesByWorkspaceIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAppStatus, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getLatestWorkspaceAppStatusesByWorkspaceIDs, pq.Array(ids))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAppStatus
|
||||
for rows.Next() {
|
||||
var i WorkspaceAppStatus
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.AgentID,
|
||||
&i.AppID,
|
||||
&i.WorkspaceID,
|
||||
&i.State,
|
||||
&i.NeedsUserAttention,
|
||||
&i.Message,
|
||||
&i.Uri,
|
||||
&i.Icon,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceAppByAgentIDAndSlug = `-- name: GetWorkspaceAppByAgentIDAndSlug :one
|
||||
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, open_in FROM workspace_apps WHERE agent_id = $1 AND slug = $2
|
||||
`
|
||||
@@ -15143,6 +15185,44 @@ func (q *sqlQuerier) GetWorkspaceAppByAgentIDAndSlug(ctx context.Context, arg Ge
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getWorkspaceAppStatusesByAppIDs = `-- name: GetWorkspaceAppStatusesByAppIDs :many
|
||||
SELECT id, created_at, agent_id, app_id, workspace_id, state, needs_user_attention, message, uri, icon FROM workspace_app_statuses WHERE app_id = ANY($1 :: uuid [ ])
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetWorkspaceAppStatusesByAppIDs(ctx context.Context, ids []uuid.UUID) ([]WorkspaceAppStatus, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspaceAppStatusesByAppIDs, pq.Array(ids))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []WorkspaceAppStatus
|
||||
for rows.Next() {
|
||||
var i WorkspaceAppStatus
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.AgentID,
|
||||
&i.AppID,
|
||||
&i.WorkspaceID,
|
||||
&i.State,
|
||||
&i.NeedsUserAttention,
|
||||
&i.Message,
|
||||
&i.Uri,
|
||||
&i.Icon,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspaceAppsByAgentID = `-- name: GetWorkspaceAppsByAgentID :many
|
||||
SELECT id, created_at, agent_id, display_name, icon, command, url, healthcheck_url, healthcheck_interval, healthcheck_threshold, health, subdomain, sharing_level, slug, external, display_order, hidden, open_in FROM workspace_apps WHERE agent_id = $1 ORDER BY slug ASC
|
||||
`
|
||||
@@ -15373,6 +15453,54 @@ func (q *sqlQuerier) InsertWorkspaceApp(ctx context.Context, arg InsertWorkspace
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertWorkspaceAppStatus = `-- name: InsertWorkspaceAppStatus :one
|
||||
INSERT INTO workspace_app_statuses (id, created_at, workspace_id, agent_id, app_id, state, message, needs_user_attention, uri, icon)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING id, created_at, agent_id, app_id, workspace_id, state, needs_user_attention, message, uri, icon
|
||||
`
|
||||
|
||||
type InsertWorkspaceAppStatusParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
WorkspaceID uuid.UUID `db:"workspace_id" json:"workspace_id"`
|
||||
AgentID uuid.UUID `db:"agent_id" json:"agent_id"`
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
State WorkspaceAppStatusState `db:"state" json:"state"`
|
||||
Message string `db:"message" json:"message"`
|
||||
NeedsUserAttention bool `db:"needs_user_attention" json:"needs_user_attention"`
|
||||
Uri sql.NullString `db:"uri" json:"uri"`
|
||||
Icon sql.NullString `db:"icon" json:"icon"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertWorkspaceAppStatus(ctx context.Context, arg InsertWorkspaceAppStatusParams) (WorkspaceAppStatus, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertWorkspaceAppStatus,
|
||||
arg.ID,
|
||||
arg.CreatedAt,
|
||||
arg.WorkspaceID,
|
||||
arg.AgentID,
|
||||
arg.AppID,
|
||||
arg.State,
|
||||
arg.Message,
|
||||
arg.NeedsUserAttention,
|
||||
arg.Uri,
|
||||
arg.Icon,
|
||||
)
|
||||
var i WorkspaceAppStatus
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.AgentID,
|
||||
&i.AppID,
|
||||
&i.WorkspaceID,
|
||||
&i.State,
|
||||
&i.NeedsUserAttention,
|
||||
&i.Message,
|
||||
&i.Uri,
|
||||
&i.Icon,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateWorkspaceAppHealthByID = `-- name: UpdateWorkspaceAppHealthByID :exec
|
||||
UPDATE
|
||||
workspace_apps
|
||||
|
||||
@@ -42,3 +42,18 @@ SET
|
||||
health = $2
|
||||
WHERE
|
||||
id = $1;
|
||||
|
||||
-- name: InsertWorkspaceAppStatus :one
|
||||
INSERT INTO workspace_app_statuses (id, created_at, workspace_id, agent_id, app_id, state, message, needs_user_attention, uri, icon)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetWorkspaceAppStatusesByAppIDs :many
|
||||
SELECT * FROM workspace_app_statuses WHERE app_id = ANY(@ids :: uuid [ ]);
|
||||
|
||||
-- name: GetLatestWorkspaceAppStatusesByWorkspaceIDs :many
|
||||
SELECT DISTINCT ON (workspace_id)
|
||||
*
|
||||
FROM workspace_app_statuses
|
||||
WHERE workspace_id = ANY(@ids :: uuid[])
|
||||
ORDER BY workspace_id, created_at DESC;
|
||||
|
||||
@@ -86,6 +86,7 @@ const (
|
||||
UniqueWorkspaceAppAuditSessionsPkey UniqueConstraint = "workspace_app_audit_sessions_pkey" // ALTER TABLE ONLY workspace_app_audit_sessions ADD CONSTRAINT workspace_app_audit_sessions_pkey PRIMARY KEY (id);
|
||||
UniqueWorkspaceAppStatsPkey UniqueConstraint = "workspace_app_stats_pkey" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_pkey PRIMARY KEY (id);
|
||||
UniqueWorkspaceAppStatsUserIDAgentIDSessionIDKey UniqueConstraint = "workspace_app_stats_user_id_agent_id_session_id_key" // ALTER TABLE ONLY workspace_app_stats ADD CONSTRAINT workspace_app_stats_user_id_agent_id_session_id_key UNIQUE (user_id, agent_id, session_id);
|
||||
UniqueWorkspaceAppStatusesPkey UniqueConstraint = "workspace_app_statuses_pkey" // ALTER TABLE ONLY workspace_app_statuses ADD CONSTRAINT workspace_app_statuses_pkey PRIMARY KEY (id);
|
||||
UniqueWorkspaceAppsAgentIDSlugIndex UniqueConstraint = "workspace_apps_agent_id_slug_idx" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_agent_id_slug_idx UNIQUE (agent_id, slug);
|
||||
UniqueWorkspaceAppsPkey UniqueConstraint = "workspace_apps_pkey" // ALTER TABLE ONLY workspace_apps ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id);
|
||||
UniqueWorkspaceBuildParametersWorkspaceBuildIDNameKey UniqueConstraint = "workspace_build_parameters_workspace_build_id_name_key" // ALTER TABLE ONLY workspace_build_parameters ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name);
|
||||
|
||||
@@ -93,6 +93,20 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
appIDs := []uuid.UUID{}
|
||||
for _, app := range dbApps {
|
||||
appIDs = append(appIDs, app.ID)
|
||||
}
|
||||
// nolint:gocritic // This is a system restricted operation.
|
||||
statuses, err := api.Database.GetWorkspaceAppStatusesByAppIDs(dbauthz.AsSystemRestricted(ctx), appIDs)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching workspace app statuses.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resource, err := api.Database.GetWorkspaceResourceByID(ctx, workspaceAgent.ResourceID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@@ -127,7 +141,7 @@ func (api *API) workspaceAgent(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
apiAgent, err := db2sdk.WorkspaceAgent(
|
||||
api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, db2sdk.Apps(dbApps, workspaceAgent, owner.Username, workspace), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout,
|
||||
api.DERPMap(), *api.TailnetCoordinator.Load(), workspaceAgent, db2sdk.Apps(dbApps, statuses, workspaceAgent, owner.Username, workspace), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout,
|
||||
api.DeploymentValues.AgentFallbackTroubleshootingURL.String(),
|
||||
)
|
||||
if err != nil {
|
||||
@@ -300,6 +314,81 @@ func (api *API) patchWorkspaceAgentLogs(rw http.ResponseWriter, r *http.Request)
|
||||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// @Summary Patch workspace agent app status
|
||||
// @ID patch-workspace-agent-app-status
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Tags Agents
|
||||
// @Param request body agentsdk.PatchAppStatus true "app status"
|
||||
// @Success 200 {object} codersdk.Response
|
||||
// @Router /workspaceagents/me/app-status [patch]
|
||||
func (api *API) patchWorkspaceAgentAppStatus(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
workspaceAgent := httpmw.WorkspaceAgent(r)
|
||||
|
||||
var req agentsdk.PatchAppStatus
|
||||
if !httpapi.Read(ctx, rw, r, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
app, err := api.Database.GetWorkspaceAppByAgentIDAndSlug(ctx, database.GetWorkspaceAppByAgentIDAndSlugParams{
|
||||
AgentID: workspaceAgent.ID,
|
||||
Slug: req.AppSlug,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Failed to get workspace app.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
workspace, err := api.Database.GetWorkspaceByAgentID(ctx, workspaceAgent.ID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Failed to get workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// nolint:gocritic // This is a system restricted operation.
|
||||
_, err = api.Database.InsertWorkspaceAppStatus(dbauthz.AsSystemRestricted(ctx), database.InsertWorkspaceAppStatusParams{
|
||||
ID: uuid.New(),
|
||||
CreatedAt: dbtime.Now(),
|
||||
WorkspaceID: workspace.ID,
|
||||
AgentID: workspaceAgent.ID,
|
||||
AppID: app.ID,
|
||||
State: database.WorkspaceAppStatusState(req.State),
|
||||
Message: req.Message,
|
||||
Uri: sql.NullString{
|
||||
String: req.URI,
|
||||
Valid: req.URI != "",
|
||||
},
|
||||
Icon: sql.NullString{
|
||||
String: req.Icon,
|
||||
Valid: req.Icon != "",
|
||||
},
|
||||
NeedsUserAttention: req.NeedsUserAttention,
|
||||
})
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Failed to insert workspace app status.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
api.publishWorkspaceUpdate(ctx, workspace.OwnerID, wspubsub.WorkspaceEvent{
|
||||
Kind: wspubsub.WorkspaceEventKindAgentAppStatusUpdate,
|
||||
WorkspaceID: workspace.ID,
|
||||
AgentID: &workspaceAgent.ID,
|
||||
})
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, nil)
|
||||
}
|
||||
|
||||
// workspaceAgentLogs returns the logs associated with a workspace agent
|
||||
//
|
||||
// @Summary Get logs by workspace agent
|
||||
@@ -1054,7 +1143,7 @@ func (api *API) workspaceAgentPostLogSource(rw http.ResponseWriter, r *http.Requ
|
||||
// convertProvisionedApps converts applications that are in the middle of provisioning process.
|
||||
// It means that they may not have an agent or workspace assigned (dry-run job).
|
||||
func convertProvisionedApps(dbApps []database.WorkspaceApp) []codersdk.WorkspaceApp {
|
||||
return db2sdk.Apps(dbApps, database.WorkspaceAgent{}, "", database.Workspace{})
|
||||
return db2sdk.Apps(dbApps, []database.WorkspaceAppStatus{}, database.WorkspaceAgent{}, "", database.Workspace{})
|
||||
}
|
||||
|
||||
func convertLogSources(dbLogSources []database.WorkspaceAgentLogSource) []codersdk.WorkspaceAgentLogSource {
|
||||
|
||||
@@ -339,6 +339,46 @@ func TestWorkspaceAgentLogs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgentAppStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("Success", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := testutil.Context(t, testutil.WaitMedium)
|
||||
client, db := coderdtest.NewWithDatabase(t, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
client, user2 := coderdtest.CreateAnotherUser(t, client, user.OrganizationID)
|
||||
|
||||
r := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user2.ID,
|
||||
}).WithAgent(func(a []*proto.Agent) []*proto.Agent {
|
||||
a[0].Apps = []*proto.App{
|
||||
{
|
||||
Slug: "vscode",
|
||||
},
|
||||
}
|
||||
return a
|
||||
}).Do()
|
||||
|
||||
agentClient := agentsdk.New(client.URL)
|
||||
agentClient.SetSessionToken(r.AgentToken)
|
||||
err := agentClient.PatchAppStatus(ctx, agentsdk.PatchAppStatus{
|
||||
AppSlug: "vscode",
|
||||
Message: "testing",
|
||||
URI: "https://example.com",
|
||||
Icon: "https://example.com/icon.png",
|
||||
State: codersdk.WorkspaceAppStatusStateComplete,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
workspace, err := client.Workspace(ctx, r.Workspace.ID)
|
||||
require.NoError(t, err)
|
||||
agent, err := client.WorkspaceAgent(ctx, workspace.LatestBuild.Resources[0].Agents[0].ID)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, agent.Apps[0].Statuses, 1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWorkspaceAgentConnectRPC(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
@@ -84,6 +84,7 @@ func (api *API) workspaceBuild(rw http.ResponseWriter, r *http.Request) {
|
||||
data.metadata,
|
||||
data.agents,
|
||||
data.apps,
|
||||
data.appStatuses,
|
||||
data.scripts,
|
||||
data.logSources,
|
||||
data.templateVersions[0],
|
||||
@@ -202,6 +203,7 @@ func (api *API) workspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
data.metadata,
|
||||
data.agents,
|
||||
data.apps,
|
||||
data.appStatuses,
|
||||
data.scripts,
|
||||
data.logSources,
|
||||
data.templateVersions,
|
||||
@@ -292,6 +294,7 @@ func (api *API) workspaceBuildByBuildNumber(rw http.ResponseWriter, r *http.Requ
|
||||
data.metadata,
|
||||
data.agents,
|
||||
data.apps,
|
||||
data.appStatuses,
|
||||
data.scripts,
|
||||
data.logSources,
|
||||
data.templateVersions[0],
|
||||
@@ -432,6 +435,7 @@ func (api *API) postWorkspaceBuilds(rw http.ResponseWriter, r *http.Request) {
|
||||
[]database.WorkspaceResourceMetadatum{},
|
||||
[]database.WorkspaceAgent{},
|
||||
[]database.WorkspaceApp{},
|
||||
[]database.WorkspaceAppStatus{},
|
||||
[]database.WorkspaceAgentScript{},
|
||||
[]database.WorkspaceAgentLogSource{},
|
||||
database.TemplateVersion{},
|
||||
@@ -764,6 +768,7 @@ type workspaceBuildsData struct {
|
||||
metadata []database.WorkspaceResourceMetadatum
|
||||
agents []database.WorkspaceAgent
|
||||
apps []database.WorkspaceApp
|
||||
appStatuses []database.WorkspaceAppStatus
|
||||
scripts []database.WorkspaceAgentScript
|
||||
logSources []database.WorkspaceAgentLogSource
|
||||
provisionerDaemons []database.GetEligibleProvisionerDaemonsByProvisionerJobIDsRow
|
||||
@@ -874,6 +879,17 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaceBuilds []datab
|
||||
return workspaceBuildsData{}, err
|
||||
}
|
||||
|
||||
appIDs := make([]uuid.UUID, 0)
|
||||
for _, app := range apps {
|
||||
appIDs = append(appIDs, app.ID)
|
||||
}
|
||||
|
||||
// nolint:gocritic // Getting workspace app statuses by app IDs is a system function.
|
||||
statuses, err := api.Database.GetWorkspaceAppStatusesByAppIDs(dbauthz.AsSystemRestricted(ctx), appIDs)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return workspaceBuildsData{}, xerrors.Errorf("get workspace app statuses: %w", err)
|
||||
}
|
||||
|
||||
return workspaceBuildsData{
|
||||
jobs: jobs,
|
||||
templateVersions: templateVersions,
|
||||
@@ -881,6 +897,7 @@ func (api *API) workspaceBuildsData(ctx context.Context, workspaceBuilds []datab
|
||||
metadata: metadata,
|
||||
agents: agents,
|
||||
apps: apps,
|
||||
appStatuses: statuses,
|
||||
scripts: scripts,
|
||||
logSources: logSources,
|
||||
provisionerDaemons: pendingJobProvisioners,
|
||||
@@ -895,6 +912,7 @@ func (api *API) convertWorkspaceBuilds(
|
||||
resourceMetadata []database.WorkspaceResourceMetadatum,
|
||||
resourceAgents []database.WorkspaceAgent,
|
||||
agentApps []database.WorkspaceApp,
|
||||
agentAppStatuses []database.WorkspaceAppStatus,
|
||||
agentScripts []database.WorkspaceAgentScript,
|
||||
agentLogSources []database.WorkspaceAgentLogSource,
|
||||
templateVersions []database.TemplateVersion,
|
||||
@@ -937,6 +955,7 @@ func (api *API) convertWorkspaceBuilds(
|
||||
resourceMetadata,
|
||||
resourceAgents,
|
||||
agentApps,
|
||||
agentAppStatuses,
|
||||
agentScripts,
|
||||
agentLogSources,
|
||||
templateVersion,
|
||||
@@ -960,6 +979,7 @@ func (api *API) convertWorkspaceBuild(
|
||||
resourceMetadata []database.WorkspaceResourceMetadatum,
|
||||
resourceAgents []database.WorkspaceAgent,
|
||||
agentApps []database.WorkspaceApp,
|
||||
agentAppStatuses []database.WorkspaceAppStatus,
|
||||
agentScripts []database.WorkspaceAgentScript,
|
||||
agentLogSources []database.WorkspaceAgentLogSource,
|
||||
templateVersion database.TemplateVersion,
|
||||
@@ -997,6 +1017,10 @@ func (api *API) convertWorkspaceBuild(
|
||||
provisionerDaemonsForThisWorkspaceBuild = append(provisionerDaemonsForThisWorkspaceBuild, provisionerDaemon.ProvisionerDaemon)
|
||||
}
|
||||
matchedProvisioners := db2sdk.MatchedProvisioners(provisionerDaemonsForThisWorkspaceBuild, job.ProvisionerJob.CreatedAt, provisionerdserver.StaleInterval)
|
||||
statusesByAgentID := map[uuid.UUID][]database.WorkspaceAppStatus{}
|
||||
for _, status := range agentAppStatuses {
|
||||
statusesByAgentID[status.AgentID] = append(statusesByAgentID[status.AgentID], status)
|
||||
}
|
||||
|
||||
resources := resourcesByJobID[job.ProvisionerJob.ID]
|
||||
apiResources := make([]codersdk.WorkspaceResource, 0)
|
||||
@@ -1018,9 +1042,10 @@ func (api *API) convertWorkspaceBuild(
|
||||
|
||||
apps := appsByAgentID[agent.ID]
|
||||
scripts := scriptsByAgentID[agent.ID]
|
||||
statuses := statusesByAgentID[agent.ID]
|
||||
logSources := logSourcesByAgentID[agent.ID]
|
||||
apiAgent, err := db2sdk.WorkspaceAgent(
|
||||
api.DERPMap(), *api.TailnetCoordinator.Load(), agent, db2sdk.Apps(apps, agent, workspace.OwnerUsername, workspace), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout,
|
||||
api.DERPMap(), *api.TailnetCoordinator.Load(), agent, db2sdk.Apps(apps, statuses, agent, workspace.OwnerUsername, workspace), convertScripts(scripts), convertLogSources(logSources), api.AgentInactiveDisconnectTimeout,
|
||||
api.DeploymentValues.AgentFallbackTroubleshootingURL.String(),
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
+76
-11
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"cdr.dev/slog"
|
||||
@@ -102,12 +103,18 @@ func (api *API) workspace(rw http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
appStatus := codersdk.WorkspaceAppStatus{}
|
||||
if len(data.appStatuses) > 0 {
|
||||
appStatus = data.appStatuses[0]
|
||||
}
|
||||
|
||||
w, err := convertWorkspace(
|
||||
apiKey.UserID,
|
||||
workspace,
|
||||
data.builds[0],
|
||||
data.templates[0],
|
||||
api.Options.AllowWorkspaceRenames,
|
||||
appStatus,
|
||||
)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@@ -300,12 +307,18 @@ func (api *API) workspaceByOwnerAndName(rw http.ResponseWriter, r *http.Request)
|
||||
return
|
||||
}
|
||||
|
||||
appStatus := codersdk.WorkspaceAppStatus{}
|
||||
if len(data.appStatuses) > 0 {
|
||||
appStatus = data.appStatuses[0]
|
||||
}
|
||||
|
||||
w, err := convertWorkspace(
|
||||
apiKey.UserID,
|
||||
workspace,
|
||||
data.builds[0],
|
||||
data.templates[0],
|
||||
api.Options.AllowWorkspaceRenames,
|
||||
appStatus,
|
||||
)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@@ -731,6 +744,7 @@ func createWorkspace(
|
||||
[]database.WorkspaceResourceMetadatum{},
|
||||
[]database.WorkspaceAgent{},
|
||||
[]database.WorkspaceApp{},
|
||||
[]database.WorkspaceAppStatus{},
|
||||
[]database.WorkspaceAgentScript{},
|
||||
[]database.WorkspaceAgentLogSource{},
|
||||
database.TemplateVersion{},
|
||||
@@ -750,6 +764,7 @@ func createWorkspace(
|
||||
apiBuild,
|
||||
template,
|
||||
api.Options.AllowWorkspaceRenames,
|
||||
codersdk.WorkspaceAppStatus{},
|
||||
)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@@ -1234,12 +1249,18 @@ func (api *API) putWorkspaceDormant(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
aReq.New = newWorkspace
|
||||
|
||||
appStatus := codersdk.WorkspaceAppStatus{}
|
||||
if len(data.appStatuses) > 0 {
|
||||
appStatus = data.appStatuses[0]
|
||||
}
|
||||
|
||||
w, err := convertWorkspace(
|
||||
apiKey.UserID,
|
||||
workspace,
|
||||
data.builds[0],
|
||||
data.templates[0],
|
||||
api.Options.AllowWorkspaceRenames,
|
||||
appStatus,
|
||||
)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
@@ -1792,12 +1813,17 @@ func (api *API) watchWorkspace(
|
||||
return
|
||||
}
|
||||
|
||||
appStatus := codersdk.WorkspaceAppStatus{}
|
||||
if len(data.appStatuses) > 0 {
|
||||
appStatus = data.appStatuses[0]
|
||||
}
|
||||
w, err := convertWorkspace(
|
||||
apiKey.UserID,
|
||||
workspace,
|
||||
data.builds[0],
|
||||
data.templates[0],
|
||||
api.Options.AllowWorkspaceRenames,
|
||||
appStatus,
|
||||
)
|
||||
if err != nil {
|
||||
_ = sendEvent(codersdk.ServerSentEvent{
|
||||
@@ -1908,6 +1934,7 @@ func (api *API) workspaceTimings(rw http.ResponseWriter, r *http.Request) {
|
||||
type workspaceData struct {
|
||||
templates []database.Template
|
||||
builds []codersdk.WorkspaceBuild
|
||||
appStatuses []codersdk.WorkspaceAppStatus
|
||||
allowRenames bool
|
||||
}
|
||||
|
||||
@@ -1923,18 +1950,42 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa
|
||||
templateIDs = append(templateIDs, workspace.TemplateID)
|
||||
}
|
||||
|
||||
templates, err := api.Database.GetTemplatesWithFilter(ctx, database.GetTemplatesWithFilterParams{
|
||||
IDs: templateIDs,
|
||||
var (
|
||||
templates []database.Template
|
||||
builds []database.WorkspaceBuild
|
||||
appStatuses []database.WorkspaceAppStatus
|
||||
eg errgroup.Group
|
||||
)
|
||||
eg.Go(func() (err error) {
|
||||
templates, err = api.Database.GetTemplatesWithFilter(ctx, database.GetTemplatesWithFilterParams{
|
||||
IDs: templateIDs,
|
||||
})
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return xerrors.Errorf("get templates: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return workspaceData{}, xerrors.Errorf("get templates: %w", err)
|
||||
}
|
||||
|
||||
// This query must be run as system restricted to be efficient.
|
||||
// nolint:gocritic
|
||||
builds, err := api.Database.GetLatestWorkspaceBuildsByWorkspaceIDs(dbauthz.AsSystemRestricted(ctx), workspaceIDs)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return workspaceData{}, xerrors.Errorf("get workspace builds: %w", err)
|
||||
eg.Go(func() (err error) {
|
||||
// This query must be run as system restricted to be efficient.
|
||||
// nolint:gocritic
|
||||
builds, err = api.Database.GetLatestWorkspaceBuildsByWorkspaceIDs(dbauthz.AsSystemRestricted(ctx), workspaceIDs)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return xerrors.Errorf("get workspace builds: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
eg.Go(func() (err error) {
|
||||
// This query must be run as system restricted to be efficient.
|
||||
// nolint:gocritic
|
||||
appStatuses, err = api.Database.GetLatestWorkspaceAppStatusesByWorkspaceIDs(dbauthz.AsSystemRestricted(ctx), workspaceIDs)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return xerrors.Errorf("get workspace app statuses: %w", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
err := eg.Wait()
|
||||
if err != nil {
|
||||
return workspaceData{}, err
|
||||
}
|
||||
|
||||
data, err := api.workspaceBuildsData(ctx, builds)
|
||||
@@ -1950,6 +2001,7 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa
|
||||
data.metadata,
|
||||
data.agents,
|
||||
data.apps,
|
||||
data.appStatuses,
|
||||
data.scripts,
|
||||
data.logSources,
|
||||
data.templateVersions,
|
||||
@@ -1961,6 +2013,7 @@ func (api *API) workspaceData(ctx context.Context, workspaces []database.Workspa
|
||||
|
||||
return workspaceData{
|
||||
templates: templates,
|
||||
appStatuses: db2sdk.WorkspaceAppStatuses(appStatuses),
|
||||
builds: apiBuilds,
|
||||
allowRenames: api.Options.AllowWorkspaceRenames,
|
||||
}, nil
|
||||
@@ -1975,6 +2028,10 @@ func convertWorkspaces(requesterID uuid.UUID, workspaces []database.Workspace, d
|
||||
for _, template := range data.templates {
|
||||
templateByID[template.ID] = template
|
||||
}
|
||||
appStatusesByWorkspaceID := map[uuid.UUID]codersdk.WorkspaceAppStatus{}
|
||||
for _, appStatus := range data.appStatuses {
|
||||
appStatusesByWorkspaceID[appStatus.WorkspaceID] = appStatus
|
||||
}
|
||||
|
||||
apiWorkspaces := make([]codersdk.Workspace, 0, len(workspaces))
|
||||
for _, workspace := range workspaces {
|
||||
@@ -1991,6 +2048,7 @@ func convertWorkspaces(requesterID uuid.UUID, workspaces []database.Workspace, d
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
appStatus := appStatusesByWorkspaceID[workspace.ID]
|
||||
|
||||
w, err := convertWorkspace(
|
||||
requesterID,
|
||||
@@ -1998,6 +2056,7 @@ func convertWorkspaces(requesterID uuid.UUID, workspaces []database.Workspace, d
|
||||
build,
|
||||
template,
|
||||
data.allowRenames,
|
||||
appStatus,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("convert workspace: %w", err)
|
||||
@@ -2014,6 +2073,7 @@ func convertWorkspace(
|
||||
workspaceBuild codersdk.WorkspaceBuild,
|
||||
template database.Template,
|
||||
allowRenames bool,
|
||||
latestAppStatus codersdk.WorkspaceAppStatus,
|
||||
) (codersdk.Workspace, error) {
|
||||
if requesterID == uuid.Nil {
|
||||
return codersdk.Workspace{}, xerrors.Errorf("developer error: requesterID cannot be uuid.Nil!")
|
||||
@@ -2057,6 +2117,10 @@ func convertWorkspace(
|
||||
// Only show favorite status if you own the workspace.
|
||||
requesterFavorite := workspace.OwnerID == requesterID && workspace.Favorite
|
||||
|
||||
appStatus := &latestAppStatus
|
||||
if latestAppStatus.ID == uuid.Nil {
|
||||
appStatus = nil
|
||||
}
|
||||
return codersdk.Workspace{
|
||||
ID: workspace.ID,
|
||||
CreatedAt: workspace.CreatedAt,
|
||||
@@ -2068,6 +2132,7 @@ func convertWorkspace(
|
||||
OrganizationName: workspace.OrganizationName,
|
||||
TemplateID: workspace.TemplateID,
|
||||
LatestBuild: workspaceBuild,
|
||||
LatestAppStatus: appStatus,
|
||||
TemplateName: workspace.TemplateName,
|
||||
TemplateIcon: workspace.TemplateIcon,
|
||||
TemplateDisplayName: workspace.TemplateDisplayName,
|
||||
|
||||
@@ -55,6 +55,7 @@ const (
|
||||
WorkspaceEventKindAgentFirstLogs WorkspaceEventKind = "agt_first_logs"
|
||||
WorkspaceEventKindAgentLogsOverflow WorkspaceEventKind = "agt_logs_overflow"
|
||||
WorkspaceEventKindAgentTimeout WorkspaceEventKind = "agt_timeout"
|
||||
WorkspaceEventKindAgentAppStatusUpdate WorkspaceEventKind = "agt_app_status_update"
|
||||
)
|
||||
|
||||
func (w *WorkspaceEvent) Validate() error {
|
||||
|
||||
@@ -581,6 +581,28 @@ func (c *Client) PatchLogs(ctx context.Context, req PatchLogs) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// PatchAppStatus updates the status of a workspace app.
|
||||
type PatchAppStatus struct {
|
||||
AppSlug string `json:"app_slug"`
|
||||
NeedsUserAttention bool `json:"needs_user_attention"`
|
||||
State codersdk.WorkspaceAppStatusState `json:"state"`
|
||||
Message string `json:"message"`
|
||||
URI string `json:"uri"`
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
func (c *Client) PatchAppStatus(ctx context.Context, req PatchAppStatus) error {
|
||||
res, err := c.SDK.Request(ctx, http.MethodPatch, "/api/v2/workspaceagents/me/app-status", req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return codersdk.ReadBodyAsError(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PostLogSourceRequest struct {
|
||||
// ID is a unique identifier for the log source.
|
||||
// It is scoped to a workspace agent, and can be statically
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package codersdk
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -13,6 +15,14 @@ const (
|
||||
WorkspaceAppHealthUnhealthy WorkspaceAppHealth = "unhealthy"
|
||||
)
|
||||
|
||||
type WorkspaceAppStatusState string
|
||||
|
||||
const (
|
||||
WorkspaceAppStatusStateWorking WorkspaceAppStatusState = "working"
|
||||
WorkspaceAppStatusStateComplete WorkspaceAppStatusState = "complete"
|
||||
WorkspaceAppStatusStateFailure WorkspaceAppStatusState = "failure"
|
||||
)
|
||||
|
||||
var MapWorkspaceAppHealths = map[WorkspaceAppHealth]struct{}{
|
||||
WorkspaceAppHealthDisabled: {},
|
||||
WorkspaceAppHealthInitializing: {},
|
||||
@@ -75,6 +85,9 @@ type WorkspaceApp struct {
|
||||
Health WorkspaceAppHealth `json:"health"`
|
||||
Hidden bool `json:"hidden"`
|
||||
OpenIn WorkspaceAppOpenIn `json:"open_in"`
|
||||
|
||||
// Statuses is a list of statuses for the app.
|
||||
Statuses []WorkspaceAppStatus `json:"statuses"`
|
||||
}
|
||||
|
||||
type Healthcheck struct {
|
||||
@@ -85,3 +98,20 @@ type Healthcheck struct {
|
||||
// Threshold specifies the number of consecutive failed health checks before returning "unhealthy".
|
||||
Threshold int32 `json:"threshold"`
|
||||
}
|
||||
|
||||
type WorkspaceAppStatus struct {
|
||||
ID uuid.UUID `json:"id" format:"uuid"`
|
||||
CreatedAt time.Time `json:"created_at" format:"date-time"`
|
||||
WorkspaceID uuid.UUID `json:"workspace_id" format:"uuid"`
|
||||
AgentID uuid.UUID `json:"agent_id" format:"uuid"`
|
||||
AppID uuid.UUID `json:"app_id" format:"uuid"`
|
||||
State WorkspaceAppStatusState `json:"state"`
|
||||
NeedsUserAttention bool `json:"needs_user_attention"`
|
||||
Message string `json:"message"`
|
||||
// URI is the URI of the resource that the status is for.
|
||||
// e.g. https://github.com/org/repo/pull/123
|
||||
// e.g. file:///path/to/file
|
||||
URI string `json:"uri"`
|
||||
// Icon is an external URL to an icon that will be rendered in the UI.
|
||||
Icon string `json:"icon"`
|
||||
}
|
||||
|
||||
+22
-21
@@ -26,27 +26,28 @@ const (
|
||||
// Workspace is a deployment of a template. It references a specific
|
||||
// version and can be updated.
|
||||
type Workspace struct {
|
||||
ID uuid.UUID `json:"id" format:"uuid"`
|
||||
CreatedAt time.Time `json:"created_at" format:"date-time"`
|
||||
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
|
||||
OwnerID uuid.UUID `json:"owner_id" format:"uuid"`
|
||||
OwnerName string `json:"owner_name"`
|
||||
OwnerAvatarURL string `json:"owner_avatar_url"`
|
||||
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
|
||||
OrganizationName string `json:"organization_name"`
|
||||
TemplateID uuid.UUID `json:"template_id" format:"uuid"`
|
||||
TemplateName string `json:"template_name"`
|
||||
TemplateDisplayName string `json:"template_display_name"`
|
||||
TemplateIcon string `json:"template_icon"`
|
||||
TemplateAllowUserCancelWorkspaceJobs bool `json:"template_allow_user_cancel_workspace_jobs"`
|
||||
TemplateActiveVersionID uuid.UUID `json:"template_active_version_id" format:"uuid"`
|
||||
TemplateRequireActiveVersion bool `json:"template_require_active_version"`
|
||||
LatestBuild WorkspaceBuild `json:"latest_build"`
|
||||
Outdated bool `json:"outdated"`
|
||||
Name string `json:"name"`
|
||||
AutostartSchedule *string `json:"autostart_schedule,omitempty"`
|
||||
TTLMillis *int64 `json:"ttl_ms,omitempty"`
|
||||
LastUsedAt time.Time `json:"last_used_at" format:"date-time"`
|
||||
ID uuid.UUID `json:"id" format:"uuid"`
|
||||
CreatedAt time.Time `json:"created_at" format:"date-time"`
|
||||
UpdatedAt time.Time `json:"updated_at" format:"date-time"`
|
||||
OwnerID uuid.UUID `json:"owner_id" format:"uuid"`
|
||||
OwnerName string `json:"owner_name"`
|
||||
OwnerAvatarURL string `json:"owner_avatar_url"`
|
||||
OrganizationID uuid.UUID `json:"organization_id" format:"uuid"`
|
||||
OrganizationName string `json:"organization_name"`
|
||||
TemplateID uuid.UUID `json:"template_id" format:"uuid"`
|
||||
TemplateName string `json:"template_name"`
|
||||
TemplateDisplayName string `json:"template_display_name"`
|
||||
TemplateIcon string `json:"template_icon"`
|
||||
TemplateAllowUserCancelWorkspaceJobs bool `json:"template_allow_user_cancel_workspace_jobs"`
|
||||
TemplateActiveVersionID uuid.UUID `json:"template_active_version_id" format:"uuid"`
|
||||
TemplateRequireActiveVersion bool `json:"template_require_active_version"`
|
||||
LatestBuild WorkspaceBuild `json:"latest_build"`
|
||||
LatestAppStatus *WorkspaceAppStatus `json:"latest_app_status"`
|
||||
Outdated bool `json:"outdated"`
|
||||
Name string `json:"name"`
|
||||
AutostartSchedule *string `json:"autostart_schedule,omitempty"`
|
||||
TTLMillis *int64 `json:"ttl_ms,omitempty"`
|
||||
LastUsedAt time.Time `json:"last_used_at" format:"date-time"`
|
||||
|
||||
// DeletingAt indicates the time at which the workspace will be permanently deleted.
|
||||
// A workspace is eligible for deletion if it is dormant (a non-nil dormant_at value)
|
||||
|
||||
Generated
+72
@@ -180,6 +180,64 @@ curl -X POST http://coder-server:8080/api/v2/workspaceagents/google-instance-ide
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Patch workspace agent app status
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X PATCH http://coder-server:8080/api/v2/workspaceagents/me/app-status \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`PATCH /workspaceagents/me/app-status`
|
||||
|
||||
> Body parameter
|
||||
|
||||
```json
|
||||
{
|
||||
"app_slug": "string",
|
||||
"icon": "string",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | In | Type | Required | Description |
|
||||
|--------|------|--------------------------------------------------------------|----------|-------------|
|
||||
| `body` | body | [agentsdk.PatchAppStatus](schemas.md#agentsdkpatchappstatus) | true | app status |
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": "string",
|
||||
"message": "string",
|
||||
"validations": [
|
||||
{
|
||||
"detail": "string",
|
||||
"field": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
|--------|---------------------------------------------------------|-------------|--------------------------------------------------|
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.Response](schemas.md#codersdkresponse) |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get workspace agent external auth
|
||||
|
||||
### Code samples
|
||||
@@ -455,6 +513,20 @@ curl -X GET http://coder-server:8080/api/v2/workspaceagents/{workspaceagent} \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
|
||||
Generated
+112
@@ -100,6 +100,20 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -314,6 +328,20 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild} \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -643,6 +671,20 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/res
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -770,6 +812,17 @@ Status Code **200**
|
||||
| `»»» open_in` | [codersdk.WorkspaceAppOpenIn](schemas.md#codersdkworkspaceappopenin) | false | | |
|
||||
| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | |
|
||||
| `»»» slug` | string | false | | Slug is a unique identifier within the agent. |
|
||||
| `»»» statuses` | array | false | | Statuses is a list of statuses for the app. |
|
||||
| `»»»» agent_id` | string(uuid) | false | | |
|
||||
| `»»»» app_id` | string(uuid) | false | | |
|
||||
| `»»»» created_at` | string(date-time) | false | | |
|
||||
| `»»»» icon` | string | false | | Icon is an external URL to an icon that will be rendered in the UI. |
|
||||
| `»»»» id` | string(uuid) | false | | |
|
||||
| `»»»» message` | string | false | | |
|
||||
| `»»»» needs_user_attention` | boolean | false | | |
|
||||
| `»»»» state` | [codersdk.WorkspaceAppStatusState](schemas.md#codersdkworkspaceappstatusstate) | false | | |
|
||||
| `»»»» uri` | string | false | | Uri is the URI of the resource that the status is for. e.g. https://github.com/org/repo/pull/123 e.g. file:///path/to/file |
|
||||
| `»»»» workspace_id` | string(uuid) | false | | |
|
||||
| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
|
||||
| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
|
||||
| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
|
||||
@@ -851,6 +904,9 @@ Status Code **200**
|
||||
| `sharing_level` | `owner` |
|
||||
| `sharing_level` | `authenticated` |
|
||||
| `sharing_level` | `public` |
|
||||
| `state` | `working` |
|
||||
| `state` | `complete` |
|
||||
| `state` | `failure` |
|
||||
| `lifecycle_state` | `created` |
|
||||
| `lifecycle_state` | `starting` |
|
||||
| `lifecycle_state` | `start_timeout` |
|
||||
@@ -970,6 +1026,20 @@ curl -X GET http://coder-server:8080/api/v2/workspacebuilds/{workspacebuild}/sta
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -1257,6 +1327,20 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -1440,6 +1524,17 @@ Status Code **200**
|
||||
| `»»»» open_in` | [codersdk.WorkspaceAppOpenIn](schemas.md#codersdkworkspaceappopenin) | false | | |
|
||||
| `»»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | |
|
||||
| `»»»» slug` | string | false | | Slug is a unique identifier within the agent. |
|
||||
| `»»»» statuses` | array | false | | Statuses is a list of statuses for the app. |
|
||||
| `»»»»» agent_id` | string(uuid) | false | | |
|
||||
| `»»»»» app_id` | string(uuid) | false | | |
|
||||
| `»»»»» created_at` | string(date-time) | false | | |
|
||||
| `»»»»» icon` | string | false | | Icon is an external URL to an icon that will be rendered in the UI. |
|
||||
| `»»»»» id` | string(uuid) | false | | |
|
||||
| `»»»»» message` | string | false | | |
|
||||
| `»»»»» needs_user_attention` | boolean | false | | |
|
||||
| `»»»»» state` | [codersdk.WorkspaceAppStatusState](schemas.md#codersdkworkspaceappstatusstate) | false | | |
|
||||
| `»»»»» uri` | string | false | | Uri is the URI of the resource that the status is for. e.g. https://github.com/org/repo/pull/123 e.g. file:///path/to/file |
|
||||
| `»»»»» workspace_id` | string(uuid) | false | | |
|
||||
| `»»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
|
||||
| `»»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
|
||||
| `»»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
|
||||
@@ -1544,6 +1639,9 @@ Status Code **200**
|
||||
| `sharing_level` | `owner` |
|
||||
| `sharing_level` | `authenticated` |
|
||||
| `sharing_level` | `public` |
|
||||
| `state` | `working` |
|
||||
| `state` | `complete` |
|
||||
| `state` | `failure` |
|
||||
| `lifecycle_state` | `created` |
|
||||
| `lifecycle_state` | `starting` |
|
||||
| `lifecycle_state` | `start_timeout` |
|
||||
@@ -1699,6 +1797,20 @@ curl -X POST http://coder-server:8080/api/v2/workspaces/{workspace}/builds \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
|
||||
Generated
+199
-30
@@ -118,6 +118,30 @@
|
||||
| `level` | [codersdk.LogLevel](#codersdkloglevel) | false | | |
|
||||
| `output` | string | false | | |
|
||||
|
||||
## agentsdk.PatchAppStatus
|
||||
|
||||
```json
|
||||
{
|
||||
"app_slug": "string",
|
||||
"icon": "string",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------|----------------------------------------------------------------------|----------|--------------|-------------|
|
||||
| `app_slug` | string | false | | |
|
||||
| `icon` | string | false | | |
|
||||
| `message` | string | false | | |
|
||||
| `needs_user_attention` | boolean | false | | |
|
||||
| `state` | [codersdk.WorkspaceAppStatusState](#codersdkworkspaceappstatusstate) | false | | |
|
||||
| `uri` | string | false | | |
|
||||
|
||||
## agentsdk.PatchLogs
|
||||
|
||||
```json
|
||||
@@ -7557,6 +7581,18 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -7631,6 +7667,20 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -7759,36 +7809,37 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|---------------------------------------------|--------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `allow_renames` | boolean | false | | |
|
||||
| `automatic_updates` | [codersdk.AutomaticUpdates](#codersdkautomaticupdates) | false | | |
|
||||
| `autostart_schedule` | string | false | | |
|
||||
| `created_at` | string | false | | |
|
||||
| `deleting_at` | string | false | | Deleting at indicates the time at which the workspace will be permanently deleted. A workspace is eligible for deletion if it is dormant (a non-nil dormant_at value) and a value has been specified for time_til_dormant_autodelete on its template. |
|
||||
| `dormant_at` | string | false | | Dormant at being non-nil indicates a workspace that is dormant. A dormant workspace is no longer accessible must be activated. It is subject to deletion if it breaches the duration of the time_til_ field on its template. |
|
||||
| `favorite` | boolean | false | | |
|
||||
| `health` | [codersdk.WorkspaceHealth](#codersdkworkspacehealth) | false | | Health shows the health of the workspace and information about what is causing an unhealthy status. |
|
||||
| `id` | string | false | | |
|
||||
| `last_used_at` | string | false | | |
|
||||
| `latest_build` | [codersdk.WorkspaceBuild](#codersdkworkspacebuild) | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `next_start_at` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_name` | string | false | | |
|
||||
| `outdated` | boolean | false | | |
|
||||
| `owner_avatar_url` | string | false | | |
|
||||
| `owner_id` | string | false | | |
|
||||
| `owner_name` | string | false | | |
|
||||
| `template_active_version_id` | string | false | | |
|
||||
| `template_allow_user_cancel_workspace_jobs` | boolean | false | | |
|
||||
| `template_display_name` | string | false | | |
|
||||
| `template_icon` | string | false | | |
|
||||
| `template_id` | string | false | | |
|
||||
| `template_name` | string | false | | |
|
||||
| `template_require_active_version` | boolean | false | | |
|
||||
| `ttl_ms` | integer | false | | |
|
||||
| `updated_at` | string | false | | |
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|---------------------------------------------|------------------------------------------------------------|----------|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `allow_renames` | boolean | false | | |
|
||||
| `automatic_updates` | [codersdk.AutomaticUpdates](#codersdkautomaticupdates) | false | | |
|
||||
| `autostart_schedule` | string | false | | |
|
||||
| `created_at` | string | false | | |
|
||||
| `deleting_at` | string | false | | Deleting at indicates the time at which the workspace will be permanently deleted. A workspace is eligible for deletion if it is dormant (a non-nil dormant_at value) and a value has been specified for time_til_dormant_autodelete on its template. |
|
||||
| `dormant_at` | string | false | | Dormant at being non-nil indicates a workspace that is dormant. A dormant workspace is no longer accessible must be activated. It is subject to deletion if it breaches the duration of the time_til_ field on its template. |
|
||||
| `favorite` | boolean | false | | |
|
||||
| `health` | [codersdk.WorkspaceHealth](#codersdkworkspacehealth) | false | | Health shows the health of the workspace and information about what is causing an unhealthy status. |
|
||||
| `id` | string | false | | |
|
||||
| `last_used_at` | string | false | | |
|
||||
| `latest_app_status` | [codersdk.WorkspaceAppStatus](#codersdkworkspaceappstatus) | false | | |
|
||||
| `latest_build` | [codersdk.WorkspaceBuild](#codersdkworkspacebuild) | false | | |
|
||||
| `name` | string | false | | |
|
||||
| `next_start_at` | string | false | | |
|
||||
| `organization_id` | string | false | | |
|
||||
| `organization_name` | string | false | | |
|
||||
| `outdated` | boolean | false | | |
|
||||
| `owner_avatar_url` | string | false | | |
|
||||
| `owner_id` | string | false | | |
|
||||
| `owner_name` | string | false | | |
|
||||
| `template_active_version_id` | string | false | | |
|
||||
| `template_allow_user_cancel_workspace_jobs` | boolean | false | | |
|
||||
| `template_display_name` | string | false | | |
|
||||
| `template_icon` | string | false | | |
|
||||
| `template_id` | string | false | | |
|
||||
| `template_name` | string | false | | |
|
||||
| `template_require_active_version` | boolean | false | | |
|
||||
| `ttl_ms` | integer | false | | |
|
||||
| `updated_at` | string | false | | |
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
@@ -7819,6 +7870,20 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -8332,6 +8397,20 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -8353,6 +8432,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `open_in` | [codersdk.WorkspaceAppOpenIn](#codersdkworkspaceappopenin) | false | | |
|
||||
| `sharing_level` | [codersdk.WorkspaceAppSharingLevel](#codersdkworkspaceappsharinglevel) | false | | |
|
||||
| `slug` | string | false | | Slug is a unique identifier within the agent. |
|
||||
| `statuses` | array of [codersdk.WorkspaceAppStatus](#codersdkworkspaceappstatus) | false | | Statuses is a list of statuses for the app. |
|
||||
| `subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
|
||||
| `subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
|
||||
| `url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
|
||||
@@ -8413,6 +8493,54 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
| `authenticated` |
|
||||
| `public` |
|
||||
|
||||
## codersdk.WorkspaceAppStatus
|
||||
|
||||
```json
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
|------------------------|----------------------------------------------------------------------|----------|--------------|----------------------------------------------------------------------------------------------------------------------------|
|
||||
| `agent_id` | string | false | | |
|
||||
| `app_id` | string | false | | |
|
||||
| `created_at` | string | false | | |
|
||||
| `icon` | string | false | | Icon is an external URL to an icon that will be rendered in the UI. |
|
||||
| `id` | string | false | | |
|
||||
| `message` | string | false | | |
|
||||
| `needs_user_attention` | boolean | false | | |
|
||||
| `state` | [codersdk.WorkspaceAppStatusState](#codersdkworkspaceappstatusstate) | false | | |
|
||||
| `uri` | string | false | | Uri is the URI of the resource that the status is for. e.g. https://github.com/org/repo/pull/123 e.g. file:///path/to/file |
|
||||
| `workspace_id` | string | false | | |
|
||||
|
||||
## codersdk.WorkspaceAppStatusState
|
||||
|
||||
```json
|
||||
"working"
|
||||
```
|
||||
|
||||
### Properties
|
||||
|
||||
#### Enumerated Values
|
||||
|
||||
| Value |
|
||||
|------------|
|
||||
| `working` |
|
||||
| `complete` |
|
||||
| `failure` |
|
||||
|
||||
## codersdk.WorkspaceBuild
|
||||
|
||||
```json
|
||||
@@ -8490,6 +8618,20 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -8890,6 +9032,20 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -9089,6 +9245,18 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -9159,6 +9327,7 @@ If the schedule is empty, the user will be updated to use the default schedule.|
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
|
||||
Generated
+56
@@ -2284,6 +2284,20 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/d
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -2411,6 +2425,17 @@ Status Code **200**
|
||||
| `»»» open_in` | [codersdk.WorkspaceAppOpenIn](schemas.md#codersdkworkspaceappopenin) | false | | |
|
||||
| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | |
|
||||
| `»»» slug` | string | false | | Slug is a unique identifier within the agent. |
|
||||
| `»»» statuses` | array | false | | Statuses is a list of statuses for the app. |
|
||||
| `»»»» agent_id` | string(uuid) | false | | |
|
||||
| `»»»» app_id` | string(uuid) | false | | |
|
||||
| `»»»» created_at` | string(date-time) | false | | |
|
||||
| `»»»» icon` | string | false | | Icon is an external URL to an icon that will be rendered in the UI. |
|
||||
| `»»»» id` | string(uuid) | false | | |
|
||||
| `»»»» message` | string | false | | |
|
||||
| `»»»» needs_user_attention` | boolean | false | | |
|
||||
| `»»»» state` | [codersdk.WorkspaceAppStatusState](schemas.md#codersdkworkspaceappstatusstate) | false | | |
|
||||
| `»»»» uri` | string | false | | Uri is the URI of the resource that the status is for. e.g. https://github.com/org/repo/pull/123 e.g. file:///path/to/file |
|
||||
| `»»»» workspace_id` | string(uuid) | false | | |
|
||||
| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
|
||||
| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
|
||||
| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
|
||||
@@ -2492,6 +2517,9 @@ Status Code **200**
|
||||
| `sharing_level` | `owner` |
|
||||
| `sharing_level` | `authenticated` |
|
||||
| `sharing_level` | `public` |
|
||||
| `state` | `working` |
|
||||
| `state` | `complete` |
|
||||
| `state` | `failure` |
|
||||
| `lifecycle_state` | `created` |
|
||||
| `lifecycle_state` | `starting` |
|
||||
| `lifecycle_state` | `start_timeout` |
|
||||
@@ -2777,6 +2805,20 @@ curl -X GET http://coder-server:8080/api/v2/templateversions/{templateversion}/r
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -2904,6 +2946,17 @@ Status Code **200**
|
||||
| `»»» open_in` | [codersdk.WorkspaceAppOpenIn](schemas.md#codersdkworkspaceappopenin) | false | | |
|
||||
| `»»» sharing_level` | [codersdk.WorkspaceAppSharingLevel](schemas.md#codersdkworkspaceappsharinglevel) | false | | |
|
||||
| `»»» slug` | string | false | | Slug is a unique identifier within the agent. |
|
||||
| `»»» statuses` | array | false | | Statuses is a list of statuses for the app. |
|
||||
| `»»»» agent_id` | string(uuid) | false | | |
|
||||
| `»»»» app_id` | string(uuid) | false | | |
|
||||
| `»»»» created_at` | string(date-time) | false | | |
|
||||
| `»»»» icon` | string | false | | Icon is an external URL to an icon that will be rendered in the UI. |
|
||||
| `»»»» id` | string(uuid) | false | | |
|
||||
| `»»»» message` | string | false | | |
|
||||
| `»»»» needs_user_attention` | boolean | false | | |
|
||||
| `»»»» state` | [codersdk.WorkspaceAppStatusState](schemas.md#codersdkworkspaceappstatusstate) | false | | |
|
||||
| `»»»» uri` | string | false | | Uri is the URI of the resource that the status is for. e.g. https://github.com/org/repo/pull/123 e.g. file:///path/to/file |
|
||||
| `»»»» workspace_id` | string(uuid) | false | | |
|
||||
| `»»» subdomain` | boolean | false | | Subdomain denotes whether the app should be accessed via a path on the `coder server` or via a hostname-based dev URL. If this is set to true and there is no app wildcard configured on the server, the app will not be accessible in the UI. |
|
||||
| `»»» subdomain_name` | string | false | | Subdomain name is the application domain exposed on the `coder server`. |
|
||||
| `»»» url` | string | false | | URL is the address being proxied to inside the workspace. If external is specified, this will be opened on the client. |
|
||||
@@ -2985,6 +3038,9 @@ Status Code **200**
|
||||
| `sharing_level` | `owner` |
|
||||
| `sharing_level` | `authenticated` |
|
||||
| `sharing_level` | `public` |
|
||||
| `state` | `working` |
|
||||
| `state` | `complete` |
|
||||
| `state` | `failure` |
|
||||
| `lifecycle_state` | `created` |
|
||||
| `lifecycle_state` | `starting` |
|
||||
| `lifecycle_state` | `start_timeout` |
|
||||
|
||||
Generated
+143
@@ -67,6 +67,18 @@ of the template will be used.
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -141,6 +153,20 @@ of the template will be used.
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -317,6 +343,18 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -391,6 +429,20 @@ curl -X GET http://coder-server:8080/api/v2/users/{user}/workspace/{workspacenam
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -591,6 +643,18 @@ of the template will be used.
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -665,6 +729,20 @@ of the template will be used.
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -844,6 +922,18 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -914,6 +1004,7 @@ curl -X GET http://coder-server:8080/api/v2/workspaces \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -1091,6 +1182,18 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -1165,6 +1268,20 @@ curl -X GET http://coder-server:8080/api/v2/workspaces/{workspace} \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
@@ -1457,6 +1574,18 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \
|
||||
},
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"last_used_at": "2019-08-24T14:15:22Z",
|
||||
"latest_app_status": {
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
},
|
||||
"latest_build": {
|
||||
"build_number": 0,
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
@@ -1531,6 +1660,20 @@ curl -X PUT http://coder-server:8080/api/v2/workspaces/{workspace}/dormant \
|
||||
"open_in": "slim-window",
|
||||
"sharing_level": "owner",
|
||||
"slug": "string",
|
||||
"statuses": [
|
||||
{
|
||||
"agent_id": "2b1e3b65-2c04-4fa2-a2d7-467901e98978",
|
||||
"app_id": "affd1d10-9538-4fc8-9e0b-4594a28c1335",
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"message": "string",
|
||||
"needs_user_attention": true,
|
||||
"state": "working",
|
||||
"uri": "string",
|
||||
"workspace_id": "0967198e-ec7b-4c6b-b4d3-f71244cadbe9"
|
||||
}
|
||||
],
|
||||
"subdomain": true,
|
||||
"subdomain_name": "string",
|
||||
"url": "string"
|
||||
|
||||
Generated
+25
@@ -3060,6 +3060,7 @@ export interface Workspace {
|
||||
readonly template_active_version_id: string;
|
||||
readonly template_require_active_version: boolean;
|
||||
readonly latest_build: WorkspaceBuild;
|
||||
readonly latest_app_status: WorkspaceAppStatus | null;
|
||||
readonly outdated: boolean;
|
||||
readonly name: string;
|
||||
readonly autostart_schedule?: string;
|
||||
@@ -3307,6 +3308,7 @@ export interface WorkspaceApp {
|
||||
readonly health: WorkspaceAppHealth;
|
||||
readonly hidden: boolean;
|
||||
readonly open_in: WorkspaceAppOpenIn;
|
||||
readonly statuses: readonly WorkspaceAppStatus[];
|
||||
}
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
@@ -3337,6 +3339,29 @@ export const WorkspaceAppSharingLevels: WorkspaceAppSharingLevel[] = [
|
||||
"public",
|
||||
];
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
export interface WorkspaceAppStatus {
|
||||
readonly id: string;
|
||||
readonly created_at: string;
|
||||
readonly workspace_id: string;
|
||||
readonly agent_id: string;
|
||||
readonly app_id: string;
|
||||
readonly state: WorkspaceAppStatusState;
|
||||
readonly needs_user_attention: boolean;
|
||||
readonly message: string;
|
||||
readonly uri: string;
|
||||
readonly icon: string;
|
||||
}
|
||||
|
||||
// From codersdk/workspaceapps.go
|
||||
export type WorkspaceAppStatusState = "complete" | "failure" | "working";
|
||||
|
||||
export const WorkspaceAppStatusStates: WorkspaceAppStatusState[] = [
|
||||
"complete",
|
||||
"failure",
|
||||
"working",
|
||||
];
|
||||
|
||||
// From codersdk/workspacebuilds.go
|
||||
export interface WorkspaceBuild {
|
||||
readonly id: string;
|
||||
|
||||
@@ -913,6 +913,7 @@ export const MockWorkspaceApp: TypesGen.WorkspaceApp = {
|
||||
},
|
||||
hidden: false,
|
||||
open_in: "slim-window",
|
||||
statuses: [],
|
||||
};
|
||||
|
||||
export const MockWorkspaceAgentLogSource: TypesGen.WorkspaceAgentLogSource = {
|
||||
@@ -1371,6 +1372,7 @@ export const MockWorkspace: TypesGen.Workspace = {
|
||||
healthy: true,
|
||||
failing_agents: [],
|
||||
},
|
||||
latest_app_status: null,
|
||||
automatic_updates: "never",
|
||||
allow_renames: true,
|
||||
favorite: false,
|
||||
|
||||
Reference in New Issue
Block a user