-
Notifications
You must be signed in to change notification settings - Fork 48
/
cloudformation_resources.go
133 lines (122 loc) · 3.78 KB
/
cloudformation_resources.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package sparta
import (
"bytes"
"fmt"
"strings"
"text/template"
gocf "github.com/mweagle/go-cloudformation"
"github.com/sirupsen/logrus"
)
// resourceOutputs is responsible for returning the conditional
// set of CloudFormation outputs for a given resource type.
func resourceOutputs(resourceName string,
resource gocf.ResourceProperties,
logger *logrus.Logger) ([]string, error) {
outputProps := []string{}
switch typedResource := resource.(type) {
case gocf.IAMRole:
// NOP
case *gocf.DynamoDBTable:
if typedResource.StreamSpecification != nil {
outputProps = append(outputProps, "StreamArn")
}
case gocf.DynamoDBTable:
if typedResource.StreamSpecification != nil {
outputProps = append(outputProps, "StreamArn")
}
case gocf.KinesisStream,
*gocf.KinesisStream:
outputProps = append(outputProps, "Arn")
case gocf.Route53RecordSet,
*gocf.Route53RecordSet:
// NOP
case gocf.S3Bucket,
*gocf.S3Bucket:
outputProps = append(outputProps, "DomainName", "WebsiteURL")
case gocf.SNSTopic,
*gocf.SNSTopic:
outputProps = append(outputProps, "TopicName")
case gocf.SQSQueue,
*gocf.SQSQueue:
outputProps = append(outputProps, "Arn", "QueueName")
default:
logger.WithFields(logrus.Fields{
"ResourceType": fmt.Sprintf("%T", typedResource),
}).Warn("Discovery information for dependency not yet implemented")
}
return outputProps, nil
}
func newCloudFormationResource(resourceType string, logger *logrus.Logger) (gocf.ResourceProperties, error) {
resProps := gocf.NewResourceByType(resourceType)
if nil == resProps {
logger.WithFields(logrus.Fields{
"Type": resourceType,
}).Fatal("Failed to create CloudFormation CustomResource!")
return nil, fmt.Errorf("unsupported CustomResourceType: %s", resourceType)
}
return resProps, nil
}
type discoveryDataTemplate struct {
ResourceID string
ResourceType string
ResourceProperties string
}
var discoveryDataForResourceDependency = `
{
"ResourceID" : "<< .ResourceID >>",
"ResourceRef" : "{"Ref":"<< .ResourceID >>"}",
"ResourceType" : "<< .ResourceType >>",
"Properties" : {
<< .ResourceProperties >>
}
}
`
func discoveryResourceInfoForDependency(cfTemplate *gocf.Template,
logicalResourceName string,
logger *logrus.Logger) ([]byte, error) {
item, ok := cfTemplate.Resources[logicalResourceName]
if !ok {
return nil, nil
}
resourceOutputs, resourceOutputsErr := resourceOutputs(logicalResourceName,
item.Properties,
logger)
if resourceOutputsErr != nil {
return nil, resourceOutputsErr
}
// Template data
templateData := &discoveryDataTemplate{
ResourceID: logicalResourceName,
ResourceType: item.Properties.CfnResourceType(),
}
quotedAttrs := make([]string, len(resourceOutputs))
for eachIndex, eachOutput := range resourceOutputs {
quotedAttrs[eachIndex] = fmt.Sprintf(`"%s" :"{ "Fn::GetAtt" : [ "%s", "%s" ] }"`,
eachOutput,
logicalResourceName,
eachOutput)
}
templateData.ResourceProperties = strings.Join(quotedAttrs, ",")
// Create the data that can be stuffed into Environment
discoveryTemplate, discoveryTemplateErr := template.New("discoveryResourceData").
Delims("<<", ">>").
Parse(discoveryDataForResourceDependency)
if nil != discoveryTemplateErr {
return nil, discoveryTemplateErr
}
var templateResults bytes.Buffer
evalResultErr := discoveryTemplate.Execute(&templateResults, templateData)
return templateResults.Bytes(), evalResultErr
}
func safeAppendDependency(resource *gocf.Resource, dependencyName string) {
if nil == resource.DependsOn {
resource.DependsOn = []string{}
}
resource.DependsOn = append(resource.DependsOn, dependencyName)
}
func safeMetadataInsert(resource *gocf.Resource, key string, value interface{}) {
if nil == resource.Metadata {
resource.Metadata = make(map[string]interface{})
}
resource.Metadata[key] = value
}