@@ -388,7 +388,13 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req
388
388
AvatarURL : member .AvatarURL ,
389
389
}
390
390
391
- createWorkspace (ctx , aReq , apiKey .UserID , api , owner , req , rw , r )
391
+ w , err := createWorkspace (ctx , aReq , apiKey .UserID , api , owner , req , r )
392
+ if err != nil {
393
+ httperror .WriteResponseError (ctx , rw , err )
394
+ return
395
+ }
396
+
397
+ httpapi .Write (ctx , rw , http .StatusCreated , w )
392
398
}
393
399
394
400
// Create a new workspace for the currently authenticated user.
@@ -442,8 +448,9 @@ func (api *API) postUserWorkspaces(rw http.ResponseWriter, r *http.Request) {
442
448
// This can be optimized. It exists as it is now for code simplicity.
443
449
// The most common case is to create a workspace for 'Me'. Which does
444
450
// not enter this code branch.
445
- template , ok := requestTemplate (ctx , rw , req , api .Database )
446
- if ! ok {
451
+ template , err := requestTemplate (ctx , req , api .Database )
452
+ if err != nil {
453
+ httperror .WriteResponseError (ctx , rw , err )
447
454
return
448
455
}
449
456
@@ -476,7 +483,14 @@ func (api *API) postUserWorkspaces(rw http.ResponseWriter, r *http.Request) {
476
483
})
477
484
478
485
defer commitAudit ()
479
- createWorkspace (ctx , aReq , apiKey .UserID , api , owner , req , rw , r )
486
+
487
+ w , err := createWorkspace (ctx , aReq , apiKey .UserID , api , owner , req , r )
488
+ if err != nil {
489
+ httperror .WriteResponseError (ctx , rw , err )
490
+ return
491
+ }
492
+
493
+ httpapi .Write (ctx , rw , http .StatusCreated , w )
480
494
}
481
495
482
496
type workspaceOwner struct {
@@ -492,12 +506,11 @@ func createWorkspace(
492
506
api * API ,
493
507
owner workspaceOwner ,
494
508
req codersdk.CreateWorkspaceRequest ,
495
- rw http.ResponseWriter ,
496
509
r * http.Request ,
497
- ) {
498
- template , ok := requestTemplate (ctx , rw , req , api .Database )
499
- if ! ok {
500
- return
510
+ ) (codersdk. Workspace , error ) {
511
+ template , err := requestTemplate (ctx , req , api .Database )
512
+ if err != nil {
513
+ return codersdk. Workspace {}, err
501
514
}
502
515
503
516
// This is a premature auth check to avoid doing unnecessary work if the user
@@ -506,14 +519,12 @@ func createWorkspace(
506
519
rbac .ResourceWorkspace .InOrg (template .OrganizationID ).WithOwner (owner .ID .String ())) {
507
520
// If this check fails, return a proper unauthorized error to the user to indicate
508
521
// what is going on.
509
- httpapi . Write ( ctx , rw , http .StatusForbidden , codersdk.Response {
522
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusForbidden , codersdk.Response {
510
523
Message : "Unauthorized to create workspace." ,
511
524
Detail : "You are unable to create a workspace in this organization. " +
512
525
"It is possible to have access to the template, but not be able to create a workspace. " +
513
526
"Please contact an administrator about your permissions if you feel this is an error." ,
514
- Validations : nil ,
515
527
})
516
- return
517
528
}
518
529
519
530
// Update audit log's organization
@@ -523,49 +534,42 @@ func createWorkspace(
523
534
// would be wasted.
524
535
if ! api .Authorize (r , policy .ActionCreate ,
525
536
rbac .ResourceWorkspace .InOrg (template .OrganizationID ).WithOwner (owner .ID .String ())) {
526
- httpapi .ResourceNotFound (rw )
527
- return
537
+ return codersdk.Workspace {}, httperror .ErrResourceNotFound
528
538
}
529
539
// The user also needs permission to use the template. At this point they have
530
540
// read perms, but not necessarily "use". This is also checked in `db.InsertWorkspace`.
531
541
// Doing this up front can save some work below if the user doesn't have permission.
532
542
if ! api .Authorize (r , policy .ActionUse , template ) {
533
- httpapi . Write ( ctx , rw , http .StatusForbidden , codersdk.Response {
543
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusForbidden , codersdk.Response {
534
544
Message : fmt .Sprintf ("Unauthorized access to use the template %q." , template .Name ),
535
545
Detail : "Although you are able to view the template, you are unable to create a workspace using it. " +
536
546
"Please contact an administrator about your permissions if you feel this is an error." ,
537
- Validations : nil ,
538
547
})
539
- return
540
548
}
541
549
542
550
templateAccessControl := (* (api .AccessControlStore .Load ())).GetTemplateAccessControl (template )
543
551
if templateAccessControl .IsDeprecated () {
544
- httpapi . Write ( ctx , rw , http .StatusBadRequest , codersdk.Response {
552
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusBadRequest , codersdk.Response {
545
553
Message : fmt .Sprintf ("Template %q has been deprecated, and cannot be used to create a new workspace." , template .Name ),
546
554
// Pass the deprecated message to the user.
547
- Detail : templateAccessControl .Deprecated ,
548
- Validations : nil ,
555
+ Detail : templateAccessControl .Deprecated ,
549
556
})
550
- return
551
557
}
552
558
553
559
dbAutostartSchedule , err := validWorkspaceSchedule (req .AutostartSchedule )
554
560
if err != nil {
555
- httpapi . Write ( ctx , rw , http .StatusBadRequest , codersdk.Response {
561
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusBadRequest , codersdk.Response {
556
562
Message : "Invalid Autostart Schedule." ,
557
563
Validations : []codersdk.ValidationError {{Field : "schedule" , Detail : err .Error ()}},
558
564
})
559
- return
560
565
}
561
566
562
567
templateSchedule , err := (* api .TemplateScheduleStore .Load ()).Get (ctx , api .Database , template .ID )
563
568
if err != nil {
564
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
569
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
565
570
Message : "Internal error fetching template schedule." ,
566
571
Detail : err .Error (),
567
572
})
568
- return
569
573
}
570
574
571
575
nextStartAt := sql.NullTime {}
@@ -578,23 +582,21 @@ func createWorkspace(
578
582
579
583
dbTTL , err := validWorkspaceTTLMillis (req .TTLMillis , templateSchedule .DefaultTTL )
580
584
if err != nil {
581
- httpapi . Write ( ctx , rw , http .StatusBadRequest , codersdk.Response {
585
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusBadRequest , codersdk.Response {
582
586
Message : "Invalid Workspace Time to Shutdown." ,
583
587
Validations : []codersdk.ValidationError {{Field : "ttl_ms" , Detail : err .Error ()}},
584
588
})
585
- return
586
589
}
587
590
588
591
// back-compatibility: default to "never" if not included.
589
592
dbAU := database .AutomaticUpdatesNever
590
593
if req .AutomaticUpdates != "" {
591
594
dbAU , err = validWorkspaceAutomaticUpdates (req .AutomaticUpdates )
592
595
if err != nil {
593
- httpapi . Write ( ctx , rw , http .StatusBadRequest , codersdk.Response {
596
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusBadRequest , codersdk.Response {
594
597
Message : "Invalid Workspace Automatic Updates setting." ,
595
598
Validations : []codersdk.ValidationError {{Field : "automatic_updates" , Detail : err .Error ()}},
596
599
})
597
- return
598
600
}
599
601
}
600
602
@@ -607,20 +609,18 @@ func createWorkspace(
607
609
})
608
610
if err == nil {
609
611
// If the workspace already exists, don't allow creation.
610
- httpapi . Write ( ctx , rw , http .StatusConflict , codersdk.Response {
612
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusConflict , codersdk.Response {
611
613
Message : fmt .Sprintf ("Workspace %q already exists." , req .Name ),
612
614
Validations : []codersdk.ValidationError {{
613
615
Field : "name" ,
614
616
Detail : "This value is already in use and should be unique." ,
615
617
}},
616
618
})
617
- return
618
619
} else if ! errors .Is (err , sql .ErrNoRows ) {
619
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
620
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
620
621
Message : fmt .Sprintf ("Internal error fetching workspace by name %q." , req .Name ),
621
622
Detail : err .Error (),
622
623
})
623
- return
624
624
}
625
625
626
626
var (
@@ -759,8 +759,7 @@ func createWorkspace(
759
759
return err
760
760
}, nil )
761
761
if err != nil {
762
- httperror .WriteWorkspaceBuildError (ctx , rw , err )
763
- return
762
+ return codersdk.Workspace {}, err
764
763
}
765
764
766
765
err = provisionerjobs .PostJob (api .Pubsub , * provisionerJob )
@@ -809,11 +808,10 @@ func createWorkspace(
809
808
provisionerDaemons ,
810
809
)
811
810
if err != nil {
812
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
811
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
813
812
Message : "Internal error converting workspace build." ,
814
813
Detail : err .Error (),
815
814
})
816
- return
817
815
}
818
816
819
817
w , err := convertWorkspace (
@@ -825,40 +823,38 @@ func createWorkspace(
825
823
codersdk.WorkspaceAppStatus {},
826
824
)
827
825
if err != nil {
828
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
826
+ return codersdk. Workspace {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
829
827
Message : "Internal error converting workspace." ,
830
828
Detail : err .Error (),
831
829
})
832
- return
833
830
}
834
- httpapi .Write (ctx , rw , http .StatusCreated , w )
831
+
832
+ return w , nil
835
833
}
836
834
837
- func requestTemplate (ctx context.Context , rw http. ResponseWriter , req codersdk.CreateWorkspaceRequest , db database.Store ) (database.Template , bool ) {
835
+ func requestTemplate (ctx context.Context , req codersdk.CreateWorkspaceRequest , db database.Store ) (database.Template , error ) {
838
836
// If we were given a `TemplateVersionID`, we need to determine the `TemplateID` from it.
839
837
templateID := req .TemplateID
840
838
841
839
if templateID == uuid .Nil {
842
840
templateVersion , err := db .GetTemplateVersionByID (ctx , req .TemplateVersionID )
843
841
if httpapi .Is404Error (err ) {
844
- httpapi . Write ( ctx , rw , http .StatusBadRequest , codersdk.Response {
842
+ return database. Template {}, httperror . NewResponseError ( http .StatusBadRequest , codersdk.Response {
845
843
Message : fmt .Sprintf ("Template version %q doesn't exist." , req .TemplateVersionID ),
846
844
Validations : []codersdk.ValidationError {{
847
845
Field : "template_version_id" ,
848
846
Detail : "template not found" ,
849
847
}},
850
848
})
851
- return database.Template {}, false
852
849
}
853
850
if err != nil {
854
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
851
+ return database. Template {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
855
852
Message : "Internal error fetching template version." ,
856
853
Detail : err .Error (),
857
854
})
858
- return database.Template {}, false
859
855
}
860
856
if templateVersion .Archived {
861
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
857
+ return database. Template {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
862
858
Message : "Archived template versions cannot be used to make a workspace." ,
863
859
Validations : []codersdk.ValidationError {
864
860
{
@@ -867,37 +863,33 @@ func requestTemplate(ctx context.Context, rw http.ResponseWriter, req codersdk.C
867
863
},
868
864
},
869
865
})
870
- return database.Template {}, false
871
866
}
872
867
873
868
templateID = templateVersion .TemplateID .UUID
874
869
}
875
870
876
871
template , err := db .GetTemplateByID (ctx , templateID )
877
872
if httpapi .Is404Error (err ) {
878
- httpapi . Write ( ctx , rw , http .StatusBadRequest , codersdk.Response {
873
+ return database. Template {}, httperror . NewResponseError ( http .StatusBadRequest , codersdk.Response {
879
874
Message : fmt .Sprintf ("Template %q doesn't exist." , templateID ),
880
875
Validations : []codersdk.ValidationError {{
881
876
Field : "template_id" ,
882
877
Detail : "template not found" ,
883
878
}},
884
879
})
885
- return database.Template {}, false
886
880
}
887
881
if err != nil {
888
- httpapi . Write ( ctx , rw , http .StatusInternalServerError , codersdk.Response {
882
+ return database. Template {}, httperror . NewResponseError ( http .StatusInternalServerError , codersdk.Response {
889
883
Message : "Internal error fetching template." ,
890
884
Detail : err .Error (),
891
885
})
892
- return database.Template {}, false
893
886
}
894
887
if template .Deleted {
895
- httpapi . Write ( ctx , rw , http .StatusNotFound , codersdk.Response {
888
+ return database. Template {}, httperror . NewResponseError ( http .StatusNotFound , codersdk.Response {
896
889
Message : fmt .Sprintf ("Template %q has been deleted!" , template .Name ),
897
890
})
898
- return database.Template {}, false
899
891
}
900
- return template , true
892
+ return template , nil
901
893
}
902
894
903
895
func claimPrebuild (
0 commit comments