Skip to content

Commit 47b8ca9

Browse files
authored
feat: add an endpoint to manually resume a coder task (#21948)
Closes coder/internal#1262. This PR adds: * the `POST /api/experimental/tasks/{user}/{task}/resume` endpoint * follows conventions from coder/internal#1261 * sets the build reason to `task_resume` * a task that is not paused (ie. is already running), cannot be resumed.
1 parent 7f7ff9c commit 47b8ca9

10 files changed

Lines changed: 817 additions & 8 deletions

File tree

‎coderd/aitasks.go‎

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,3 +1304,90 @@ func (api *API) pauseTask(rw http.ResponseWriter, r *http.Request) {
13041304
WorkspaceBuild: &build,
13051305
})
13061306
}
1307+
1308+
// @Summary Resume task
1309+
// @ID resume-task
1310+
// @Security CoderSessionToken
1311+
// @Accept json
1312+
// @Tags Tasks
1313+
// @Param user path string true "Username, user ID, or 'me' for the authenticated user"
1314+
// @Param task path string true "Task ID" format(uuid)
1315+
// @Success 202 {object} codersdk.ResumeTaskResponse
1316+
// @Router /tasks/{user}/{task}/resume [post]
1317+
func (api *API) resumeTask(rw http.ResponseWriter, r *http.Request) {
1318+
var (
1319+
ctx = r.Context()
1320+
apiKey = httpmw.APIKey(r)
1321+
task = httpmw.TaskParam(r)
1322+
)
1323+
1324+
if !task.WorkspaceID.Valid {
1325+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1326+
Message: "Task does not have a workspace.",
1327+
})
1328+
return
1329+
}
1330+
1331+
workspace, err := api.Database.GetWorkspaceByID(ctx, task.WorkspaceID.UUID)
1332+
if err != nil {
1333+
if httpapi.Is404Error(err) {
1334+
httpapi.ResourceNotFound(rw)
1335+
return
1336+
}
1337+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1338+
Message: "Internal error fetching task workspace.",
1339+
Detail: err.Error(),
1340+
})
1341+
return
1342+
}
1343+
1344+
latestBuild, err := api.Database.GetLatestWorkspaceBuildByWorkspaceID(ctx, workspace.ID)
1345+
if err != nil {
1346+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1347+
Message: "Internal error fetching task workspace build.",
1348+
Detail: err.Error(),
1349+
})
1350+
return
1351+
}
1352+
job, err := api.Database.GetProvisionerJobByID(ctx, latestBuild.JobID)
1353+
if err != nil {
1354+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
1355+
Message: "Internal error fetching task workspace build job.",
1356+
Detail: err.Error(),
1357+
})
1358+
return
1359+
}
1360+
workspaceStatus := codersdk.ConvertWorkspaceStatus(
1361+
codersdk.ProvisionerJobStatus(job.JobStatus),
1362+
codersdk.WorkspaceTransition(latestBuild.Transition),
1363+
)
1364+
if workspaceStatus == codersdk.WorkspaceStatusRunning {
1365+
httpapi.Write(ctx, rw, http.StatusConflict, codersdk.Response{
1366+
Message: "Task workspace is already running.",
1367+
Detail: fmt.Sprintf("Workspace status is %q.", workspaceStatus),
1368+
})
1369+
return
1370+
}
1371+
1372+
buildReq := codersdk.CreateWorkspaceBuildRequest{
1373+
Transition: codersdk.WorkspaceTransitionStart,
1374+
Reason: codersdk.CreateWorkspaceBuildReasonTaskResume,
1375+
}
1376+
build, err := api.postWorkspaceBuildsInternal(
1377+
ctx,
1378+
apiKey,
1379+
workspace,
1380+
buildReq,
1381+
func(action policy.Action, object rbac.Objecter) bool {
1382+
return api.Authorize(r, action, object)
1383+
},
1384+
audit.WorkspaceBuildBaggageFromRequest(r),
1385+
)
1386+
if err != nil {
1387+
httperror.WriteWorkspaceBuildError(ctx, rw, err)
1388+
return
1389+
}
1390+
httpapi.Write(ctx, rw, http.StatusAccepted, codersdk.ResumeTaskResponse{
1391+
WorkspaceBuild: &build,
1392+
})
1393+
}

0 commit comments

Comments
 (0)