1 | fs = require 'fs'
|
2 | vm = require 'vm'
|
3 | path = require 'path'
|
4 | CoffeeScript = require 'coffee-script'
|
5 |
|
6 | class CloudFormationTemplateContext
|
7 | constructor: ->
|
8 | @_resources = {}
|
9 | @_parameters = {}
|
10 | @_mappings = null
|
11 | @_outputs = {}
|
12 | @_description = null
|
13 | @_conditions = null
|
14 | @_metadatas = null
|
15 | @Params = {}
|
16 | @Resources = {}
|
17 | @Mappings = {}
|
18 | @Conditions = {}
|
19 | @AWS =
|
20 | AutoScaling:
|
21 | AutoScalingGroup: null
|
22 | LaunchConfiguration: null
|
23 | ScalingPolicy: null
|
24 | LifecycleHook: null
|
25 | ScheduledAction: null
|
26 | Trigger: null
|
27 | CloudFormation:
|
28 | Authentication: null
|
29 | CustomResource: null
|
30 | Stack: null
|
31 | WaitCondition: null
|
32 | WaitConditionHandle: null
|
33 | CloudFront:
|
34 | Distribution: null
|
35 | CloudWatch:
|
36 | Alarm: null
|
37 | CodeDeploy:
|
38 | Application: null
|
39 | DeploymentConfig: null
|
40 | DeploymentGroup: null
|
41 | CodePipeline:
|
42 | Pipeline: null
|
43 | CustomActionType: null
|
44 | Config:
|
45 | ConfigRule: null
|
46 | ConfigurationRecorder: null
|
47 | DeliveryChannel: null
|
48 | DirectoryService:
|
49 | MicrosoftAD: null
|
50 | SimpleAD: null
|
51 | CloudTrail:
|
52 | Trail: null
|
53 | DataPipeline:
|
54 | Pipeline: null
|
55 | DynamoDB:
|
56 | Table: null
|
57 | EC2:
|
58 | CustomerGateway: null
|
59 | DHCPOptions: null
|
60 | EIP: null
|
61 | EIPAssociation: null
|
62 | Instance: null
|
63 | InternetGateway: null
|
64 | NatGateway: null
|
65 | NetworkAcl: null
|
66 | NetworkAclEntry: null
|
67 | NetworkInterface: null
|
68 | PlacementGroup: null
|
69 | Route: null
|
70 | RouteTable: null
|
71 | SecurityGroup: null
|
72 | SecurityGroupIngress: null
|
73 | SecurityGroupEgress: null
|
74 | Subnet: null
|
75 | SubnetNetworkAclAssociation: null
|
76 | SubnetRouteTableAssociation: null
|
77 | SpotFleet: null
|
78 | Volume: null
|
79 | VolumeAttachment: null
|
80 | VPC: null
|
81 | VPCDHCPOptionsAssociation: null
|
82 | VPCEndpoint: null
|
83 | VPCGatewayAttachment: null
|
84 | VPNConnection: null
|
85 | VPNGateway: null
|
86 | ECR:
|
87 | Repository: null
|
88 | ECS:
|
89 | Cluster: null
|
90 | Service: null
|
91 | TaskDefinition: null
|
92 | ElastiCache:
|
93 | CacheCluster: null
|
94 | ParameterGroup: null
|
95 | ReplicationGroup: null
|
96 | SecurityGroup: null
|
97 | SecurityGroupIngress: null
|
98 | SubnetGroup: null
|
99 | ElasticBeanstalk:
|
100 | Application: null
|
101 | ApplicationVersion: null
|
102 | Environment: null
|
103 | ConfigurationTemplate: null
|
104 | ElasticLoadBalancing:
|
105 | LoadBalancer: null
|
106 | Elasticsearch:
|
107 | Domain: null
|
108 | EFS:
|
109 | FileSystem: null
|
110 | MountTarget: null
|
111 | EMR:
|
112 | Cluster: null
|
113 | InstanceGroupConfig: null
|
114 | Step: null
|
115 | GameLift:
|
116 | Alias: null
|
117 | Build: null
|
118 | Fleet: null
|
119 | IAM:
|
120 | AccessKey: null
|
121 | Group: null
|
122 | InstanceProfile: null
|
123 | ManagedPolicy: null
|
124 | Policy: null
|
125 | Role: null
|
126 | User: null
|
127 | UserToGroupAddition: null
|
128 | Kinesis:
|
129 | Stream: null
|
130 | KMS:
|
131 | Key: null
|
132 | Logs:
|
133 | Destination: null
|
134 | LogGroup: null
|
135 | LogStream: null
|
136 | MetricFilter: null
|
137 | SubscriptionFilter: null
|
138 | Lambda:
|
139 | Alias: null
|
140 | EventSourceMapping: null
|
141 | Function: null
|
142 | Permission: null
|
143 | Version: null
|
144 | OpsWorks:
|
145 | App: null
|
146 | Instance: null
|
147 | Layer: null
|
148 | Stack: null
|
149 | Redshift:
|
150 | Cluster: null
|
151 | ClusterParameterGroup: null
|
152 | ClusterSecurityGroup: null
|
153 | ClusterSubnetGroup: null
|
154 | RDS:
|
155 | DBCluster: null
|
156 | DBClusterParameterGroup: null
|
157 | DBInstance: null
|
158 | DBParameterGroup: null
|
159 | DBSubnetGroup: null
|
160 | DBSecurityGroup: null
|
161 | DBSecurityGroupIngress: null
|
162 | EventSubscription: null
|
163 | OptionGroup: null
|
164 | Route53:
|
165 | RecordSet: null
|
166 | RecordSetGroup: null
|
167 | HostedZone: null
|
168 | HealthCheck: null
|
169 | SDB:
|
170 | Domain: null
|
171 | S3:
|
172 | Bucket: null
|
173 | BucketPolicy: null
|
174 | SNS:
|
175 | Topic: null
|
176 | TopicPolicy: null
|
177 | SQS:
|
178 | Queue: null
|
179 | QueuePolicy: null
|
180 | SSM:
|
181 | Document: null
|
182 | WAF:
|
183 | ByteMatchSet: null
|
184 | IPSet: null
|
185 | Rule: null
|
186 | SqlInjectionMatchSet: null
|
187 | WebACL: null
|
188 | WorkSpaces:
|
189 | Workspace: null
|
190 | @Param =
|
191 | String: (name, arg1, arg2) => @_paramByType 'String', name, arg1, arg2
|
192 | Number: (name, arg1, arg2) => @_paramByType 'Number', name, arg1, arg2
|
193 | CommaDelimitedList: (name, arg1, arg2) => @_paramByType 'CommaDelimitedList', name, arg1, arg2
|
194 | AWS: (type, name, arg1, arg2) => @_paramByType "AWS::#{type}", name, arg1, arg2
|
195 | AWSList: (type, name, arg1, arg2) => @_paramByType "List<AWS::#{type}>", name, arg1, arg2
|
196 | @_buildCall null, null, 'AWS', @AWS
|
197 |
|
198 | _paramByType: (type, name, arg1, arg2) =>
|
199 | result = {}
|
200 | if not arg1?
|
201 | result[name] = {}
|
202 | else if not arg2?
|
203 | result[name] = if typeof arg1 is 'string' then Description: arg1 else arg1
|
204 | else
|
205 | result[name] = arg2
|
206 | result[name].Description = arg1
|
207 | result[name].Type = type
|
208 | @_set result, @_parameters
|
209 | @Params[name] = Ref: name
|
210 |
|
211 | _buildCall: (parent, lastKey, awsType, leaf) =>
|
212 | if leaf?
|
213 | for key, val of leaf
|
214 | @_buildCall leaf, key, "#{awsType}::#{key}", val
|
215 | return
|
216 | parent[lastKey] = (name, props) =>
|
217 | @_resourceByType awsType, name, props
|
218 |
|
219 |
|
220 | DeclareResource: (name) =>
|
221 | @Resources[name] ?= Ref: name
|
222 |
|
223 | _resourceByType: (type, name, props) =>
|
224 | result = {}
|
225 | if props?.Metadata? or props?.Properties? or props?.DependsOn? or props?.UpdatePolicy? or props?.CreationPolicy? or props?.Condition?
|
226 | result[name] = props
|
227 | result[name].Type = type
|
228 | else
|
229 | result[name] =
|
230 | Type: type
|
231 | Properties: props
|
232 | @_set result, @_resources
|
233 | @DeclareResource name
|
234 |
|
235 | _set: (source, target) ->
|
236 | for key, val of source
|
237 | target[key] = val
|
238 |
|
239 | Mapping: (name, map) =>
|
240 | @_mappings ?= {}
|
241 | result = {}
|
242 | result[name] = map
|
243 | @_set result, @_mappings
|
244 |
|
245 | Output: (name, args...) =>
|
246 | result = {}
|
247 | if args.length is 1
|
248 | result[name] =
|
249 | Value: args[0]
|
250 | if args.length is 2
|
251 | result[name] =
|
252 | Description: args[0]
|
253 | Value: args[1]
|
254 | @_set result, @_outputs
|
255 |
|
256 | Condition: (name, intrinsicfn) =>
|
257 | @_conditions ?= {}
|
258 | result = {}
|
259 | result[name] = intrinsicfn
|
260 | @_set result, @_conditions
|
261 |
|
262 | Description: (d) => @_description = d
|
263 |
|
264 | Metadata: (o) =>
|
265 | @_metadatas ?= {}
|
266 | @_set o, @_metadatas
|
267 |
|
268 | Tag: (key, val) ->
|
269 | Key: key
|
270 | Value: val
|
271 |
|
272 |
|
273 | Join: (delimiter, args...) ->
|
274 | if args.length is 1 and (args[0] instanceof Array)
|
275 | 'Fn::Join': [ delimiter, args[0] ]
|
276 | else
|
277 | 'Fn::Join': [ delimiter, args ]
|
278 | FindInMap: (args...) ->
|
279 | 'Fn::FindInMap': args
|
280 | GetAtt: (args...) ->
|
281 | 'Fn::GetAtt': args
|
282 | Base64: (arg) ->
|
283 | 'Fn::Base64': arg
|
284 | GetAZs: (arg) ->
|
285 | 'Fn::GetAZs': arg
|
286 | Select: (index, args...) ->
|
287 | if args.length is 1 and (args[0] instanceof Array)
|
288 | 'Fn::Select': [index, args[0]]
|
289 | else
|
290 | 'Fn::Select': [index, args]
|
291 | And: (conditions...) ->
|
292 | 'Fn::And': conditions
|
293 | Equals: (value_1, value_2) ->
|
294 | 'Fn::Equals': [ value_1, value_2 ]
|
295 | If: (condition, value_if_true, value_if_false) ->
|
296 | 'Fn::If': [condition, value_if_true, value_if_false]
|
297 | Not: (condition) ->
|
298 | 'Fn::Not': [condition]
|
299 | Or: (conditions...) ->
|
300 | 'Fn::Or': conditions
|
301 | AccountId: Ref: 'AWS::AccountId'
|
302 | NotificationARNs: Ref: 'AWS::NotificationARNs'
|
303 | NoValue: Ref: 'AWS::NoValue'
|
304 | Region: Ref: 'AWS::Region'
|
305 | StackId: Ref: 'AWS::StackId'
|
306 | StackName: Ref: 'AWS::StackName'
|
307 | InitScript: (arg) ->
|
308 | existsSyncFunc = if fs.existsSync? then fs.existsSync else path.existsSync
|
309 | if not existsSyncFunc(arg)
|
310 | text = arg
|
311 | else
|
312 | text = fs.readFileSync(arg).toString()
|
313 | chunks = []
|
314 |
|
315 | pattern = /((.|\n)*?)%{([^}?]+)}?((.|\n)*)/
|
316 | match = text.match pattern
|
317 | while match
|
318 | chunks.push match[1]
|
319 | compiled = CoffeeScript.compile match[3], {bare: true}
|
320 | chunks.push eval compiled
|
321 | text = match[4]
|
322 | match = text.match pattern
|
323 | chunks.push text if text and text.length > 0
|
324 | @Base64 @Join '', chunks
|
325 |
|
326 | module.exports.CloudFormationTemplateContext = CloudFormationTemplateContext
|
327 |
|
328 | module.exports = (func) ->
|
329 | context = new CloudFormationTemplateContext
|
330 | func.apply context, [context]
|
331 | template = AWSTemplateFormatVersion: '2010-09-09'
|
332 | template.Description = context._description if context._description?
|
333 | template.Parameters = context._parameters
|
334 | template.Mappings = context._mappings if context._mappings?
|
335 | template.Resources = context._resources
|
336 | template.Outputs = context._outputs
|
337 | template.Conditions = context._conditions if context._conditions?
|
338 | template.Metadata = context._metadatas if context._metadatas?
|
339 | template
|
340 |
|
341 | require('pkginfo')(module, 'version')
|