diff --git a/builder/amazon/common/step_run_spot_instance.go b/builder/amazon/common/step_run_spot_instance.go index f1b484b81..940e7c8ac 100644 --- a/builder/amazon/common/step_run_spot_instance.go +++ b/builder/amazon/common/step_run_spot_instance.go @@ -6,9 +6,11 @@ import ( "fmt" "io/ioutil" "log" + "strings" "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/packer/common/random" "github.com/hashicorp/packer/common/retry" @@ -278,23 +280,39 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) Type: aws.String("instant"), } + var createOutput *ec2.CreateFleetOutput + + err = retry.Config{ + Tries: 11, + ShouldRetry: func(err error) bool { + if strings.Contains(err.Error(), "Invalid IAM Instance Profile name") { + // eventual consistency of the profile. PutRolePolicy & + // AddRoleToInstanceProfile are eventually consistent and once + // we can wait on those operations, this can be removed. + return true + } + return request.IsErrorRetryable(err) + }, + RetryDelay: (&retry.Backoff{InitialBackoff: 500 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, + }.Run(ctx, func(ctx context.Context) error { + createOutput, err = ec2conn.CreateFleet(createFleetInput) + + if err == nil && createOutput.Errors != nil { + err = fmt.Errorf("errors: %v", createOutput.Errors) + } + if err != nil { + log.Printf("create request failed %v", err) + } + return err + }) + // Create the request for the spot instance. - req, createOutput := ec2conn.CreateFleetRequest(createFleetInput) - ui.Message(fmt.Sprintf("Sending spot request (%s)...", req.RequestID)) - // Actually send the spot connection request. - err = req.Send() if err != nil { if createOutput.FleetId != nil { err = fmt.Errorf("Error waiting for fleet request (%s): %s", *createOutput.FleetId, err) } else { err = fmt.Errorf("Error waiting for fleet request: %s", err) } - state.Put("error", err) - ui.Error(err.Error()) - return multistep.ActionHalt - } - - if len(createOutput.Instances) == 0 { // We can end up with errors because one of the allowed availability // zones doesn't have one of the allowed instance types; as long as // an instance is launched, these errors aren't important. @@ -308,6 +326,9 @@ func (s *StepRunSpotInstance) Run(ctx context.Context, state multistep.StateBag) ui.Error(err.Error()) return multistep.ActionHalt } + state.Put("error", err) + ui.Error(err.Error()) + return multistep.ActionHalt } instanceId = *createOutput.Instances[0].InstanceIds[0]