From 35998225dbf22660528113a4ddb9361ea94a84d4 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sun, 13 Mar 2016 12:16:39 +0000 Subject: [PATCH] provider/aws: Handle deleted CloudFormation stack during creation gracefully --- .../aws/resource_aws_cloudformation_stack.go | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/builtin/providers/aws/resource_aws_cloudformation_stack.go b/builtin/providers/aws/resource_aws_cloudformation_stack.go index 7ce22e48470d..e4d3d6090a88 100644 --- a/builtin/providers/aws/resource_aws_cloudformation_stack.go +++ b/builtin/providers/aws/resource_aws_cloudformation_stack.go @@ -145,16 +145,46 @@ func resourceAwsCloudFormationStackCreate(d *schema.ResourceData, meta interface } d.SetId(*resp.StackId) + creationTime := time.Now() + var deletionReasons []string wait := resource.StateChangeConf{ - Pending: []string{"CREATE_IN_PROGRESS", "ROLLBACK_IN_PROGRESS", "ROLLBACK_COMPLETE"}, + Pending: []string{ + "CREATE_IN_PROGRESS", + "DELETE_IN_PROGRESS", + "DELETE_COMPLETE", + "ROLLBACK_IN_PROGRESS", + "ROLLBACK_COMPLETE", + }, Target: []string{"CREATE_COMPLETE"}, Timeout: time.Duration(retryTimeout) * time.Minute, - MinTimeout: 5 * time.Second, + MinTimeout: 1 * time.Second, Refresh: func() (interface{}, string, error) { resp, err := conn.DescribeStacks(&cloudformation.DescribeStacksInput{ StackName: aws.String(d.Get("name").(string)), }) + if len(resp.Stacks) == 0 { + if d.Get("on_failure").(string) == "DELETE" { + log.Printf("[DEBUG] Deleting CloudFormation stack %q (%q) because creation failed: %q", + d.Get("name").(string), d.Id(), deletionReasons) + d.SetId("") + return resp, "", fmt.Errorf( + "Creation of CloudFormation stack %q failed: %q", + d.Get("name").(string), deletionReasons) + } else { + // This shouldn't happen unless CloudFormation is inconsistent + // See https://github.com/hashicorp/terraform/issues/5487 + log.Printf("[WARN] CloudFormation stack %q (%q) not found.\nresponse: %q\nfailures: %q", + d.Get("name").(string), d.Id(), resp, deletionReasons) + return resp, "", fmt.Errorf( + "CloudFormation stack %q vanished unexpectedly during creation.\n"+ + "Unless you knowingly manually deleted the stack "+ + "please report this as bug at https://github.com/hashicorp/terraform/issues\n"+ + "along with the config & Terraform version & the details below:\n"+ + "Full API response: %s\nCaptured reasons: %q\n", + d.Get("name").(string), resp, deletionReasons) + } + } status := *resp.Stacks[0].StackStatus log.Printf("[DEBUG] Current CloudFormation stack status: %q", status) @@ -168,6 +198,18 @@ func resourceAwsCloudFormationStackCreate(d *schema.ResourceData, meta interface return resp, "", fmt.Errorf("ROLLBACK_COMPLETE:\n%q", failures) } + + // There is a small chance we may not be able to catch this + // if stack becomes deleted in between retries + if status == "DELETE_IN_PROGRESS" { + failures, err := getCloudFormationFailures(aws.String(d.Get("name").(string)), creationTime, conn) + if err != nil { + return resp, "", fmt.Errorf( + "Failed getting details about deletion reasons: %q", err.Error()) + } + deletionReasons = failures + } + return resp, status, err }, } @@ -485,3 +527,10 @@ func cfStackEventIsFailure(event *cloudformation.StackEvent, afterTime time.Time return failRe.MatchString(*event.ResourceStatus) || rollbackRe.MatchString(*event.ResourceStatus) && event.Timestamp.After(afterTime) && event.ResourceStatusReason != nil } + +func cfStackEventIsStackDeletion(event *cloudformation.StackEvent, afterTime time.Time) bool { + return *event.ResourceStatus == "DELETE_IN_PROGRESS" && + *event.ResourceType == "AWS::CloudFormation::Stack" && + event.Timestamp.After(afterTime) && + event.ResourceStatusReason != nil +}