mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add an endpoint to manually pause a coder task (#21889)
Closes https://github.com/coder/internal/issues/1261. This pull request adds an endpoint to pause coder tasks by stopping the underlying workspace. * Instead of `POST /api/v2/tasks/{user}/{task}/pause`, the endpoint is currently experimental. * We do not currently set the build reason to `task_manual_pause`, because build reasons are currently only used on stop transitions.
This commit is contained in:
@@ -1244,3 +1244,63 @@ func (api *API) postWorkspaceAgentTaskLogSnapshot(rw http.ResponseWriter, r *htt
|
||||
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// @Summary Pause task
|
||||
// @ID pause-task
|
||||
// @Security CoderSessionToken
|
||||
// @Accept json
|
||||
// @Tags Tasks
|
||||
// @Param user path string true "Username, user ID, or 'me' for the authenticated user"
|
||||
// @Param task path string true "Task ID" format(uuid)
|
||||
// @Success 202 {object} codersdk.PauseTaskResponse
|
||||
// @Router /tasks/{user}/{task}/pause [post]
|
||||
func (api *API) pauseTask(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
apiKey = httpmw.APIKey(r)
|
||||
task = httpmw.TaskParam(r)
|
||||
)
|
||||
|
||||
if !task.WorkspaceID.Valid {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Task does not have a workspace.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
workspace, err := api.Database.GetWorkspaceByID(ctx, task.WorkspaceID.UUID)
|
||||
if err != nil {
|
||||
if httpapi.Is404Error(err) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching task workspace.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
buildReq := codersdk.CreateWorkspaceBuildRequest{
|
||||
Transition: codersdk.WorkspaceTransitionStop,
|
||||
Reason: codersdk.CreateWorkspaceBuildReasonTaskManualPause,
|
||||
}
|
||||
build, err := api.postWorkspaceBuildsInternal(
|
||||
ctx,
|
||||
apiKey,
|
||||
workspace,
|
||||
buildReq,
|
||||
func(action policy.Action, object rbac.Objecter) bool {
|
||||
return api.Authorize(r, action, object)
|
||||
},
|
||||
audit.WorkspaceBuildBaggageFromRequest(r),
|
||||
)
|
||||
if err != nil {
|
||||
httperror.WriteWorkspaceBuildError(ctx, rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusAccepted, codersdk.PauseTaskResponse{
|
||||
WorkspaceBuild: &build,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
agentapisdk "github.com/coder/agentapi-sdk-go"
|
||||
"github.com/coder/coder/v2/agent"
|
||||
@@ -26,11 +27,14 @@ import (
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/database/dbfake"
|
||||
"github.com/coder/coder/v2/coderd/database/dbgen"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/database/pubsub"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/notifications"
|
||||
"github.com/coder/coder/v2/coderd/notifications/notificationstest"
|
||||
"github.com/coder/coder/v2/coderd/rbac"
|
||||
"github.com/coder/coder/v2/coderd/rbac/policy"
|
||||
"github.com/coder/coder/v2/coderd/util/slice"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||
@@ -100,6 +104,36 @@ func createTaskInState(db database.Store, ownerSubject rbac.Subject, ownerOrgID,
|
||||
}
|
||||
}
|
||||
|
||||
type aiTaskStoreWrapper struct {
|
||||
database.Store
|
||||
getWorkspaceByID func(ctx context.Context, id uuid.UUID) (database.Workspace, error)
|
||||
insertWorkspaceBuild func(ctx context.Context, arg database.InsertWorkspaceBuildParams) error
|
||||
}
|
||||
|
||||
func (s aiTaskStoreWrapper) GetWorkspaceByID(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
|
||||
if s.getWorkspaceByID != nil {
|
||||
return s.getWorkspaceByID(ctx, id)
|
||||
}
|
||||
return s.Store.GetWorkspaceByID(ctx, id)
|
||||
}
|
||||
|
||||
func (s aiTaskStoreWrapper) InsertWorkspaceBuild(ctx context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
if s.insertWorkspaceBuild != nil {
|
||||
return s.insertWorkspaceBuild(ctx, arg)
|
||||
}
|
||||
return s.Store.InsertWorkspaceBuild(ctx, arg)
|
||||
}
|
||||
|
||||
func (s aiTaskStoreWrapper) InTx(fn func(database.Store) error, opts *database.TxOptions) error {
|
||||
return s.Store.InTx(func(tx database.Store) error {
|
||||
return fn(aiTaskStoreWrapper{
|
||||
Store: tx,
|
||||
getWorkspaceByID: s.getWorkspaceByID,
|
||||
insertWorkspaceBuild: s.insertWorkspaceBuild,
|
||||
})
|
||||
}, opts)
|
||||
}
|
||||
|
||||
func TestTasks(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -2422,3 +2456,328 @@ func TestPostWorkspaceAgentTaskSnapshot(t *testing.T) {
|
||||
require.Equal(t, http.StatusUnauthorized, res.StatusCode)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPauseTask(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
setupClient := func(t *testing.T, db database.Store, ps pubsub.Pubsub, authorizer rbac.Authorizer) *codersdk.Client {
|
||||
t.Helper()
|
||||
client, _, _ := coderdtest.NewWithAPI(t, &coderdtest.Options{
|
||||
Database: db,
|
||||
Pubsub: ps,
|
||||
Authorizer: authorizer,
|
||||
})
|
||||
return client
|
||||
}
|
||||
|
||||
setupWorkspaceTask := func(t *testing.T, db database.Store, user codersdk.CreateFirstUserResponse) (database.Task, uuid.UUID) {
|
||||
t.Helper()
|
||||
workspaceBuild := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).WithTask(database.TaskTable{
|
||||
Prompt: "pause me",
|
||||
}, nil).Do()
|
||||
return workspaceBuild.Task, workspaceBuild.Workspace.ID
|
||||
}
|
||||
|
||||
t.Run("OK", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||
Parse: echo.ParseComplete,
|
||||
ProvisionApply: echo.ApplyComplete,
|
||||
ProvisionGraph: []*proto.Response{
|
||||
{Type: &proto.Response_Graph{Graph: &proto.GraphComplete{
|
||||
HasAiTasks: true,
|
||||
}}},
|
||||
},
|
||||
})
|
||||
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
|
||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||
|
||||
task, err := client.CreateTask(ctx, codersdk.Me, codersdk.CreateTaskRequest{
|
||||
TemplateVersionID: template.ActiveVersionID,
|
||||
Input: "pause me",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.True(t, task.WorkspaceID.Valid)
|
||||
|
||||
workspace, err := client.Workspace(ctx, task.WorkspaceID.UUID)
|
||||
require.NoError(t, err)
|
||||
coderdtest.AwaitWorkspaceBuildJobCompleted(t, client, workspace.LatestBuild.ID)
|
||||
|
||||
resp, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
require.NoError(t, err)
|
||||
build := *resp.WorkspaceBuild
|
||||
require.NotNil(t, build)
|
||||
require.Equal(t, codersdk.WorkspaceTransitionStop, build.Transition)
|
||||
require.Equal(t, task.WorkspaceID.UUID, build.WorkspaceID)
|
||||
require.Equal(t, workspace.LatestBuild.BuildNumber+1, build.BuildNumber)
|
||||
require.Equal(t, string(codersdk.CreateWorkspaceBuildReasonTaskManualPause), string(build.Reason))
|
||||
})
|
||||
|
||||
t.Run("Non-owner role access", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
client := setupClient(t, db, ps, nil)
|
||||
owner := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
roles []rbac.RoleIdentifier
|
||||
expectedStatus int
|
||||
}{
|
||||
{
|
||||
name: "org_member",
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "org_admin",
|
||||
roles: []rbac.RoleIdentifier{rbac.ScopedRoleOrgAdmin(owner.OrganizationID)},
|
||||
expectedStatus: http.StatusAccepted,
|
||||
},
|
||||
{
|
||||
name: "sitewide_member",
|
||||
roles: []rbac.RoleIdentifier{rbac.RoleMember()},
|
||||
expectedStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "sitewide_admin",
|
||||
roles: []rbac.RoleIdentifier{rbac.RoleOwner()},
|
||||
expectedStatus: http.StatusAccepted,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
task, _ := setupWorkspaceTask(t, db, owner)
|
||||
userClient, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, tc.roles...)
|
||||
|
||||
resp, err := userClient.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
if tc.expectedStatus == http.StatusAccepted {
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp.WorkspaceBuild)
|
||||
require.NotEqual(t, uuid.Nil, resp.WorkspaceBuild.ID)
|
||||
return
|
||||
}
|
||||
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, tc.expectedStatus, apiErr.StatusCode())
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Task not found", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||
_ = coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, uuid.New())
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Task lookup forbidden", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
auth := &coderdtest.FakeAuthorizer{
|
||||
ConditionalReturn: func(_ context.Context, _ rbac.Subject, action policy.Action, object rbac.Object) error {
|
||||
if action == policy.ActionRead && object.Type == rbac.ResourceTask.Type {
|
||||
return rbac.UnauthorizedError{}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
client := setupClient(t, db, ps, auth)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
task, _ := setupWorkspaceTask(t, db, user)
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Workspace lookup forbidden", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
auth := &coderdtest.FakeAuthorizer{
|
||||
ConditionalReturn: func(_ context.Context, _ rbac.Subject, action policy.Action, object rbac.Object) error {
|
||||
if action == policy.ActionRead && object.Type == rbac.ResourceWorkspace.Type {
|
||||
return rbac.UnauthorizedError{}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
client := setupClient(t, db, ps, auth)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
task, _ := setupWorkspaceTask(t, db, user)
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("No Workspace for Task", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
client := setupClient(t, db, ps, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
|
||||
workspaceBuild := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).Do()
|
||||
task := dbgen.Task(t, db, database.TaskTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
TemplateVersionID: workspaceBuild.Build.TemplateVersionID,
|
||||
Prompt: "no workspace",
|
||||
})
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusInternalServerError, apiErr.StatusCode())
|
||||
require.Equal(t, "Task does not have a workspace.", apiErr.Message)
|
||||
})
|
||||
|
||||
t.Run("Workspace not found", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
var workspaceID uuid.UUID
|
||||
wrapped := aiTaskStoreWrapper{
|
||||
Store: db,
|
||||
getWorkspaceByID: func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
|
||||
if id == workspaceID && id != uuid.Nil {
|
||||
return database.Workspace{}, sql.ErrNoRows
|
||||
}
|
||||
return db.GetWorkspaceByID(ctx, id)
|
||||
},
|
||||
}
|
||||
client := setupClient(t, wrapped, ps, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
task, workspaceIDValue := setupWorkspaceTask(t, db, user)
|
||||
workspaceID = workspaceIDValue
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusNotFound, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Workspace lookup internal error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
var workspaceID uuid.UUID
|
||||
wrapped := aiTaskStoreWrapper{
|
||||
Store: db,
|
||||
getWorkspaceByID: func(ctx context.Context, id uuid.UUID) (database.Workspace, error) {
|
||||
if id == workspaceID && id != uuid.Nil {
|
||||
return database.Workspace{}, xerrors.New("boom")
|
||||
}
|
||||
return db.GetWorkspaceByID(ctx, id)
|
||||
},
|
||||
}
|
||||
client := setupClient(t, wrapped, ps, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
task, workspaceIDValue := setupWorkspaceTask(t, db, user)
|
||||
workspaceID = workspaceIDValue
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusInternalServerError, apiErr.StatusCode())
|
||||
require.Equal(t, "Internal error fetching task workspace.", apiErr.Message)
|
||||
})
|
||||
|
||||
t.Run("Build Forbidden", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
auth := &coderdtest.FakeAuthorizer{
|
||||
ConditionalReturn: func(_ context.Context, _ rbac.Subject, action policy.Action, object rbac.Object) error {
|
||||
if action == policy.ActionWorkspaceStop && object.Type == rbac.ResourceWorkspace.Type {
|
||||
return rbac.UnauthorizedError{}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
client := setupClient(t, db, ps, auth)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
task, _ := setupWorkspaceTask(t, db, user)
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusForbidden, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Job already in progress", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
client := setupClient(t, db, ps, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
workspaceBuild := dbfake.WorkspaceBuild(t, db, database.WorkspaceTable{
|
||||
OrganizationID: user.OrganizationID,
|
||||
OwnerID: user.UserID,
|
||||
}).
|
||||
WithTask(database.TaskTable{
|
||||
Prompt: "pause me",
|
||||
}, nil).
|
||||
Starting().
|
||||
Do()
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, workspaceBuild.Task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusConflict, apiErr.StatusCode())
|
||||
})
|
||||
|
||||
t.Run("Build Internal Error", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitShort)
|
||||
db, ps := dbtestutil.NewDB(t)
|
||||
wrapped := aiTaskStoreWrapper{
|
||||
Store: db,
|
||||
insertWorkspaceBuild: func(ctx context.Context, arg database.InsertWorkspaceBuildParams) error {
|
||||
return xerrors.New("insert failed")
|
||||
},
|
||||
}
|
||||
client := setupClient(t, wrapped, ps, nil)
|
||||
user := coderdtest.CreateFirstUser(t, client)
|
||||
task, _ := setupWorkspaceTask(t, db, user)
|
||||
|
||||
_, err := client.PauseTask(ctx, codersdk.Me, task.ID)
|
||||
var apiErr *codersdk.Error
|
||||
require.ErrorAs(t, err, &apiErr)
|
||||
require.Equal(t, http.StatusInternalServerError, apiErr.StatusCode())
|
||||
})
|
||||
}
|
||||
|
||||
Generated
+56
-3
@@ -5824,6 +5824,48 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tasks/{user}/{task}/pause": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Tasks"
|
||||
],
|
||||
"summary": "Pause task",
|
||||
"operationId": "pause-task",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Username, user ID, or 'me' for the authenticated user",
|
||||
"name": "user",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Task ID",
|
||||
"name": "task",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"202": {
|
||||
"description": "Accepted",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PauseTaskResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tasks/{user}/{task}/send": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -14102,14 +14144,16 @@ const docTemplate = `{
|
||||
"cli",
|
||||
"ssh_connection",
|
||||
"vscode_connection",
|
||||
"jetbrains_connection"
|
||||
"jetbrains_connection",
|
||||
"task_manual_pause"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"CreateWorkspaceBuildReasonDashboard",
|
||||
"CreateWorkspaceBuildReasonCLI",
|
||||
"CreateWorkspaceBuildReasonSSHConnection",
|
||||
"CreateWorkspaceBuildReasonVSCodeConnection",
|
||||
"CreateWorkspaceBuildReasonJetbrainsConnection"
|
||||
"CreateWorkspaceBuildReasonJetbrainsConnection",
|
||||
"CreateWorkspaceBuildReasonTaskManualPause"
|
||||
]
|
||||
},
|
||||
"codersdk.CreateWorkspaceBuildRequest": {
|
||||
@@ -14143,7 +14187,8 @@ const docTemplate = `{
|
||||
"cli",
|
||||
"ssh_connection",
|
||||
"vscode_connection",
|
||||
"jetbrains_connection"
|
||||
"jetbrains_connection",
|
||||
"task_manual_pause"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
@@ -17014,6 +17059,14 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PauseTaskResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workspace_build": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceBuild"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.Permission": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Generated
+52
-3
@@ -5147,6 +5147,44 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tasks/{user}/{task}/pause": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"consumes": ["application/json"],
|
||||
"tags": ["Tasks"],
|
||||
"summary": "Pause task",
|
||||
"operationId": "pause-task",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Username, user ID, or 'me' for the authenticated user",
|
||||
"name": "user",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"description": "Task ID",
|
||||
"name": "task",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"202": {
|
||||
"description": "Accepted",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/codersdk.PauseTaskResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/tasks/{user}/{task}/send": {
|
||||
"post": {
|
||||
"security": [
|
||||
@@ -12662,14 +12700,16 @@
|
||||
"cli",
|
||||
"ssh_connection",
|
||||
"vscode_connection",
|
||||
"jetbrains_connection"
|
||||
"jetbrains_connection",
|
||||
"task_manual_pause"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"CreateWorkspaceBuildReasonDashboard",
|
||||
"CreateWorkspaceBuildReasonCLI",
|
||||
"CreateWorkspaceBuildReasonSSHConnection",
|
||||
"CreateWorkspaceBuildReasonVSCodeConnection",
|
||||
"CreateWorkspaceBuildReasonJetbrainsConnection"
|
||||
"CreateWorkspaceBuildReasonJetbrainsConnection",
|
||||
"CreateWorkspaceBuildReasonTaskManualPause"
|
||||
]
|
||||
},
|
||||
"codersdk.CreateWorkspaceBuildRequest": {
|
||||
@@ -12699,7 +12739,8 @@
|
||||
"cli",
|
||||
"ssh_connection",
|
||||
"vscode_connection",
|
||||
"jetbrains_connection"
|
||||
"jetbrains_connection",
|
||||
"task_manual_pause"
|
||||
],
|
||||
"allOf": [
|
||||
{
|
||||
@@ -15477,6 +15518,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.PauseTaskResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"workspace_build": {
|
||||
"$ref": "#/definitions/codersdk.WorkspaceBuild"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.Permission": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1078,6 +1078,7 @@ func New(options *Options) *API {
|
||||
r.Patch("/input", api.taskUpdateInput)
|
||||
r.Post("/send", api.taskSend)
|
||||
r.Get("/logs", api.taskLogs)
|
||||
r.Post("/pause", api.pauseTask)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -384,7 +384,7 @@ func (api *API) postWorkspaceBuildsInternal(
|
||||
Experiments(api.Experiments).
|
||||
TemplateVersionPresetID(createBuild.TemplateVersionPresetID)
|
||||
|
||||
if transition == database.WorkspaceTransitionStart && createBuild.Reason != "" {
|
||||
if (transition == database.WorkspaceTransitionStart || transition == database.WorkspaceTransitionStop) && createBuild.Reason != "" {
|
||||
builder = builder.Reason(database.BuildReason(createBuild.Reason))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user