From a9abe43325f50c6d15b986d0eb43c10018cda455 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Wed, 28 Sep 2016 18:06:42 -0700 Subject: [PATCH] builder/amazon: add retry login when creating tags. also move Retry from builder/googlecompute/common to common/retry --- builder/amazon/common/step_create_tags.go | 21 ++++++++++++++++--- builder/googlecompute/driver_gce.go | 3 ++- .../step_wait_instance_startup.go | 7 ++++--- .../common.go => common/retry.go | 10 ++++----- .../common_test.go => common/retry_test.go | 10 ++++----- 5 files changed, 34 insertions(+), 17 deletions(-) rename builder/googlecompute/common.go => common/retry.go (90%) rename builder/googlecompute/common_test.go => common/retry_test.go (98%) diff --git a/builder/amazon/common/step_create_tags.go b/builder/amazon/common/step_create_tags.go index 2ac39dc02..0eb98d9a0 100644 --- a/builder/amazon/common/step_create_tags.go +++ b/builder/amazon/common/step_create_tags.go @@ -4,9 +4,11 @@ import ( "fmt" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" + retry "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" ) @@ -71,9 +73,22 @@ func (s *StepCreateTags) Run(state multistep.StateBag) multistep.StepAction { } } - _, err = regionconn.CreateTags(&ec2.CreateTagsInput{ - Resources: resourceIds, - Tags: ec2Tags, + // Retry creating tags for about 2.5 minutes + err = retry.Retry(0.2, 30, 11, func() (bool, error) { + _, err := regionconn.CreateTags(&ec2.CreateTagsInput{ + Resources: resourceIds, + Tags: ec2Tags, + }) + if err == nil { + return true, nil + } + if awsErr, ok := err.(awserr.Error); ok { + if awsErr.Code() == "InvalidAMIID.NotFound" || + awsErr.Code() == "InvalidSnapshot.NotFound" { + return false, nil + } + } + return true, err }) if err != nil { diff --git a/builder/googlecompute/driver_gce.go b/builder/googlecompute/driver_gce.go index d5d081793..b47fe6d18 100644 --- a/builder/googlecompute/driver_gce.go +++ b/builder/googlecompute/driver_gce.go @@ -7,6 +7,7 @@ import ( "runtime" "strings" + "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/version" @@ -458,7 +459,7 @@ type stateRefreshFunc func() (string, error) // waitForState will spin in a loop forever waiting for state to // reach a certain target. func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) error { - err := Retry(2, 2, 0, func() (bool, error) { + err := common.Retry(2, 2, 0, func() (bool, error) { state, err := refresh() if err != nil { return false, err diff --git a/builder/googlecompute/step_wait_instance_startup.go b/builder/googlecompute/step_wait_instance_startup.go index 7d7e6777f..d81dfabea 100644 --- a/builder/googlecompute/step_wait_instance_startup.go +++ b/builder/googlecompute/step_wait_instance_startup.go @@ -1,10 +1,11 @@ package googlecompute -import( +import ( "errors" "fmt" "github.com/mitchellh/multistep" + "github.com/mitchellh/packer/common" "github.com/mitchellh/packer/packer" ) @@ -17,11 +18,11 @@ func (s *StepWaitInstanceStartup) Run(state multistep.StateBag) multistep.StepAc driver := state.Get("driver").(Driver) ui := state.Get("ui").(packer.Ui) instanceName := state.Get("instance_name").(string) - + ui.Say("Waiting for any running startup script to finish...") // Keep checking the serial port output to see if the startup script is done. - err := Retry(10, 60, 0, func() (bool, error) { + err := common.Retry(10, 60, 0, func() (bool, error) { status, err := driver.GetInstanceMetadata(config.Zone, instanceName, StartupScriptStatusKey) diff --git a/builder/googlecompute/common.go b/common/retry.go similarity index 90% rename from builder/googlecompute/common.go rename to common/retry.go index 8f5520c16..a7aa74594 100644 --- a/builder/googlecompute/common.go +++ b/common/retry.go @@ -1,4 +1,4 @@ -package googlecompute +package common import ( "fmt" @@ -25,20 +25,20 @@ func Retry(initialInterval float64, maxInterval float64, numTries uint, function done := false interval := initialInterval for i := uint(0); !done && (numTries == 0 || i < numTries); i++ { - done, err = function() + done, err = function() if err != nil { return err } - + if !done { // Retry after delay. Calculate next delay. time.Sleep(time.Duration(interval) * time.Second) - interval = math.Min(interval * 2, maxInterval) + interval = math.Min(interval*2, maxInterval) } } if !done { - return RetryExhaustedError + return RetryExhaustedError } return nil } diff --git a/builder/googlecompute/common_test.go b/common/retry_test.go similarity index 98% rename from builder/googlecompute/common_test.go rename to common/retry_test.go index caf9c2436..4940dbc67 100644 --- a/builder/googlecompute/common_test.go +++ b/common/retry_test.go @@ -1,4 +1,4 @@ -package googlecompute +package common import ( "fmt" @@ -18,7 +18,7 @@ func TestRetry(t *testing.T) { if err != nil { t.Fatalf("Passing function should not have returned a retry error. Error: %s", err) } - + // Test that a failing function gets retried (once in this example). numTries = 0 results := []bool{false, true} @@ -33,7 +33,7 @@ func TestRetry(t *testing.T) { if err != nil { t.Fatalf("Successful retried function should not have returned a retry error. Error: %s", err) } - + // Test that a function error gets returned, and the function does not get called again. numTries = 0 funcErr := fmt.Errorf("This function had an error!") @@ -47,7 +47,7 @@ func TestRetry(t *testing.T) { if err != funcErr { t.Fatalf("Errant function did not return the right error %s. Error: %s", funcErr, err) } - + // Test when a function exhausts its retries. numTries = 0 expectedTries := uint(3) @@ -61,4 +61,4 @@ func TestRetry(t *testing.T) { if err != RetryExhaustedError { t.Fatalf("Unsuccessful retry function should have returned a retry exhausted error. Actual error: %s", err) } -} \ No newline at end of file +}