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  }