func resourceAwsCloudFormationStackCreate(d *schema.ResourceData, meta interface{}) error { retryTimeout := int64(30) conn := meta.(*AWSClient).cfconn input := cloudformation.CreateStackInput{ StackName: aws.String(d.Get("name").(string)), } if v, ok := d.GetOk("template_body"); ok { template, _ := normalizeJsonString(v.(string)) input.TemplateBody = aws.String(template) } if v, ok := d.GetOk("template_url"); ok { input.TemplateURL = aws.String(v.(string)) } if v, ok := d.GetOk("capabilities"); ok { input.Capabilities = expandStringList(v.(*schema.Set).List()) } if v, ok := d.GetOk("disable_rollback"); ok { input.DisableRollback = aws.Bool(v.(bool)) } if v, ok := d.GetOk("notification_arns"); ok { input.NotificationARNs = expandStringList(v.(*schema.Set).List()) } if v, ok := d.GetOk("on_failure"); ok { input.OnFailure = aws.String(v.(string)) } if v, ok := d.GetOk("parameters"); ok { input.Parameters = expandCloudFormationParameters(v.(map[string]interface{})) } if v, ok := d.GetOk("policy_body"); ok { policy, _ := normalizeJsonString(v.(string)) input.StackPolicyBody = aws.String(policy) } if v, ok := d.GetOk("policy_url"); ok { input.StackPolicyURL = aws.String(v.(string)) } if v, ok := d.GetOk("tags"); ok { input.Tags = expandCloudFormationTags(v.(map[string]interface{})) } if v, ok := d.GetOk("timeout_in_minutes"); ok { m := int64(v.(int)) input.TimeoutInMinutes = aws.Int64(m) if m > retryTimeout { retryTimeout = m + 5 log.Printf("[DEBUG] CloudFormation timeout: %d", retryTimeout) } } log.Printf("[DEBUG] Creating CloudFormation Stack: %s", input) resp, err := conn.CreateStack(&input) if err != nil { return fmt.Errorf("Creating CloudFormation stack failed: %s", err.Error()) } d.SetId(*resp.StackId) var lastStatus string wait := resource.StateChangeConf{ Pending: []string{ "CREATE_IN_PROGRESS", "DELETE_IN_PROGRESS", "ROLLBACK_IN_PROGRESS", }, Target: []string{ "CREATE_COMPLETE", "CREATE_FAILED", "DELETE_COMPLETE", "DELETE_FAILED", "ROLLBACK_COMPLETE", "ROLLBACK_FAILED", }, Timeout: time.Duration(retryTimeout) * time.Minute, MinTimeout: 1 * time.Second, Refresh: func() (interface{}, string, error) { resp, err := conn.DescribeStacks(&cloudformation.DescribeStacksInput{ StackName: aws.String(d.Id()), }) if err != nil { log.Printf("[ERROR] Failed to describe stacks: %s", err) return nil, "", err } if len(resp.Stacks) == 0 { // This shouldn't happen unless CloudFormation is inconsistent // See https://github.com/hashicorp/terraform/issues/5487 log.Printf("[WARN] CloudFormation stack %q not found.\nresponse: %q", d.Id(), resp) 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\n", d.Id(), resp) } status := *resp.Stacks[0].StackStatus lastStatus = status log.Printf("[DEBUG] Current CloudFormation stack status: %q", status) return resp, status, err }, } _, err = wait.WaitForState() if err != nil { return err } if lastStatus == "ROLLBACK_COMPLETE" || lastStatus == "ROLLBACK_FAILED" { reasons, err := getCloudFormationRollbackReasons(d.Id(), nil, conn) if err != nil { return fmt.Errorf("Failed getting rollback reasons: %q", err.Error()) } return fmt.Errorf("%s: %q", lastStatus, reasons) } if lastStatus == "DELETE_COMPLETE" || lastStatus == "DELETE_FAILED" { reasons, err := getCloudFormationDeletionReasons(d.Id(), conn) if err != nil { return fmt.Errorf("Failed getting deletion reasons: %q", err.Error()) } d.SetId("") return fmt.Errorf("%s: %q", lastStatus, reasons) } if lastStatus == "CREATE_FAILED" { reasons, err := getCloudFormationFailures(d.Id(), conn) if err != nil { return fmt.Errorf("Failed getting failure reasons: %q", err.Error()) } return fmt.Errorf("%s: %q", lastStatus, reasons) } log.Printf("[INFO] CloudFormation Stack %q created", d.Id()) return resourceAwsCloudFormationStackRead(d, meta) }
func resourceAwsCloudFormationStackCreate(d *schema.ResourceData, meta interface{}) error { retryTimeout := int64(30) conn := meta.(*AWSClient).cfconn input := cloudformation.CreateStackInput{ StackName: aws.String(d.Get("name").(string)), } if v, ok := d.GetOk("template_body"); ok { input.TemplateBody = aws.String(normalizeJson(v.(string))) } if v, ok := d.GetOk("template_url"); ok { input.TemplateURL = aws.String(v.(string)) } if v, ok := d.GetOk("capabilities"); ok { input.Capabilities = expandStringList(v.(*schema.Set).List()) } if v, ok := d.GetOk("disable_rollback"); ok { input.DisableRollback = aws.Bool(v.(bool)) } if v, ok := d.GetOk("notification_arns"); ok { input.NotificationARNs = expandStringList(v.(*schema.Set).List()) } if v, ok := d.GetOk("on_failure"); ok { input.OnFailure = aws.String(v.(string)) } if v, ok := d.GetOk("parameters"); ok { input.Parameters = expandCloudFormationParameters(v.(map[string]interface{})) } if v, ok := d.GetOk("policy_body"); ok { input.StackPolicyBody = aws.String(normalizeJson(v.(string))) } if v, ok := d.GetOk("policy_url"); ok { input.StackPolicyURL = aws.String(v.(string)) } if v, ok := d.GetOk("tags"); ok { input.Tags = expandCloudFormationTags(v.(map[string]interface{})) } if v, ok := d.GetOk("timeout_in_minutes"); ok { m := int64(v.(int)) input.TimeoutInMinutes = aws.Int64(m) if m > retryTimeout { retryTimeout = m + 5 log.Printf("[DEBUG] CloudFormation timeout: %d", retryTimeout) } } log.Printf("[DEBUG] Creating CloudFormation Stack: %s", input) resp, err := conn.CreateStack(&input) if err != nil { return fmt.Errorf("Creating CloudFormation stack failed: %s", err.Error()) } d.SetId(*resp.StackId) wait := resource.StateChangeConf{ Pending: []string{"CREATE_IN_PROGRESS", "ROLLBACK_IN_PROGRESS", "ROLLBACK_COMPLETE"}, Target: []string{"CREATE_COMPLETE"}, Timeout: time.Duration(retryTimeout) * time.Minute, MinTimeout: 5 * time.Second, Refresh: func() (interface{}, string, error) { resp, err := conn.DescribeStacks(&cloudformation.DescribeStacksInput{ StackName: aws.String(d.Get("name").(string)), }) status := *resp.Stacks[0].StackStatus log.Printf("[DEBUG] Current CloudFormation stack status: %q", status) if status == "ROLLBACK_COMPLETE" { stack := resp.Stacks[0] failures, err := getCloudFormationFailures(stack.StackName, *stack.CreationTime, conn) if err != nil { return resp, "", fmt.Errorf( "Failed getting details about rollback: %q", err.Error()) } return resp, "", fmt.Errorf("ROLLBACK_COMPLETE:\n%q", failures) } return resp, status, err }, } _, err = wait.WaitForState() if err != nil { return err } log.Printf("[INFO] CloudFormation Stack %q created", d.Get("name").(string)) return resourceAwsCloudFormationStackRead(d, meta) }
stackDetails.Tags = map[string]string{"test-tag-key-1": "test-tag-value-1"} createStackInput.Tags = []*cloudformation.Tag{ &cloudformation.Tag{Key: aws.String("test-tag-key-1"), Value: aws.String("test-tag-value-1")}, } }) It("makes the proper call", func() { err := stack.Create(stackName, stackDetails) Expect(err).ToNot(HaveOccurred()) }) }) Context("when has TimeoutInMinutes", func() { BeforeEach(func() { stackDetails.TimeoutInMinutes = int64(1) createStackInput.TimeoutInMinutes = aws.Int64(int64(1)) }) It("makes the proper call", func() { err := stack.Create(stackName, stackDetails) Expect(err).ToNot(HaveOccurred()) }) }) Context("when creating the Stack fails", func() { BeforeEach(func() { createStackError = errors.New("operation failed") }) It("returns the proper error", func() { err := stack.Create(stackName, stackDetails)