github.com/kubernetes-incubator/kube-aws@v0.16.4/cfnstack/cfnstack.go (about) 1 package cfnstack 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/aws/aws-sdk-go/aws" 8 "github.com/aws/aws-sdk-go/service/cloudformation" 9 "github.com/aws/aws-sdk-go/service/s3" 10 "github.com/kubernetes-incubator/kube-aws/logger" 11 ) 12 13 var CFN_TEMPLATE_SIZE_LIMIT = 51200 14 15 type CreationService interface { 16 CreateStack(*cloudformation.CreateStackInput) (*cloudformation.CreateStackOutput, error) 17 } 18 19 type UpdateService interface { 20 UpdateStack(input *cloudformation.UpdateStackInput) (*cloudformation.UpdateStackOutput, error) 21 } 22 23 type CRUDService interface { 24 CreateStack(*cloudformation.CreateStackInput) (*cloudformation.CreateStackOutput, error) 25 UpdateStack(input *cloudformation.UpdateStackInput) (*cloudformation.UpdateStackOutput, error) 26 DescribeStacks(input *cloudformation.DescribeStacksInput) (*cloudformation.DescribeStacksOutput, error) 27 DescribeStackEvents(input *cloudformation.DescribeStackEventsInput) (*cloudformation.DescribeStackEventsOutput, error) 28 EstimateTemplateCost(input *cloudformation.EstimateTemplateCostInput) (*cloudformation.EstimateTemplateCostOutput, error) 29 } 30 31 type S3ObjectPutterService interface { 32 PutObject(input *s3.PutObjectInput) (*s3.PutObjectOutput, error) 33 } 34 35 // Used for querying existance of stacks and nested stacks. 36 type CFInterrogator interface { 37 ListStackResources(input *cloudformation.ListStackResourcesInput) (*cloudformation.ListStackResourcesOutput, error) 38 DescribeStacks(input *cloudformation.DescribeStacksInput) (*cloudformation.DescribeStacksOutput, error) 39 } 40 41 func StackEventErrMsgs(events []*cloudformation.StackEvent) []string { 42 var errMsgs []string 43 44 for _, event := range events { 45 if aws.StringValue(event.ResourceStatus) == cloudformation.ResourceStatusCreateFailed { 46 // Only show actual failures, not cancelled dependent resources. 47 if aws.StringValue(event.ResourceStatusReason) != "Resource creation cancelled" { 48 errMsgs = append(errMsgs, 49 strings.TrimSpace( 50 strings.Join([]string{ 51 aws.StringValue(event.ResourceStatus), 52 aws.StringValue(event.ResourceType), 53 aws.StringValue(event.LogicalResourceId), 54 aws.StringValue(event.ResourceStatusReason), 55 }, " "))) 56 } 57 } 58 } 59 60 return errMsgs 61 } 62 63 func NestedStackExists(cf CFInterrogator, parentStackName, stackName string) (bool, error) { 64 logger.Debugf("testing whether nested stack '%s' is present in parent stack '%s'", stackName, parentStackName) 65 parentExists, err := StackExists(cf, parentStackName) 66 if err != nil { 67 return false, err 68 } 69 if !parentExists { 70 logger.Debugf("parent stack '%s' does not exist, so nested stack can not exist either", parentStackName) 71 return false, nil 72 } 73 74 req := &cloudformation.ListStackResourcesInput{StackName: &parentStackName} 75 logger.Debugf("calling AWS cloudformation ListStackResources for stack %s ->", parentStackName) 76 out, err := cf.ListStackResources(req) 77 if err != nil { 78 return false, fmt.Errorf("Could not read cf stack %s: %v", parentStackName, err) 79 } 80 if out == nil { 81 return false, nil 82 } 83 logger.Debugf("<- AWS responded with %d stack resources", len(out.StackResourceSummaries)) 84 for _, resource := range out.StackResourceSummaries { 85 if *resource.LogicalResourceId == stackName { 86 logger.Debugf("match! resource id '%s' exists", stackName) 87 return true, nil 88 } 89 } 90 logger.Debugf("no match! resource id '%s' does not exist", stackName) 91 return false, nil 92 } 93 94 func StackExists(cf CFInterrogator, stackName string) (bool, error) { 95 logger.Debugf("testing whether cf stack %s exists", stackName) 96 req := &cloudformation.DescribeStacksInput{} 97 req.StackName = &stackName 98 logger.Debug("calling AWS cloudformation DescribeStacks ->") 99 stacks, err := cf.DescribeStacks(req) 100 if err != nil { 101 if strings.HasPrefix(err.Error(), "ValidationError: Stack with id "+stackName+" does not exist") { 102 return false, nil 103 } 104 return false, fmt.Errorf("could not list cloudformation stacks: %v", err) 105 } 106 if stacks == nil { 107 logger.Debugf("<- AWS Responded with empty stacks object") 108 return false, nil 109 } 110 111 if stacks.Stacks != nil { 112 logger.Debugf("<- AWS Responded with %d stacks", len(stacks.Stacks)) 113 for _, summary := range stacks.Stacks { 114 if *summary.StackName == stackName { 115 logger.Debugf("found matching stack %s: %+v", *summary.StackName, *summary) 116 if summary.DeletionTime == nil { 117 logger.Debugf("stack is active - matched!") 118 return true, nil 119 } else { 120 logger.Debugf("stack is not active, ignoring") 121 } 122 } 123 } 124 } 125 logger.Debugf("found no active stacks with id %s", stackName) 126 return false, nil 127 }