@@ -3,6 +3,7 @@ package storage
33import (
44 "bytes"
55 "context"
6+ "errors"
67 "fmt"
78 "os"
89 "reflect"
@@ -497,7 +498,7 @@ func (s *TemplateStorage) Update(
497498 }
498499
499500 if waitErr := waitForTemplateVersionBuild (ctx , sdk , newVersion .ID ); waitErr != nil {
500- return nil , false , fmt . Errorf ( "wait for template version %q build: %w" , newVersion . ID . String (), waitErr )
501+ return nil , false , mapTemplateVersionBuildWaitError ( waitErr , name )
501502 }
502503
503504 if err := sdk .UpdateActiveTemplateVersion (ctx , templateID , codersdk.UpdateActiveTemplateVersion {ID : newVersion .ID }); err != nil {
@@ -596,6 +597,102 @@ func (s *TemplateStorage) ConvertToTable(ctx context.Context, object, tableOptio
596597 return s .tableConvertor .ConvertToTable (ctx , object , tableOptions )
597598}
598599
600+ type templateVersionBuildTerminalError struct {
601+ versionID uuid.UUID
602+ status codersdk.ProvisionerJobStatus
603+ detail string
604+ }
605+
606+ func (e * templateVersionBuildTerminalError ) Error () string {
607+ if e == nil {
608+ panic ("assertion failed: template version build terminal error must not be nil" )
609+ }
610+ if e .versionID == uuid .Nil {
611+ panic ("assertion failed: template version build terminal error must include version ID" )
612+ }
613+ if e .status == "" {
614+ panic ("assertion failed: template version build terminal error must include status" )
615+ }
616+
617+ detail := e .detail
618+ if detail == "" {
619+ detail = "template version build did not succeed"
620+ }
621+
622+ return fmt .Sprintf (
623+ "template version %q build ended with status %q: %s" ,
624+ e .versionID .String (),
625+ e .status ,
626+ detail ,
627+ )
628+ }
629+
630+ type templateVersionBuildWaitTimeoutError struct {
631+ versionID uuid.UUID
632+ waitTimeout time.Duration
633+ lastStatus codersdk.ProvisionerJobStatus
634+ lastError string
635+ cause error
636+ }
637+
638+ func (e * templateVersionBuildWaitTimeoutError ) Error () string {
639+ if e == nil {
640+ panic ("assertion failed: template version build timeout error must not be nil" )
641+ }
642+ if e .versionID == uuid .Nil {
643+ panic ("assertion failed: template version build timeout error must include version ID" )
644+ }
645+ if e .waitTimeout <= 0 {
646+ panic ("assertion failed: template version build timeout error must include positive timeout" )
647+ }
648+ if e .lastStatus == "" {
649+ panic ("assertion failed: template version build timeout error must include last status" )
650+ }
651+ if e .cause == nil {
652+ panic ("assertion failed: template version build timeout error must include cause" )
653+ }
654+
655+ if e .lastError == "" {
656+ return fmt .Sprintf (
657+ "template version %q did not succeed within %s (last status: %q): %v" ,
658+ e .versionID .String (),
659+ e .waitTimeout ,
660+ e .lastStatus ,
661+ e .cause ,
662+ )
663+ }
664+
665+ return fmt .Sprintf (
666+ "template version %q did not succeed within %s (last status: %q, last error: %s): %v" ,
667+ e .versionID .String (),
668+ e .waitTimeout ,
669+ e .lastStatus ,
670+ e .lastError ,
671+ e .cause ,
672+ )
673+ }
674+
675+ func mapTemplateVersionBuildWaitError (waitErr error , templateName string ) error {
676+ if waitErr == nil {
677+ return fmt .Errorf ("assertion failed: wait error must not be nil" )
678+ }
679+ if templateName == "" {
680+ return fmt .Errorf ("assertion failed: template name must not be empty" )
681+ }
682+
683+ var terminalErr * templateVersionBuildTerminalError
684+ if errors .As (waitErr , & terminalErr ) {
685+ return apierrors .NewBadRequest (terminalErr .Error ())
686+ }
687+
688+ var timeoutErr * templateVersionBuildWaitTimeoutError
689+ if errors .As (waitErr , & timeoutErr ) {
690+ return apierrors .NewBadRequest (timeoutErr .Error ())
691+ }
692+
693+ return coder .MapCoderError (waitErr , aggregationv1alpha1 .Resource ("codertemplates" ), templateName )
694+ }
695+
599696func waitForTemplateVersionBuild (ctx context.Context , sdk * codersdk.Client , versionID uuid.UUID ) error {
600697 if ctx == nil {
601698 return fmt .Errorf ("assertion failed: context must not be nil" )
@@ -624,12 +721,13 @@ func waitForTemplateVersionBuild(ctx context.Context, sdk *codersdk.Client, vers
624721 version , err := sdk .TemplateVersion (waitCtx , versionID )
625722 if err != nil {
626723 if waitCtx .Err () != nil {
627- return fmt .Errorf (
628- "template version %q build wait canceled after status %q: %w" ,
629- versionID .String (),
630- lastStatus ,
631- waitCtx .Err (),
632- )
724+ return & templateVersionBuildWaitTimeoutError {
725+ versionID : versionID ,
726+ waitTimeout : waitConfig .waitTimeout ,
727+ lastStatus : lastStatus ,
728+ lastError : lastError ,
729+ cause : waitCtx .Err (),
730+ }
633731 }
634732 return fmt .Errorf ("fetch template version %q: %w" , versionID .String (), err )
635733 }
@@ -648,12 +746,11 @@ func waitForTemplateVersionBuild(ctx context.Context, sdk *codersdk.Client, vers
648746 if lastError == "" {
649747 lastError = "template version build did not succeed"
650748 }
651- return fmt .Errorf (
652- "template version %q build ended with status %q: %s" ,
653- versionID .String (),
654- status ,
655- lastError ,
656- )
749+ return & templateVersionBuildTerminalError {
750+ versionID : versionID ,
751+ status : status ,
752+ detail : lastError ,
753+ }
657754 case codersdk .ProvisionerJobPending , codersdk .ProvisionerJobRunning , codersdk .ProvisionerJobCanceling , codersdk .ProvisionerJobUnknown :
658755 // Keep polling below.
659756 default :
@@ -674,23 +771,16 @@ func waitForTemplateVersionBuild(ctx context.Context, sdk *codersdk.Client, vers
674771
675772 select {
676773 case <- waitCtx .Done ():
677- if lastError == "" {
678- return fmt .Errorf (
679- "template version %q did not succeed within %s (last status: %q): %w" ,
680- versionID .String (),
681- waitConfig .waitTimeout ,
682- lastStatus ,
683- waitCtx .Err (),
684- )
774+ if waitCtx .Err () == nil {
775+ return fmt .Errorf ("assertion failed: wait context finished without an error" )
776+ }
777+ return & templateVersionBuildWaitTimeoutError {
778+ versionID : versionID ,
779+ waitTimeout : waitConfig .waitTimeout ,
780+ lastStatus : lastStatus ,
781+ lastError : lastError ,
782+ cause : waitCtx .Err (),
685783 }
686- return fmt .Errorf (
687- "template version %q did not succeed within %s (last status: %q, last error: %s): %w" ,
688- versionID .String (),
689- waitConfig .waitTimeout ,
690- lastStatus ,
691- lastError ,
692- waitCtx .Err (),
693- )
694784 case <- time .After (pollInterval ):
695785 }
696786 }
0 commit comments