2022-03-07 17:40:54 +00:00
package coderd
import (
2022-09-16 18:54:23 +00:00
"context"
2022-03-22 19:17:50 +00:00
"database/sql"
2022-05-16 19:36:27 +00:00
"encoding/json"
"errors"
2022-03-07 17:40:54 +00:00
"fmt"
"net/http"
2022-06-10 16:08:50 +00:00
"strconv"
2022-10-09 20:01:18 +00:00
"time"
2022-03-07 17:40:54 +00:00
2022-05-16 19:36:27 +00:00
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
2022-08-01 21:53:05 +00:00
"golang.org/x/exp/slices"
2022-05-16 19:36:27 +00:00
"golang.org/x/xerrors"
2022-10-25 19:34:48 +00:00
"cdr.dev/slog"
2022-10-14 13:47:38 +00:00
"github.com/coder/coder/coderd/audit"
2022-03-25 21:07:45 +00:00
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
2022-11-08 01:10:49 +00:00
"github.com/coder/coder/coderd/provisionerdserver"
2022-05-18 23:15:19 +00:00
"github.com/coder/coder/coderd/rbac"
2022-03-22 19:17:50 +00:00
"github.com/coder/coder/codersdk"
2022-03-07 17:40:54 +00:00
)
2022-05-26 03:14:08 +00:00
func ( api * API ) workspaceBuild ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-03-07 17:40:54 +00:00
workspaceBuild := httpmw . WorkspaceBuildParam ( r )
2022-06-01 23:49:43 +00:00
workspace := httpmw . WorkspaceParam ( r )
2022-05-18 23:15:19 +00:00
2022-08-09 18:16:53 +00:00
if ! api . Authorize ( r , rbac . ActionRead , workspace ) {
2022-06-14 15:14:05 +00:00
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-09-21 22:07:00 +00:00
data , err := api . workspaceBuildsData ( ctx , [ ] database . Workspace { workspace } , [ ] database . WorkspaceBuild { workspaceBuild } )
2022-03-07 17:40:54 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error getting workspace build data." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-03-07 17:40:54 +00:00
} )
return
}
2022-04-12 15:17:33 +00:00
2022-09-16 18:54:23 +00:00
apiBuild , err := api . convertWorkspaceBuild (
workspaceBuild ,
workspace ,
data . jobs [ 0 ] ,
data . users ,
data . resources ,
data . metadata ,
data . agents ,
data . apps ,
)
2022-06-07 14:28:47 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error converting workspace build." ,
2022-06-07 14:28:47 +00:00
Detail : err . Error ( ) ,
} )
return
}
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusOK , apiBuild )
2022-03-07 17:40:54 +00:00
}
2022-05-26 03:14:08 +00:00
func ( api * API ) workspaceBuilds ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-05-16 19:36:27 +00:00
workspace := httpmw . WorkspaceParam ( r )
2022-06-14 15:14:05 +00:00
if ! api . Authorize ( r , rbac . ActionRead , workspace ) {
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-05-18 16:33:33 +00:00
paginationParams , ok := parsePagination ( rw , r )
if ! ok {
return
}
2022-06-02 14:01:45 +00:00
2022-10-09 20:01:18 +00:00
var since time . Time
sinceParam := r . URL . Query ( ) . Get ( "since" )
if sinceParam != "" {
var err error
since , err = time . Parse ( time . RFC3339 , sinceParam )
if err != nil {
httpapi . Write ( r . Context ( ) , rw , http . StatusBadRequest , codersdk . Response {
Message : "bad `since` format, must be RFC3339" ,
Detail : err . Error ( ) ,
} )
return
}
}
2022-09-16 18:54:23 +00:00
var workspaceBuilds [ ] database . WorkspaceBuild
2022-06-02 14:01:45 +00:00
// Ensure all db calls happen in the same tx
err := api . Database . InTx ( func ( store database . Store ) error {
var err error
if paginationParams . AfterID != uuid . Nil {
// See if the record exists first. If the record does not exist, the pagination
// query will not work.
2022-09-21 22:07:00 +00:00
_ , err := store . GetWorkspaceBuildByID ( ctx , paginationParams . AfterID )
2022-06-02 14:01:45 +00:00
if err != nil && xerrors . Is ( err , sql . ErrNoRows ) {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusBadRequest , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : fmt . Sprintf ( "Record at \"after_id\" (%q) does not exist." , paginationParams . AfterID . String ( ) ) ,
2022-06-02 14:01:45 +00:00
} )
return err
} else if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching workspace build at \"after_id\"." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-06-02 14:01:45 +00:00
} )
return err
}
}
2022-10-09 20:01:18 +00:00
req := database . GetWorkspaceBuildsByWorkspaceIDParams {
2022-06-02 14:01:45 +00:00
WorkspaceID : workspace . ID ,
AfterID : paginationParams . AfterID ,
OffsetOpt : int32 ( paginationParams . Offset ) ,
LimitOpt : int32 ( paginationParams . Limit ) ,
2022-10-09 20:01:18 +00:00
Since : database . Time ( since ) ,
2022-06-02 14:01:45 +00:00
}
2022-10-09 20:01:18 +00:00
workspaceBuilds , err = store . GetWorkspaceBuildsByWorkspaceID ( ctx , req )
2022-06-02 14:01:45 +00:00
if xerrors . Is ( err , sql . ErrNoRows ) {
err = nil
}
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching workspace build." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-06-02 14:01:45 +00:00
} )
return err
}
return nil
} )
2022-05-16 19:36:27 +00:00
if err != nil {
return
}
2022-06-02 14:01:45 +00:00
2022-09-21 22:07:00 +00:00
data , err := api . workspaceBuildsData ( ctx , [ ] database . Workspace { workspace } , workspaceBuilds )
2022-05-16 19:36:27 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error getting workspace build data." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-05-16 19:36:27 +00:00
} )
return
}
2022-09-16 18:54:23 +00:00
apiBuilds , err := api . convertWorkspaceBuilds (
workspaceBuilds ,
[ ] database . Workspace { workspace } ,
data . jobs ,
data . users ,
data . resources ,
data . metadata ,
data . agents ,
data . apps ,
)
2022-06-07 14:28:47 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error converting workspace build." ,
2022-06-07 14:28:47 +00:00
Detail : err . Error ( ) ,
} )
return
}
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusOK , apiBuilds )
2022-05-16 19:36:27 +00:00
}
2022-06-10 16:08:50 +00:00
func ( api * API ) workspaceBuildByBuildNumber ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-06-10 16:08:50 +00:00
owner := httpmw . UserParam ( r )
workspaceName := chi . URLParam ( r , "workspacename" )
buildNumber , err := strconv . ParseInt ( chi . URLParam ( r , "buildnumber" ) , 10 , 32 )
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusBadRequest , codersdk . Response {
2022-06-10 16:08:50 +00:00
Message : "Failed to parse build number as integer." ,
Detail : err . Error ( ) ,
} )
return
}
2022-09-21 22:07:00 +00:00
workspace , err := api . Database . GetWorkspaceByOwnerIDAndName ( ctx , database . GetWorkspaceByOwnerIDAndNameParams {
2022-06-10 16:08:50 +00:00
OwnerID : owner . ID ,
Name : workspaceName ,
} )
if errors . Is ( err , sql . ErrNoRows ) {
2022-06-14 15:14:05 +00:00
httpapi . ResourceNotFound ( rw )
2022-06-10 16:08:50 +00:00
return
}
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-10 16:08:50 +00:00
Message : "Internal error fetching workspace by name." ,
Detail : err . Error ( ) ,
} )
return
}
2022-06-14 15:14:05 +00:00
if ! api . Authorize ( r , rbac . ActionRead , workspace ) {
httpapi . ResourceNotFound ( rw )
2022-06-10 16:08:50 +00:00
return
}
2022-09-21 22:07:00 +00:00
workspaceBuild , err := api . Database . GetWorkspaceBuildByWorkspaceIDAndBuildNumber ( ctx , database . GetWorkspaceBuildByWorkspaceIDAndBuildNumberParams {
2022-06-10 16:08:50 +00:00
WorkspaceID : workspace . ID ,
BuildNumber : int32 ( buildNumber ) ,
} )
if errors . Is ( err , sql . ErrNoRows ) {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusNotFound , codersdk . Response {
2022-06-10 16:08:50 +00:00
Message : fmt . Sprintf ( "Workspace %q Build %d does not exist." , workspaceName , buildNumber ) ,
} )
return
}
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-10 16:08:50 +00:00
Message : "Internal error fetching workspace build." ,
Detail : err . Error ( ) ,
} )
return
}
2022-09-21 22:07:00 +00:00
data , err := api . workspaceBuildsData ( ctx , [ ] database . Workspace { workspace } , [ ] database . WorkspaceBuild { workspaceBuild } )
2022-06-10 16:08:50 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error getting workspace build data." ,
2022-06-10 16:08:50 +00:00
Detail : err . Error ( ) ,
} )
return
}
2022-09-16 18:54:23 +00:00
apiBuild , err := api . convertWorkspaceBuild (
workspaceBuild ,
workspace ,
data . jobs [ 0 ] ,
data . users ,
data . resources ,
data . metadata ,
data . agents ,
data . apps ,
)
2022-06-10 16:08:50 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error converting workspace build." ,
2022-06-10 16:08:50 +00:00
Detail : err . Error ( ) ,
} )
return
}
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusOK , apiBuild )
2022-06-10 16:08:50 +00:00
}
2022-05-26 03:14:08 +00:00
func ( api * API ) postWorkspaceBuilds ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-05-16 19:36:27 +00:00
apiKey := httpmw . APIKey ( r )
workspace := httpmw . WorkspaceParam ( r )
var createBuild codersdk . CreateWorkspaceBuildRequest
2022-09-21 22:07:00 +00:00
if ! httpapi . Read ( ctx , rw , r , & createBuild ) {
2022-05-16 19:36:27 +00:00
return
}
2022-05-18 23:15:19 +00:00
// Rbac action depends on the transition
var action rbac . Action
switch createBuild . Transition {
2022-05-19 18:04:44 +00:00
case codersdk . WorkspaceTransitionDelete :
2022-05-18 23:15:19 +00:00
action = rbac . ActionDelete
2022-05-19 18:04:44 +00:00
case codersdk . WorkspaceTransitionStart , codersdk . WorkspaceTransitionStop :
2022-05-18 23:15:19 +00:00
action = rbac . ActionUpdate
default :
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : fmt . Sprintf ( "Transition %q not supported." , createBuild . Transition ) ,
2022-05-18 23:15:19 +00:00
} )
return
}
2022-08-09 18:16:53 +00:00
if ! api . Authorize ( r , action , workspace ) {
2022-06-14 15:14:05 +00:00
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-10-25 19:34:48 +00:00
auditor := api . Auditor . Load ( )
// if user deletes a workspace, audit the workspace
2022-10-14 13:47:38 +00:00
if action == rbac . ActionDelete {
2022-10-25 19:34:48 +00:00
aReq , commitAudit := audit . InitRequest [ database . Workspace ] ( rw , & audit . RequestParams {
Audit : * auditor ,
Log : api . Logger ,
Request : r ,
Action : database . AuditActionDelete ,
} )
2022-10-14 13:47:38 +00:00
defer commitAudit ( )
aReq . Old = workspace
}
2022-10-25 19:34:48 +00:00
latestBuild , latestBuildErr := api . Database . GetLatestWorkspaceBuildByWorkspaceID ( ctx , workspace . ID )
// if a user starts/stops a workspace, audit the workspace build
if action == rbac . ActionUpdate {
var auditAction database . AuditAction
if createBuild . Transition == codersdk . WorkspaceTransitionStart {
auditAction = database . AuditActionStart
} else if createBuild . Transition == codersdk . WorkspaceTransitionStop {
auditAction = database . AuditActionStop
} else {
auditAction = database . AuditActionWrite
}
// We pass the workspace name to the Auditor so that it
// can form a friendly string for the user.
workspaceResourceInfo := map [ string ] string {
"workspaceName" : workspace . Name ,
}
wriBytes , err := json . Marshal ( workspaceResourceInfo )
2022-10-25 13:41:00 +00:00
if err != nil {
2022-10-25 19:34:48 +00:00
api . Logger . Error ( ctx , "could not marshal workspace name" , slog . Error ( err ) )
}
aReq , commitAudit := audit . InitRequest [ database . WorkspaceBuild ] ( rw , & audit . RequestParams {
Audit : * auditor ,
Log : api . Logger ,
Request : r ,
Action : auditAction ,
AdditionalFields : wriBytes ,
} )
defer commitAudit ( )
aReq . Old = latestBuild
}
if createBuild . TemplateVersionID == uuid . Nil {
if latestBuildErr != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching the latest workspace build." ,
2022-10-25 19:34:48 +00:00
Detail : latestBuildErr . Error ( ) ,
2022-05-16 19:36:27 +00:00
} )
return
}
createBuild . TemplateVersionID = latestBuild . TemplateVersionID
}
2022-09-06 17:07:00 +00:00
2022-09-21 22:07:00 +00:00
templateVersion , err := api . Database . GetTemplateVersionByID ( ctx , createBuild . TemplateVersionID )
2022-05-16 19:36:27 +00:00
if errors . Is ( err , sql . ErrNoRows ) {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusBadRequest , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Template version not found." ,
2022-07-13 00:15:02 +00:00
Validations : [ ] codersdk . ValidationError { {
2022-05-16 19:36:27 +00:00
Field : "template_version_id" ,
Detail : "template version not found" ,
} } ,
} )
return
}
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching template version." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-05-16 19:36:27 +00:00
} )
return
}
2022-09-06 17:07:00 +00:00
2022-09-21 22:07:00 +00:00
template , err := api . Database . GetTemplateByID ( ctx , templateVersion . TemplateID . UUID )
2022-09-06 17:07:00 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-06 17:07:00 +00:00
Message : "Failed to get template" ,
Detail : err . Error ( ) ,
} )
return
}
var state [ ] byte
// If custom state, deny request since user could be corrupting or leaking
// cloud state.
if createBuild . ProvisionerState != nil || createBuild . Orphan {
if ! api . Authorize ( r , rbac . ActionUpdate , template . RBACObject ( ) ) {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusForbidden , codersdk . Response {
2022-09-06 17:07:00 +00:00
Message : "Only template managers may provide custom state" ,
} )
return
}
state = createBuild . ProvisionerState
}
if createBuild . Orphan {
if createBuild . Transition != codersdk . WorkspaceTransitionDelete {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusBadRequest , codersdk . Response {
2022-09-06 17:07:00 +00:00
Message : "Orphan is only permitted when deleting a workspace." ,
Detail : err . Error ( ) ,
} )
return
}
if createBuild . ProvisionerState != nil && createBuild . Orphan {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusBadRequest , codersdk . Response {
2022-09-06 17:07:00 +00:00
Message : "ProvisionerState cannot be set alongside Orphan since state intent is unclear." ,
} )
return
}
state = [ ] byte { }
}
2022-09-21 22:07:00 +00:00
templateVersionJob , err := api . Database . GetProvisionerJobByID ( ctx , templateVersion . JobID )
2022-05-16 19:36:27 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching provisioner job." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-05-16 19:36:27 +00:00
} )
return
}
templateVersionJobStatus := convertProvisionerJob ( templateVersionJob ) . Status
switch templateVersionJobStatus {
case codersdk . ProvisionerJobPending , codersdk . ProvisionerJobRunning :
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusNotAcceptable , codersdk . Response {
2022-05-16 19:36:27 +00:00
Message : fmt . Sprintf ( "The provided template version is %s. Wait for it to complete importing!" , templateVersionJobStatus ) ,
} )
return
case codersdk . ProvisionerJobFailed :
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusPreconditionFailed , codersdk . Response {
2022-05-16 19:36:27 +00:00
Message : fmt . Sprintf ( "The provided template version %q has failed to import: %q. You cannot build workspaces with it!" , templateVersion . Name , templateVersionJob . Error . String ) ,
} )
return
case codersdk . ProvisionerJobCanceled :
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusPreconditionFailed , codersdk . Response {
2022-05-16 19:36:27 +00:00
Message : "The provided template version was canceled during import. You cannot builds workspaces with it!" ,
} )
return
}
2022-05-18 16:33:33 +00:00
// Store prior build number to compute new build number
var priorBuildNum int32
2022-09-21 22:07:00 +00:00
priorHistory , err := api . Database . GetLatestWorkspaceBuildByWorkspaceID ( ctx , workspace . ID )
2022-05-16 19:36:27 +00:00
if err == nil {
2022-09-21 22:07:00 +00:00
priorJob , err := api . Database . GetProvisionerJobByID ( ctx , priorHistory . JobID )
2022-05-16 19:36:27 +00:00
if err == nil && convertProvisionerJob ( priorJob ) . Status . Active ( ) {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusConflict , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "A workspace build is already active." ,
2022-05-16 19:36:27 +00:00
} )
return
}
2022-05-18 16:33:33 +00:00
priorBuildNum = priorHistory . BuildNumber
2022-05-16 19:36:27 +00:00
} else if ! errors . Is ( err , sql . ErrNoRows ) {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching prior workspace build." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-05-16 19:36:27 +00:00
} )
return
}
2022-09-06 17:07:00 +00:00
if state == nil {
state = priorHistory . ProvisionerState
}
2022-05-16 19:36:27 +00:00
var workspaceBuild database . WorkspaceBuild
var provisionerJob database . ProvisionerJob
// This must happen in a transaction to ensure history can be inserted, and
// the prior history can update it's "after" column to point at the new.
err = api . Database . InTx ( func ( db database . Store ) error {
2022-09-21 22:07:00 +00:00
existing , err := db . ParameterValues ( ctx , database . ParameterValuesParams {
2022-06-27 16:19:10 +00:00
Scopes : [ ] database . ParameterScope { database . ParameterScopeWorkspace } ,
ScopeIds : [ ] uuid . UUID { workspace . ID } ,
} )
if err != nil && ! xerrors . Is ( err , sql . ErrNoRows ) {
return xerrors . Errorf ( "Fetch previous parameters: %w" , err )
}
// Write/Update any new params
now := database . Now ( )
for _ , param := range createBuild . ParameterValues {
for _ , exists := range existing {
// If the param exists, delete the old param before inserting the new one
if exists . Name == param . Name {
2022-09-21 22:07:00 +00:00
err = db . DeleteParameterValueByID ( ctx , exists . ID )
2022-06-27 16:19:10 +00:00
if err != nil && ! xerrors . Is ( err , sql . ErrNoRows ) {
return xerrors . Errorf ( "Failed to delete old param %q: %w" , exists . Name , err )
}
}
}
2022-09-21 22:07:00 +00:00
_ , err = db . InsertParameterValue ( ctx , database . InsertParameterValueParams {
2022-06-27 16:19:10 +00:00
ID : uuid . New ( ) ,
Name : param . Name ,
CreatedAt : now ,
UpdatedAt : now ,
Scope : database . ParameterScopeWorkspace ,
ScopeID : workspace . ID ,
SourceScheme : database . ParameterSourceScheme ( param . SourceScheme ) ,
SourceValue : param . SourceValue ,
DestinationScheme : database . ParameterDestinationScheme ( param . DestinationScheme ) ,
} )
if err != nil {
return xerrors . Errorf ( "insert parameter value: %w" , err )
}
}
2022-05-16 19:36:27 +00:00
workspaceBuildID := uuid . New ( )
2022-11-08 01:10:49 +00:00
input , err := json . Marshal ( provisionerdserver . WorkspaceProvisionJob {
2022-05-16 19:36:27 +00:00
WorkspaceBuildID : workspaceBuildID ,
} )
if err != nil {
return xerrors . Errorf ( "marshal provision job: %w" , err )
}
2022-09-21 22:07:00 +00:00
provisionerJob , err = db . InsertProvisionerJob ( ctx , database . InsertProvisionerJobParams {
2022-05-16 19:36:27 +00:00
ID : uuid . New ( ) ,
CreatedAt : database . Now ( ) ,
UpdatedAt : database . Now ( ) ,
InitiatorID : apiKey . UserID ,
OrganizationID : template . OrganizationID ,
Provisioner : template . Provisioner ,
Type : database . ProvisionerJobTypeWorkspaceBuild ,
StorageMethod : templateVersionJob . StorageMethod ,
2022-10-13 23:02:52 +00:00
FileID : templateVersionJob . FileID ,
2022-05-16 19:36:27 +00:00
Input : input ,
} )
if err != nil {
return xerrors . Errorf ( "insert provisioner job: %w" , err )
}
2022-09-21 22:07:00 +00:00
workspaceBuild , err = db . InsertWorkspaceBuild ( ctx , database . InsertWorkspaceBuildParams {
2022-05-16 19:36:27 +00:00
ID : workspaceBuildID ,
CreatedAt : database . Now ( ) ,
UpdatedAt : database . Now ( ) ,
WorkspaceID : workspace . ID ,
TemplateVersionID : templateVersion . ID ,
2022-05-18 16:33:33 +00:00
BuildNumber : priorBuildNum + 1 ,
2022-05-16 19:36:27 +00:00
ProvisionerState : state ,
InitiatorID : apiKey . UserID ,
2022-05-19 18:04:44 +00:00
Transition : database . WorkspaceTransition ( createBuild . Transition ) ,
2022-05-16 19:36:27 +00:00
JobID : provisionerJob . ID ,
2022-06-17 17:41:11 +00:00
Reason : database . BuildReasonInitiator ,
2022-05-16 19:36:27 +00:00
} )
if err != nil {
return xerrors . Errorf ( "insert workspace build: %w" , err )
}
return nil
} )
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error inserting workspace build." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-05-16 19:36:27 +00:00
} )
return
}
2022-10-10 18:03:54 +00:00
users , err := api . Database . GetUsersByIDs ( ctx , [ ] uuid . UUID {
workspace . OwnerID ,
workspaceBuild . InitiatorID ,
2022-06-09 01:23:35 +00:00
} )
2022-06-07 14:28:47 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:52:44 +00:00
Message : "Internal error getting user." ,
2022-06-07 14:28:47 +00:00
Detail : err . Error ( ) ,
} )
return
}
2022-09-16 18:54:23 +00:00
apiBuild , err := api . convertWorkspaceBuild (
workspaceBuild ,
workspace ,
provisionerJob ,
users ,
[ ] database . WorkspaceResource { } ,
[ ] database . WorkspaceResourceMetadatum { } ,
[ ] database . WorkspaceAgent { } ,
[ ] database . WorkspaceApp { } ,
)
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-09-16 18:54:23 +00:00
Message : "Internal error converting workspace build." ,
Detail : err . Error ( ) ,
} )
return
}
2022-11-07 15:25:18 +00:00
api . publishWorkspaceUpdate ( ctx , workspace . ID )
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusCreated , apiBuild )
2022-05-16 19:36:27 +00:00
}
2022-05-26 03:14:08 +00:00
func ( api * API ) patchCancelWorkspaceBuild ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-03-22 19:17:50 +00:00
workspaceBuild := httpmw . WorkspaceBuildParam ( r )
2022-09-21 22:07:00 +00:00
workspace , err := api . Database . GetWorkspaceByID ( ctx , workspaceBuild . WorkspaceID )
2022-05-18 23:15:19 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "No workspace exists for this job." ,
2022-05-18 23:15:19 +00:00
} )
return
}
2022-08-09 18:16:53 +00:00
if ! api . Authorize ( r , rbac . ActionUpdate , workspace ) {
2022-06-14 15:14:05 +00:00
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-09-21 22:07:00 +00:00
job , err := api . Database . GetProvisionerJobByID ( ctx , workspaceBuild . JobID )
2022-03-22 19:17:50 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching provisioner job." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-03-22 19:17:50 +00:00
} )
return
}
if job . CompletedAt . Valid {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusPreconditionFailed , codersdk . Response {
2022-03-22 19:17:50 +00:00
Message : "Job has already completed!" ,
} )
return
}
if job . CanceledAt . Valid {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusPreconditionFailed , codersdk . Response {
2022-03-22 19:17:50 +00:00
Message : "Job has already been marked as canceled!" ,
} )
return
}
2022-09-21 22:07:00 +00:00
err = api . Database . UpdateProvisionerJobWithCancelByID ( ctx , database . UpdateProvisionerJobWithCancelByIDParams {
2022-03-22 19:17:50 +00:00
ID : job . ID ,
CanceledAt : sql . NullTime {
Time : database . Now ( ) ,
Valid : true ,
} ,
2022-10-03 16:43:11 +00:00
CompletedAt : sql . NullTime {
Time : database . Now ( ) ,
// If the job is running, don't mark it completed!
Valid : ! job . WorkerID . Valid ,
} ,
2022-03-22 19:17:50 +00:00
} )
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error updating provisioner job." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-03-22 19:17:50 +00:00
} )
return
}
2022-11-07 15:25:18 +00:00
api . publishWorkspaceUpdate ( ctx , workspace . ID )
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusOK , codersdk . Response {
2022-03-22 19:17:50 +00:00
Message : "Job has been marked as canceled..." ,
} )
}
2022-05-26 03:14:08 +00:00
func ( api * API ) workspaceBuildResources ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-03-07 17:40:54 +00:00
workspaceBuild := httpmw . WorkspaceBuildParam ( r )
2022-09-21 22:07:00 +00:00
workspace , err := api . Database . GetWorkspaceByID ( ctx , workspaceBuild . WorkspaceID )
2022-05-18 23:15:19 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "No workspace exists for this job." ,
2022-05-18 23:15:19 +00:00
} )
return
}
2022-08-09 18:16:53 +00:00
if ! api . Authorize ( r , rbac . ActionRead , workspace ) {
2022-06-14 15:14:05 +00:00
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-09-21 22:07:00 +00:00
job , err := api . Database . GetProvisionerJobByID ( ctx , workspaceBuild . JobID )
2022-03-07 17:40:54 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching provisioner job." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-03-07 17:40:54 +00:00
} )
return
}
api . provisionerJobResources ( rw , r , job )
}
2022-05-26 03:14:08 +00:00
func ( api * API ) workspaceBuildLogs ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-03-07 17:40:54 +00:00
workspaceBuild := httpmw . WorkspaceBuildParam ( r )
2022-09-21 22:07:00 +00:00
workspace , err := api . Database . GetWorkspaceByID ( ctx , workspaceBuild . WorkspaceID )
2022-05-18 23:15:19 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "No workspace exists for this job." ,
2022-05-18 23:15:19 +00:00
} )
return
}
2022-08-09 18:16:53 +00:00
if ! api . Authorize ( r , rbac . ActionRead , workspace ) {
2022-06-14 15:14:05 +00:00
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-09-21 22:07:00 +00:00
job , err := api . Database . GetProvisionerJobByID ( ctx , workspaceBuild . JobID )
2022-03-07 17:40:54 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "Internal error fetching provisioner job." ,
2022-06-03 21:48:09 +00:00
Detail : err . Error ( ) ,
2022-03-07 17:40:54 +00:00
} )
return
}
api . provisionerJobLogs ( rw , r , job )
}
2022-05-26 03:14:08 +00:00
func ( api * API ) workspaceBuildState ( rw http . ResponseWriter , r * http . Request ) {
2022-09-21 22:07:00 +00:00
ctx := r . Context ( )
2022-05-02 22:51:58 +00:00
workspaceBuild := httpmw . WorkspaceBuildParam ( r )
2022-09-21 22:07:00 +00:00
workspace , err := api . Database . GetWorkspaceByID ( ctx , workspaceBuild . WorkspaceID )
2022-05-18 23:15:19 +00:00
if err != nil {
2022-09-21 22:07:00 +00:00
httpapi . Write ( ctx , rw , http . StatusInternalServerError , codersdk . Response {
2022-06-07 14:33:06 +00:00
Message : "No workspace exists for this job." ,
2022-05-18 23:15:19 +00:00
} )
return
}
2022-06-14 15:14:05 +00:00
if ! api . Authorize ( r , rbac . ActionRead , workspace ) {
httpapi . ResourceNotFound ( rw )
2022-05-18 23:15:19 +00:00
return
}
2022-05-02 22:51:58 +00:00
rw . Header ( ) . Set ( "Content-Type" , "application/json" )
rw . WriteHeader ( http . StatusOK )
_ , _ = rw . Write ( workspaceBuild . ProvisionerState )
}
2022-09-16 18:54:23 +00:00
type workspaceBuildsData struct {
users [ ] database . User
jobs [ ] database . ProvisionerJob
resources [ ] database . WorkspaceResource
metadata [ ] database . WorkspaceResourceMetadatum
agents [ ] database . WorkspaceAgent
apps [ ] database . WorkspaceApp
}
func ( api * API ) workspaceBuildsData ( ctx context . Context , workspaces [ ] database . Workspace , workspaceBuilds [ ] database . WorkspaceBuild ) ( workspaceBuildsData , error ) {
userIDs := make ( [ ] uuid . UUID , 0 , len ( workspaceBuilds ) )
for _ , build := range workspaceBuilds {
userIDs = append ( userIDs , build . InitiatorID )
}
for _ , workspace := range workspaces {
userIDs = append ( userIDs , workspace . OwnerID )
}
2022-10-10 18:03:54 +00:00
users , err := api . Database . GetUsersByIDs ( ctx , userIDs )
2022-09-16 18:54:23 +00:00
if err != nil {
return workspaceBuildsData { } , xerrors . Errorf ( "get users: %w" , err )
}
jobIDs := make ( [ ] uuid . UUID , 0 , len ( workspaceBuilds ) )
for _ , build := range workspaceBuilds {
jobIDs = append ( jobIDs , build . JobID )
}
jobs , err := api . Database . GetProvisionerJobsByIDs ( ctx , jobIDs )
if err != nil && ! errors . Is ( err , sql . ErrNoRows ) {
return workspaceBuildsData { } , xerrors . Errorf ( "get provisioner jobs: %w" , err )
}
resources , err := api . Database . GetWorkspaceResourcesByJobIDs ( ctx , jobIDs )
if err != nil && ! errors . Is ( err , sql . ErrNoRows ) {
return workspaceBuildsData { } , xerrors . Errorf ( "get workspace resources by job: %w" , err )
}
if len ( resources ) == 0 {
return workspaceBuildsData {
users : users ,
jobs : jobs ,
} , nil
}
resourceIDs := make ( [ ] uuid . UUID , 0 )
for _ , resource := range resources {
resourceIDs = append ( resourceIDs , resource . ID )
}
metadata , err := api . Database . GetWorkspaceResourceMetadataByResourceIDs ( ctx , resourceIDs )
if err != nil && ! errors . Is ( err , sql . ErrNoRows ) {
return workspaceBuildsData { } , xerrors . Errorf ( "fetching resource metadata: %w" , err )
}
agents , err := api . Database . GetWorkspaceAgentsByResourceIDs ( ctx , resourceIDs )
if err != nil && ! errors . Is ( err , sql . ErrNoRows ) {
return workspaceBuildsData { } , xerrors . Errorf ( "get workspace agents: %w" , err )
}
if len ( resources ) == 0 {
return workspaceBuildsData {
users : users ,
jobs : jobs ,
resources : resources ,
metadata : metadata ,
} , nil
}
agentIDs := make ( [ ] uuid . UUID , 0 )
for _ , agent := range agents {
agentIDs = append ( agentIDs , agent . ID )
}
apps , err := api . Database . GetWorkspaceAppsByAgentIDs ( ctx , agentIDs )
if err != nil && ! errors . Is ( err , sql . ErrNoRows ) {
return workspaceBuildsData { } , xerrors . Errorf ( "fetching workspace apps: %w" , err )
2022-06-01 23:49:43 +00:00
}
2022-06-09 01:23:35 +00:00
2022-09-16 18:54:23 +00:00
return workspaceBuildsData {
users : users ,
jobs : jobs ,
resources : resources ,
metadata : metadata ,
agents : agents ,
apps : apps ,
} , nil
}
func ( api * API ) convertWorkspaceBuilds (
workspaceBuilds [ ] database . WorkspaceBuild ,
workspaces [ ] database . Workspace ,
jobs [ ] database . ProvisionerJob ,
users [ ] database . User ,
workspaceResources [ ] database . WorkspaceResource ,
resourceMetadata [ ] database . WorkspaceResourceMetadatum ,
resourceAgents [ ] database . WorkspaceAgent ,
agentApps [ ] database . WorkspaceApp ,
) ( [ ] codersdk . WorkspaceBuild , error ) {
workspaceByID := map [ uuid . UUID ] database . Workspace { }
for _ , workspace := range workspaces {
workspaceByID [ workspace . ID ] = workspace
}
jobByID := map [ uuid . UUID ] database . ProvisionerJob { }
for _ , job := range jobs {
jobByID [ job . ID ] = job
2022-06-09 01:23:35 +00:00
}
2022-09-16 18:54:23 +00:00
var apiBuilds [ ] codersdk . WorkspaceBuild
for _ , build := range workspaceBuilds {
job , exists := jobByID [ build . JobID ]
if ! exists {
return nil , xerrors . New ( "build job not found" )
}
workspace , exists := workspaceByID [ build . WorkspaceID ]
if ! exists {
return nil , xerrors . New ( "workspace not found" )
}
apiBuild , err := api . convertWorkspaceBuild (
build ,
workspace ,
job ,
users ,
workspaceResources ,
resourceMetadata ,
resourceAgents ,
agentApps ,
)
if err != nil {
return nil , xerrors . Errorf ( "converting workspace build: %w" , err )
}
apiBuilds = append ( apiBuilds , apiBuild )
}
return apiBuilds , nil
}
func ( api * API ) convertWorkspaceBuild (
build database . WorkspaceBuild ,
workspace database . Workspace ,
job database . ProvisionerJob ,
users [ ] database . User ,
workspaceResources [ ] database . WorkspaceResource ,
resourceMetadata [ ] database . WorkspaceResourceMetadatum ,
resourceAgents [ ] database . WorkspaceAgent ,
agentApps [ ] database . WorkspaceApp ,
) ( codersdk . WorkspaceBuild , error ) {
userByID := map [ uuid . UUID ] database . User { }
for _ , user := range users {
userByID [ user . ID ] = user
}
resourcesByJobID := map [ uuid . UUID ] [ ] database . WorkspaceResource { }
for _ , resource := range workspaceResources {
resourcesByJobID [ resource . JobID ] = append ( resourcesByJobID [ resource . JobID ] , resource )
}
metadataByResourceID := map [ uuid . UUID ] [ ] database . WorkspaceResourceMetadatum { }
for _ , metadata := range resourceMetadata {
metadataByResourceID [ metadata . WorkspaceResourceID ] = append ( metadataByResourceID [ metadata . WorkspaceResourceID ] , metadata )
}
agentsByResourceID := map [ uuid . UUID ] [ ] database . WorkspaceAgent { }
for _ , agent := range resourceAgents {
agentsByResourceID [ agent . ResourceID ] = append ( agentsByResourceID [ agent . ResourceID ] , agent )
}
appsByAgentID := map [ uuid . UUID ] [ ] database . WorkspaceApp { }
for _ , app := range agentApps {
appsByAgentID [ app . AgentID ] = append ( appsByAgentID [ app . AgentID ] , app )
}
owner , exists := userByID [ workspace . OwnerID ]
if ! exists {
return codersdk . WorkspaceBuild { } , xerrors . Errorf ( "owner not found for workspace: %q" , workspace . Name )
}
initiator , exists := userByID [ build . InitiatorID ]
if ! exists {
return codersdk . WorkspaceBuild { } , xerrors . Errorf ( "build initiator not found for workspace: %q" , workspace . Name )
}
resources := resourcesByJobID [ job . ID ]
apiResources := make ( [ ] codersdk . WorkspaceResource , 0 )
for _ , resource := range resources {
agents := agentsByResourceID [ resource . ID ]
apiAgents := make ( [ ] codersdk . WorkspaceAgent , 0 )
for _ , agent := range agents {
apps := appsByAgentID [ agent . ID ]
2022-10-17 13:43:30 +00:00
apiAgent , err := convertWorkspaceAgent ( api . DERPMap , * api . TailnetCoordinator . Load ( ) , agent , convertApps ( apps ) , api . AgentInactiveDisconnectTimeout )
2022-09-16 18:54:23 +00:00
if err != nil {
return codersdk . WorkspaceBuild { } , xerrors . Errorf ( "converting workspace agent: %w" , err )
}
apiAgents = append ( apiAgents , apiAgent )
}
metadata := append ( make ( [ ] database . WorkspaceResourceMetadatum , 0 ) , metadataByResourceID [ resource . ID ] ... )
apiResources = append ( apiResources , convertWorkspaceResource ( resource , apiAgents , metadata ) )
2022-06-09 01:23:35 +00:00
}
2022-10-03 16:43:11 +00:00
apiJob := convertProvisionerJob ( job )
transition := codersdk . WorkspaceTransition ( build . Transition )
2022-03-22 19:17:50 +00:00
return codersdk . WorkspaceBuild {
2022-09-16 18:54:23 +00:00
ID : build . ID ,
CreatedAt : build . CreatedAt ,
UpdatedAt : build . UpdatedAt ,
2022-06-07 14:28:47 +00:00
WorkspaceOwnerID : workspace . OwnerID ,
2022-09-16 18:54:23 +00:00
WorkspaceOwnerName : owner . Username ,
WorkspaceID : build . WorkspaceID ,
2022-06-07 14:28:47 +00:00
WorkspaceName : workspace . Name ,
2022-09-16 18:54:23 +00:00
TemplateVersionID : build . TemplateVersionID ,
BuildNumber : build . BuildNumber ,
2022-10-03 16:43:11 +00:00
Transition : transition ,
2022-09-16 18:54:23 +00:00
InitiatorID : build . InitiatorID ,
InitiatorUsername : initiator . Username ,
2022-10-03 16:43:11 +00:00
Job : apiJob ,
2022-09-16 18:54:23 +00:00
Deadline : codersdk . NewNullTime ( build . Deadline , ! build . Deadline . IsZero ( ) ) ,
Reason : codersdk . BuildReason ( build . Reason ) ,
Resources : apiResources ,
2022-10-03 16:43:11 +00:00
Status : convertWorkspaceStatus ( apiJob . Status , transition ) ,
2022-09-16 18:54:23 +00:00
} , nil
2022-03-07 17:40:54 +00:00
}
2022-08-01 21:53:05 +00:00
func convertWorkspaceResource ( resource database . WorkspaceResource , agents [ ] codersdk . WorkspaceAgent , metadata [ ] database . WorkspaceResourceMetadatum ) codersdk . WorkspaceResource {
metadataMap := map [ string ] database . WorkspaceResourceMetadatum { }
// implicit metadata fields come first
metadataMap [ "type" ] = database . WorkspaceResourceMetadatum {
Key : "type" ,
Value : sql . NullString { String : resource . Type , Valid : true } ,
Sensitive : false ,
}
// explicit metadata fields come afterward, and can override implicit ones
for _ , field := range metadata {
metadataMap [ field . Key ] = field
}
var convertedMetadata [ ] codersdk . WorkspaceResourceMetadata
for _ , field := range metadataMap {
if field . Value . Valid {
convertedField := codersdk . WorkspaceResourceMetadata {
Key : field . Key ,
Value : field . Value . String ,
Sensitive : field . Sensitive ,
}
convertedMetadata = append ( convertedMetadata , convertedField )
}
}
slices . SortFunc ( convertedMetadata , func ( a , b codersdk . WorkspaceResourceMetadata ) bool {
return a . Key < b . Key
} )
2022-03-22 19:17:50 +00:00
return codersdk . WorkspaceResource {
2022-03-07 17:40:54 +00:00
ID : resource . ID ,
CreatedAt : resource . CreatedAt ,
JobID : resource . JobID ,
2022-05-19 18:04:44 +00:00
Transition : codersdk . WorkspaceTransition ( resource . Transition ) ,
2022-03-07 17:40:54 +00:00
Type : resource . Type ,
Name : resource . Name ,
2022-09-09 19:38:00 +00:00
Hide : resource . Hide ,
2022-09-13 14:32:59 +00:00
Icon : resource . Icon ,
2022-04-11 21:06:15 +00:00
Agents : agents ,
2022-08-01 21:53:05 +00:00
Metadata : convertedMetadata ,
2022-03-07 17:40:54 +00:00
}
}
2022-10-03 16:43:11 +00:00
func convertWorkspaceStatus ( jobStatus codersdk . ProvisionerJobStatus , transition codersdk . WorkspaceTransition ) codersdk . WorkspaceStatus {
switch jobStatus {
case codersdk . ProvisionerJobPending :
return codersdk . WorkspaceStatusPending
case codersdk . ProvisionerJobRunning :
switch transition {
case codersdk . WorkspaceTransitionStart :
return codersdk . WorkspaceStatusStarting
case codersdk . WorkspaceTransitionStop :
return codersdk . WorkspaceStatusStopping
case codersdk . WorkspaceTransitionDelete :
return codersdk . WorkspaceStatusDeleting
}
case codersdk . ProvisionerJobSucceeded :
switch transition {
case codersdk . WorkspaceTransitionStart :
return codersdk . WorkspaceStatusRunning
case codersdk . WorkspaceTransitionStop :
return codersdk . WorkspaceStatusStopped
case codersdk . WorkspaceTransitionDelete :
return codersdk . WorkspaceStatusDeleted
}
case codersdk . ProvisionerJobCanceling :
return codersdk . WorkspaceStatusCanceling
case codersdk . ProvisionerJobCanceled :
return codersdk . WorkspaceStatusCanceled
case codersdk . ProvisionerJobFailed :
return codersdk . WorkspaceStatusFailed
}
// return error status since we should never get here
return codersdk . WorkspaceStatusFailed
}