@@ -561,62 +561,105 @@ class CodeCatalystClientInternal {
561
561
status : string ,
562
562
timeout : Timeout = new Timeout ( 180000 )
563
563
) : Promise < DevEnvironment > {
564
- let lastStatus : undefined | string
564
+ // Track the status changes chronologically so that we can
565
+ // 1. reason about hysterisis (weird flip-flops)
566
+ // 2. have visibility in the logs
567
+ const lastStatus = new Array < { status : string ; start : number } > ( )
568
+ let alias : string | undefined
569
+
565
570
try {
566
571
const devenv = await this . getDevEnvironment ( args )
567
- lastStatus = devenv . status
568
- if ( status === 'RUNNING' && lastStatus === 'RUNNING' ) {
572
+ alias = devenv . alias
573
+ lastStatus . push ( { status : devenv . status , start : Date . now ( ) } )
574
+ if ( status === 'RUNNING' && devenv . status === 'RUNNING' ) {
569
575
// "Debounce" in case caller did not check if the environment was already running.
570
576
return devenv
571
577
}
572
578
} catch {
573
- lastStatus = undefined
579
+ lastStatus . length = 0
580
+ }
581
+
582
+ function statusesToString ( ) {
583
+ let s = ''
584
+ for ( let i = 0 ; i < lastStatus . length ; i ++ ) {
585
+ const item = lastStatus [ i ]
586
+ const nextItem = i < lastStatus . length - 1 ? lastStatus [ i + 1 ] : undefined
587
+ const nextTime = nextItem ? nextItem . start : Date . now ( )
588
+ const elapsed = nextTime - item . start
589
+ s += `${ s ? ' ' : '' } ${ item . status } /${ elapsed } ms`
590
+ }
591
+ return `[${ s } ]`
592
+ }
593
+
594
+ const doLog = ( kind : 'debug' | 'info' , msg : string ) => {
595
+ const name = ( alias ? alias : args . id ) . substring ( 0 , 19 )
596
+ const fmt = `${ msg } (time: %d s): %s %s`
597
+ if ( kind === 'debug' ) {
598
+ this . log . debug ( fmt , timeout . elapsedTime / 1000 , name , statusesToString ( ) )
599
+ } else {
600
+ this . log . info ( fmt , timeout . elapsedTime / 1000 , name , statusesToString ( ) )
601
+ }
574
602
}
575
603
576
604
const progress = await showMessageWithCancel (
577
- localize ( 'AWS.CodeCatalyst.startMde .message' , 'CodeCatalyst' ) ,
605
+ localize ( 'AWS.CodeCatalyst.devenv .message' , 'CodeCatalyst' ) ,
578
606
timeout
579
607
)
580
- progress . report ( { message : localize ( 'AWS.CodeCatalyst.startMde .checking' , 'checking status...' ) } )
608
+ progress . report ( { message : localize ( 'AWS.CodeCatalyst.devenv .checking' , 'checking status...' ) } )
581
609
582
610
const pollDevEnv = waitUntil (
583
611
async ( ) => {
584
- this . log . debug (
585
- 'devenv not started, waiting (time: %d seconds): %s' ,
586
- timeout . elapsedTime / 1000 ,
587
- args . id
588
- )
612
+ doLog ( 'debug' , 'devenv not started, waiting' )
589
613
// technically this will continue to be called until it reaches its own timeout, need a better way to 'cancel' a `waitUntil`
590
614
if ( timeout . completed ) {
591
615
return
592
616
}
593
617
594
618
const resp = await this . getDevEnvironment ( args )
595
- if ( lastStatus === 'STARTING' && ( resp . status === 'STOPPED' || resp . status === 'STOPPING' ) ) {
619
+ const startingStates = lastStatus . filter ( o => o . status === 'STARTING' ) . length
620
+ if (
621
+ startingStates > 1 &&
622
+ lastStatus [ 0 ] . status === 'STARTING' &&
623
+ ( resp . status === 'STOPPED' || resp . status === 'STOPPING' )
624
+ ) {
596
625
throw new ToolkitError ( 'Dev Environment failed to start' , { code : 'BadDevEnvState' } )
597
626
}
598
627
599
628
if ( resp . status === 'STOPPED' ) {
600
629
progress . report ( {
601
- message : localize ( 'AWS.CodeCatalyst.startMde .stopStart' , 'Resuming Dev Environment...' ) ,
630
+ message : localize ( 'AWS.CodeCatalyst.devenv .stopStart' , 'Resuming Dev Environment...' ) ,
602
631
} )
603
- await this . startDevEnvironment ( args )
632
+ try {
633
+ await this . startDevEnvironment ( args )
634
+ } catch ( e ) {
635
+ const err = e as AWS . AWSError
636
+ // May happen after "Update Dev Environment":
637
+ // ConflictException: "Cannot start Dev Environment because update process is still going on"
638
+ // Continue retrying in that case.
639
+ if ( err . code === 'ConflictException' ) {
640
+ doLog ( 'debug' , 'devenv not started (ConflictException), waiting' )
641
+ } else {
642
+ throw new ToolkitError ( 'Dev Environment failed to start' , {
643
+ code : 'BadDevEnvState' ,
644
+ cause : err ,
645
+ } )
646
+ }
647
+ }
604
648
} else if ( resp . status === 'STOPPING' ) {
605
649
progress . report ( {
606
- message : localize (
607
- 'AWS.CodeCatalyst.startMde.resuming' ,
608
- 'Waiting for Dev Environment to stop...'
609
- ) ,
650
+ message : localize ( 'AWS.CodeCatalyst.devenv.resuming' , 'Waiting for Dev Environment to stop...' ) ,
610
651
} )
611
652
} else if ( resp . status === 'FAILED' ) {
612
653
throw new ToolkitError ( 'Dev Environment failed to start' , { code : 'FailedDevEnv' } )
613
654
} else {
614
655
progress . report ( {
615
- message : localize ( 'AWS.CodeCatalyst.startMde .starting' , 'Opening Dev Environment...' ) ,
656
+ message : localize ( 'AWS.CodeCatalyst.devenv .starting' , 'Opening Dev Environment...' ) ,
616
657
} )
617
658
}
618
659
619
- lastStatus = resp . status
660
+ if ( lastStatus [ lastStatus . length - 1 ] . status !== resp . status ) {
661
+ lastStatus . push ( { status : resp . status , start : Date . now ( ) } )
662
+ }
620
663
return resp . status === 'RUNNING' ? resp : undefined
621
664
} ,
622
665
// note: the `waitUntil` will resolve prior to the real timeout if it is refreshed
@@ -625,9 +668,10 @@ class CodeCatalystClientInternal {
625
668
626
669
const devenv = await waitTimeout ( pollDevEnv , timeout )
627
670
if ( ! devenv ) {
671
+ doLog ( 'info' , 'devenv failed to start' )
628
672
throw new ToolkitError ( 'Dev Environment failed to start' , { code : 'Timeout' } )
629
673
}
630
- this . log . info ( 'devenv started after %d seconds: %s ', timeout . elapsedTime / 1000 , args . id )
674
+ doLog ( 'info ', 'devenv started' )
631
675
632
676
return devenv
633
677
}
0 commit comments