github.com/mweagle/Sparta@v1.15.0/cloudformation_resources.go (about)

     1  package sparta
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"strings"
     7  	"text/template"
     8  
     9  	gocf "github.com/mweagle/go-cloudformation"
    10  	"github.com/sirupsen/logrus"
    11  )
    12  
    13  // Utility function to marshal an interface
    14  func marshalInterface(item interface{}) interface{} {
    15  	if item != nil {
    16  		return item
    17  	}
    18  	return nil
    19  }
    20  
    21  // Utility function to marshal an int
    22  func marshalInt(intVal int64) *gocf.IntegerExpr {
    23  	if intVal != 0 {
    24  		return gocf.Integer(intVal)
    25  	}
    26  	return nil
    27  }
    28  
    29  // Utility function to marshal a string
    30  func marshalString(stringVal string) *gocf.StringExpr {
    31  	if stringVal != "" {
    32  		return gocf.String(stringVal)
    33  	}
    34  	return nil
    35  }
    36  
    37  func marshalStringExpr(stringExpr gocf.Stringable) *gocf.StringExpr {
    38  	if stringExpr != nil {
    39  		return stringExpr.String()
    40  	}
    41  	return nil
    42  }
    43  
    44  // Utility function to marshal a string lsit
    45  func marshalStringList(stringVals []string) *gocf.StringListExpr {
    46  	if len(stringVals) != 0 {
    47  		stringableList := make([]gocf.Stringable, len(stringVals))
    48  		for eachIndex, eachStringVal := range stringVals {
    49  			stringableList[eachIndex] = gocf.String(eachStringVal)
    50  		}
    51  		return gocf.StringList(stringableList...)
    52  	}
    53  	return nil
    54  }
    55  
    56  // Utility function to marshal a boolean
    57  func marshalBool(boolValue bool) *gocf.BoolExpr {
    58  	if !boolValue {
    59  		return gocf.Bool(boolValue)
    60  	}
    61  	return nil
    62  }
    63  
    64  // resourceOutputs is responsible for returning the conditional
    65  // set of CloudFormation outputs for a given resource type.
    66  func resourceOutputs(resourceName string,
    67  	resource gocf.ResourceProperties,
    68  	logger *logrus.Logger) ([]string, error) {
    69  
    70  	outputProps := []string{}
    71  	switch typedResource := resource.(type) {
    72  	case gocf.IAMRole:
    73  		// NOP
    74  	case *gocf.DynamoDBTable:
    75  		if typedResource.StreamSpecification != nil {
    76  			outputProps = append(outputProps, "StreamArn")
    77  		}
    78  	case gocf.DynamoDBTable:
    79  		if typedResource.StreamSpecification != nil {
    80  			outputProps = append(outputProps, "StreamArn")
    81  		}
    82  	case gocf.KinesisStream,
    83  		*gocf.KinesisStream:
    84  		outputProps = append(outputProps, "Arn")
    85  	case gocf.Route53RecordSet,
    86  		*gocf.Route53RecordSet:
    87  		// NOP
    88  	case gocf.S3Bucket,
    89  		*gocf.S3Bucket:
    90  		outputProps = append(outputProps, "DomainName", "WebsiteURL")
    91  	case gocf.SNSTopic,
    92  		*gocf.SNSTopic:
    93  		outputProps = append(outputProps, "TopicName")
    94  	case gocf.SQSQueue,
    95  		*gocf.SQSQueue:
    96  		outputProps = append(outputProps, "Arn", "QueueName")
    97  	default:
    98  		logger.WithFields(logrus.Fields{
    99  			"ResourceType": fmt.Sprintf("%T", typedResource),
   100  		}).Warn("Discovery information for dependency not yet implemented")
   101  	}
   102  	return outputProps, nil
   103  }
   104  
   105  func newCloudFormationResource(resourceType string, logger *logrus.Logger) (gocf.ResourceProperties, error) {
   106  	resProps := gocf.NewResourceByType(resourceType)
   107  	if nil == resProps {
   108  		logger.WithFields(logrus.Fields{
   109  			"Type": resourceType,
   110  		}).Fatal("Failed to create CloudFormation CustomResource!")
   111  		return nil, fmt.Errorf("unsupported CustomResourceType: %s", resourceType)
   112  	}
   113  	return resProps, nil
   114  }
   115  
   116  type discoveryDataTemplate struct {
   117  	ResourceID         string
   118  	ResourceType       string
   119  	ResourceProperties string
   120  }
   121  
   122  var discoveryDataForResourceDependency = `
   123  	{
   124  		"ResourceID" : "<< .ResourceID >>",
   125  		"ResourceRef" : "{"Ref":"<< .ResourceID >>"}",
   126  		"ResourceType" : "<< .ResourceType >>",
   127  		"Properties" : {
   128  			<< .ResourceProperties >>
   129  		}
   130  	}
   131  `
   132  
   133  func discoveryResourceInfoForDependency(cfTemplate *gocf.Template,
   134  	logicalResourceName string,
   135  	logger *logrus.Logger) ([]byte, error) {
   136  
   137  	item, ok := cfTemplate.Resources[logicalResourceName]
   138  	if !ok {
   139  		return nil, nil
   140  	}
   141  	resourceOutputs, resourceOutputsErr := resourceOutputs(logicalResourceName,
   142  		item.Properties,
   143  		logger)
   144  	if resourceOutputsErr != nil {
   145  		return nil, resourceOutputsErr
   146  	}
   147  	// Template data
   148  	templateData := &discoveryDataTemplate{
   149  		ResourceID:   logicalResourceName,
   150  		ResourceType: item.Properties.CfnResourceType(),
   151  	}
   152  	quotedAttrs := make([]string, len(resourceOutputs))
   153  	for eachIndex, eachOutput := range resourceOutputs {
   154  		quotedAttrs[eachIndex] = fmt.Sprintf(`"%s" :"{ "Fn::GetAtt" : [ "%s", "%s" ] }"`,
   155  			eachOutput,
   156  			logicalResourceName,
   157  			eachOutput)
   158  	}
   159  	templateData.ResourceProperties = strings.Join(quotedAttrs, ",")
   160  
   161  	// Create the data that can be stuffed into Environment
   162  	discoveryTemplate, discoveryTemplateErr := template.New("discoveryResourceData").
   163  		Delims("<<", ">>").
   164  		Parse(discoveryDataForResourceDependency)
   165  	if nil != discoveryTemplateErr {
   166  		return nil, discoveryTemplateErr
   167  	}
   168  
   169  	var templateResults bytes.Buffer
   170  	evalResultErr := discoveryTemplate.Execute(&templateResults, templateData)
   171  	return templateResults.Bytes(), evalResultErr
   172  }
   173  func safeAppendDependency(resource *gocf.Resource, dependencyName string) {
   174  	if nil == resource.DependsOn {
   175  		resource.DependsOn = []string{}
   176  	}
   177  	resource.DependsOn = append(resource.DependsOn, dependencyName)
   178  }
   179  func safeMetadataInsert(resource *gocf.Resource, key string, value interface{}) {
   180  	if nil == resource.Metadata {
   181  		resource.Metadata = make(map[string]interface{})
   182  	}
   183  	resource.Metadata[key] = value
   184  }