cfndsl 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/README.md +327 -0
- data/lib/cfndsl.rb +0 -1
- data/lib/cfndsl/orchestration_template.rb +95 -105
- data/lib/cfndsl/resources.rb +2 -3
- data/lib/cfndsl/version.rb +1 -1
- data/spec/cloud_formation_template_spec.rb +5 -0
- data/spec/heat_template_spec.rb +5 -0
- data/spec/metadata_spec.rb +15 -0
- data/spec/resources_spec.rb +1 -14
- data/spec/spec_helper.rb +2 -0
- data/spec/support/shared_examples/orchestration_template.rb +97 -0
- metadata +9 -2
- data/lib/cfndsl/metadata.rb +0 -14
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
N2U4ZWRiYzQ2MTQ2OTAzMGFlNzM0YmY3N2Q2YWVmNDEzNDk3M2JiOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MzczODNhYWYzOWZkNjMyZTYyZDQyNTExMmE4OGY3NDk3YTk1ZDVhOA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ODc4ODAyNjQ2NDYzMTBjMWIzYjk5NTMzYWJjMWRlZTI2MjcxN2UzMzNmZTVh
|
10
|
+
NmE5ZjY3MzI2NmUxMGZhZjE0ZTZhOTM4NDZjZmM1NGI5ZTFiMjdkY2E4MWVm
|
11
|
+
ZDU0MWE3MzhkZjU2ZWZjMTA2MWM1ZTM4NjM4NGIxN2EwMTNkNmU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MDI2OThlM2M4NDQ3Njg4NmQ0ZjY2ODZlYjgxMTUzYzA3MGNmZjgzNWE4YjNk
|
14
|
+
MjczOTQwOGI4MjBjYTUyN2IyYTIyNDhlZGE0NTVmNDViNjRmZDA1NjhhY2Ix
|
15
|
+
ZTUyMzM5MjdmYzg1NWRlOTZjNTdkMmVjNWYyMTBhODk1MWM4MjE=
|
data/README.md
CHANGED
@@ -85,6 +85,333 @@ chris@raspberrypi:~/git/cfndsl$ cfndsl test.rb | json_pp
|
|
85
85
|
*Aside: that is correct - a significant amount of the development for
|
86
86
|
this gem was done on a [Raspberry Pi](http://www.raspberrypi.org).*
|
87
87
|
|
88
|
+
## Syntax
|
89
|
+
|
90
|
+
`cfndsl` comes with a number of helper methods defined on _each_ resource and/or the stack as a whole.
|
91
|
+
|
92
|
+
### Template Metadata
|
93
|
+
|
94
|
+
Metadata is a special template section described [here](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/metadata-section-structure.html). The argument supplied must be JSON-able. Some CloudFormation features reference special keys if included in the `Metadata`, check the AWS documentation for specifics.
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
CloudFormation do
|
98
|
+
Metadata(foo: 'bar')
|
99
|
+
|
100
|
+
EC2_Instance(:myInstance) do
|
101
|
+
ImageId 'ami-12345678'
|
102
|
+
Type 't1.micro'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
### Template Parameters
|
108
|
+
|
109
|
+
At a bare minumum, parameters need a name, and default to having Type `String`. Specify the parameter in the singular, not plural:
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
CloudFormation do
|
113
|
+
Parameter 'foo'
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
```json
|
118
|
+
{
|
119
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
120
|
+
"Parameters": {
|
121
|
+
"foo": {
|
122
|
+
"Type": "String"
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
```
|
127
|
+
|
128
|
+
However, they can accept all of the following additional keys per the [documentation](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html):
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
Parameter('foo') do
|
132
|
+
Description 'This is a sample parameter definition'
|
133
|
+
Type 'String'
|
134
|
+
Default 'foo'
|
135
|
+
NoEcho true
|
136
|
+
AllowedValues %w(foo bar)
|
137
|
+
AllowedPattern '/pattern/'
|
138
|
+
MaxLength 5
|
139
|
+
MinLength 3
|
140
|
+
MaxValue 10
|
141
|
+
MinValue 2
|
142
|
+
ConstraintDescription 'The error message printed when a parameter outside the constraints is given'
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Parameters can be referenced later in your template:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
EC2_Instance(:myInstance) do
|
150
|
+
InstanceType 'm3.xlarge'
|
151
|
+
UserData Ref('foo')
|
152
|
+
end
|
153
|
+
```
|
154
|
+
|
155
|
+
### Template Mappings
|
156
|
+
|
157
|
+
[Mappings](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html) are a hash-based lookup for your template. They can be specified in the singular or plural.
|
158
|
+
|
159
|
+
```ruby
|
160
|
+
CloudFormation do
|
161
|
+
Mapping('foo', letters: { a: 'a', b: 'b' }, numbers: { 1: 1, 2: 2 })
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
```json
|
166
|
+
{
|
167
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
168
|
+
"Mappings": {
|
169
|
+
"foo": {
|
170
|
+
"letters": {
|
171
|
+
"a": "a",
|
172
|
+
"b": "b"
|
173
|
+
},
|
174
|
+
"numbers": {
|
175
|
+
"one": 1,
|
176
|
+
"two": 2
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
}
|
181
|
+
}
|
182
|
+
```
|
183
|
+
|
184
|
+
You can then reference them later in your template using the `FnFindInMap` method:
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
EC2_Instance(:myInstance) do
|
188
|
+
InstanceType 'm3.xlarge'
|
189
|
+
UserData FnFindInMap('foo', :numbers, :one)
|
190
|
+
end
|
191
|
+
```
|
192
|
+
|
193
|
+
### Template Outputs
|
194
|
+
|
195
|
+
Outputs are declared one at a time and must be given a name and a value at a minimum, description is optional. Values are most typically obtained from other resources using `Ref` or `FnGetAtt`:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
CloudFormation do
|
199
|
+
EC2_Instance(:myInstance) do
|
200
|
+
ImageId 'ami-12345678'
|
201
|
+
Type 't1.micro'
|
202
|
+
end
|
203
|
+
|
204
|
+
Output(:myInstanceId) do
|
205
|
+
Description 'My instance Id'
|
206
|
+
Value Ref(:myInstance)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
```
|
210
|
+
|
211
|
+
```json
|
212
|
+
{
|
213
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
214
|
+
"Resources": {
|
215
|
+
"myInstance": {
|
216
|
+
"Properties": {
|
217
|
+
"ImageId": "ami-12345678"
|
218
|
+
},
|
219
|
+
"Type": "AWS::EC2::Instance"
|
220
|
+
}
|
221
|
+
},
|
222
|
+
"Outputs": {
|
223
|
+
"myInstanceId": {
|
224
|
+
"Description": "My instance Id",
|
225
|
+
"Value": {
|
226
|
+
"Ref": "myInstance"
|
227
|
+
}
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}
|
231
|
+
```
|
232
|
+
|
233
|
+
### Template Conditions
|
234
|
+
|
235
|
+
Conditions must be created with statements in three [sections](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html): a variable entry as a `Parameter`, a template-level `Condition` that holds the logic based upon the value of that `Parameter`, and a resource-level `Condition` that references the template-level one by logical id.
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
CloudFormation do
|
239
|
+
Parameter(:environment) do
|
240
|
+
Default 'development'
|
241
|
+
AllowedValues %w(production development)
|
242
|
+
end
|
243
|
+
|
244
|
+
Condition(:createResource, FnEquals(Ref(:environment), 'production'))
|
245
|
+
|
246
|
+
EC2_Instance(:myInstance) do
|
247
|
+
Condition :createResource
|
248
|
+
ImageId 'ami-12345678'
|
249
|
+
Type 't1.micro'
|
250
|
+
end
|
251
|
+
end
|
252
|
+
```
|
253
|
+
|
254
|
+
```json
|
255
|
+
{
|
256
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
257
|
+
"Parameters": {
|
258
|
+
"environment": {
|
259
|
+
"Type": "String",
|
260
|
+
"Default": "development",
|
261
|
+
"AllowedValues": [
|
262
|
+
"production",
|
263
|
+
"development"
|
264
|
+
]
|
265
|
+
}
|
266
|
+
},
|
267
|
+
"Conditions": {
|
268
|
+
"createResource": {
|
269
|
+
"Fn::Equals": [
|
270
|
+
{
|
271
|
+
"Ref": "environment"
|
272
|
+
},
|
273
|
+
"production"
|
274
|
+
]
|
275
|
+
}
|
276
|
+
},
|
277
|
+
"Resources": {
|
278
|
+
"myInstance": {
|
279
|
+
"Condition": "createResource",
|
280
|
+
"Properties": {
|
281
|
+
"ImageId": "ami-12345678"
|
282
|
+
},
|
283
|
+
"Type": "AWS::EC2::Instance"
|
284
|
+
}
|
285
|
+
}
|
286
|
+
}
|
287
|
+
```
|
288
|
+
|
289
|
+
### Template Resources
|
290
|
+
|
291
|
+
Cfndsl creates accessor methods for all of the resources listed [here](https://github.com/stevenjack/cfndsl/blob/master/lib/cfndsl/aws/types.yaml) and [here](https://github.com/stevenjack/cfndsl/blob/master/lib/cfndsl/os/types.yaml). If a resource is missing, or if you prefer to explicitly enter a resource in a template, you can do so. Keep in mind that since you are using the generic `Resource` class, you will also need to explicitly set the `Type` and that you no longer have access to the helper methods defined on that particular class, so you will have to use the `Property` method to set them.
|
292
|
+
|
293
|
+
```ruby
|
294
|
+
CloudFormation do
|
295
|
+
Resource(:myInstance) do
|
296
|
+
Type 'AWS::EC2::Instance'
|
297
|
+
Property('ImageId', 'ami-12345678')
|
298
|
+
Property('Type', 't1.micro')
|
299
|
+
end
|
300
|
+
|
301
|
+
# Will generate the same json as this
|
302
|
+
#
|
303
|
+
# EC2_Instance(:myInstance) do
|
304
|
+
# ImageId 'ami-12345678'
|
305
|
+
# Type 't1.micro'
|
306
|
+
# end
|
307
|
+
end
|
308
|
+
```
|
309
|
+
|
310
|
+
```json
|
311
|
+
{
|
312
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
313
|
+
"Resources": {
|
314
|
+
"myInstance": {
|
315
|
+
"Type": "AWS::ApiGateway::Resource",
|
316
|
+
"Properties": {
|
317
|
+
"ImageId": "ami-12345678",
|
318
|
+
"Type": "t1.micro"
|
319
|
+
}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
}
|
323
|
+
```
|
324
|
+
|
325
|
+
### Resource Types
|
326
|
+
|
327
|
+
When using the generic `Resource` method, rather than the dsl methods, specify the type of resource using `Type` amd the properties using `Property`. See [Template Resources](#template-resources) for an example.
|
328
|
+
|
329
|
+
### Resource Conditions
|
330
|
+
|
331
|
+
Resource conditions are specified singularly, referencing a template-level condition by logical id. See [Template Conditions](#template-conditions) for an example.
|
332
|
+
|
333
|
+
### Resource DependsOn
|
334
|
+
|
335
|
+
Resources can depend upon other resources explicitly using `DependsOn`. It accepts one or more logical ids.
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
CloudFormation do
|
339
|
+
EC2_Instance(:database) do
|
340
|
+
ImageId 'ami-12345678'
|
341
|
+
Type 't1.micro'
|
342
|
+
end
|
343
|
+
|
344
|
+
EC2_Instance(:webserver) do
|
345
|
+
DependsOn :database
|
346
|
+
ImageId 'ami-12345678'
|
347
|
+
Type 't1.micro'
|
348
|
+
end
|
349
|
+
end
|
350
|
+
```
|
351
|
+
|
352
|
+
```json
|
353
|
+
{
|
354
|
+
"AWSTemplateFormatVersion": "2010-09-09",
|
355
|
+
"Resources": {
|
356
|
+
"database": {
|
357
|
+
"Properties": {
|
358
|
+
"ImageId": "ami-12345678"
|
359
|
+
},
|
360
|
+
"Type": "AWS::EC2::Instance"
|
361
|
+
},
|
362
|
+
"webserver": {
|
363
|
+
"Properties": {
|
364
|
+
"ImageId": "ami-12345678"
|
365
|
+
},
|
366
|
+
"Type": "AWS::EC2::Instance",
|
367
|
+
"DependsOn": "database"
|
368
|
+
}
|
369
|
+
}
|
370
|
+
}
|
371
|
+
```
|
372
|
+
|
373
|
+
### Resource DeletionPolicy
|
374
|
+
|
375
|
+
Resources can have [deletion policies](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-deletionpolicy.html) associated with them. Specify them one per resource as an attribute:
|
376
|
+
|
377
|
+
```ruby
|
378
|
+
CloudFormation do
|
379
|
+
EC2_Instance(:myInstance) do
|
380
|
+
DeletionPolicy 'Retain'
|
381
|
+
ImageId 'ami-12345678'
|
382
|
+
Type 't1.micro'
|
383
|
+
end
|
384
|
+
end
|
385
|
+
```
|
386
|
+
|
387
|
+
### Resource Metadata
|
388
|
+
|
389
|
+
You can attach arbitrary [metadata](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-metadata.html) as an attribute. Arguments provided must be able to be JSON-ified:
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
CloudFormation do
|
393
|
+
EC2_Instance(:myInstance) do
|
394
|
+
Metadata(foo: 'bar')
|
395
|
+
ImageId 'ami-12345678'
|
396
|
+
Type 't1.micro'
|
397
|
+
end
|
398
|
+
end
|
399
|
+
```
|
400
|
+
|
401
|
+
### Resource CreationPolicy/UpdatePolicy
|
402
|
+
|
403
|
+
These attributes are only usable on particular [resources](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-creationpolicy.html). The name of the attribute is not arbitrary, it must match the policy name you are trying to attach. Different policies have different parameters.
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
CloudFormation do
|
407
|
+
EC2_Instance(:myInstance) do
|
408
|
+
ImageId 'ami-12345678'
|
409
|
+
Type 't1.micro'
|
410
|
+
CreationPolicy(:ResourceSignal, { Count: 1, Timeout: 'PT1M' })
|
411
|
+
end
|
412
|
+
end
|
413
|
+
```
|
414
|
+
|
88
415
|
## Samples
|
89
416
|
|
90
417
|
There is a more detailed example in the samples directory. The file
|
data/lib/cfndsl.rb
CHANGED
@@ -7,13 +7,9 @@ module CfnDsl
|
|
7
7
|
# Handles the overall template object
|
8
8
|
# rubocop:disable Metrics/ClassLength
|
9
9
|
class OrchestrationTemplate < JSONable
|
10
|
-
dsl_attr_setter :AWSTemplateFormatVersion, :Description
|
10
|
+
dsl_attr_setter :AWSTemplateFormatVersion, :Description, :Metadata
|
11
11
|
dsl_content_object :Condition, :Parameter, :Output, :Resource, :Mapping
|
12
12
|
|
13
|
-
def initialize
|
14
|
-
@AWSTemplateFormatVersion = '2010-09-09'
|
15
|
-
end
|
16
|
-
|
17
13
|
GlobalRefs = {
|
18
14
|
'AWS::NotificationARNs' => 1,
|
19
15
|
'AWS::Region' => 1,
|
@@ -23,6 +19,99 @@ module CfnDsl
|
|
23
19
|
'AWS::NoValue' => 1
|
24
20
|
}.freeze
|
25
21
|
|
22
|
+
class << self
|
23
|
+
def create_types
|
24
|
+
accessors = {}
|
25
|
+
types_mapping = {}
|
26
|
+
template_types['Resources'].each_pair do |resource, info|
|
27
|
+
resource_name = create_resource_def(resource, info)
|
28
|
+
parts = resource.split('::')
|
29
|
+
until parts.empty?
|
30
|
+
break if parts.first == 'Resource' # Don't allow us to define Resource as different method
|
31
|
+
abreve_name = parts.join('_')
|
32
|
+
if accessors.key? abreve_name
|
33
|
+
accessors.delete abreve_name # Delete potentially ambiguous names
|
34
|
+
else
|
35
|
+
accessors[abreve_name] = type_module.const_get resource_name
|
36
|
+
types_mapping[abreve_name] = resource
|
37
|
+
end
|
38
|
+
parts.shift
|
39
|
+
end
|
40
|
+
end
|
41
|
+
accessors.each_pair { |acc, res| create_resource_accessor(acc, res, types_mapping[acc]) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_resource_def(name, info)
|
45
|
+
resource = Class.new ResourceDefinition
|
46
|
+
resource_name = name.gsub(/::/, '_')
|
47
|
+
type_module.const_set(resource_name, resource)
|
48
|
+
info['Properties'].each_pair do |pname, ptype|
|
49
|
+
if ptype.is_a? Array
|
50
|
+
pclass = type_module.const_get ptype.first
|
51
|
+
create_array_property_def(resource, pname, pclass)
|
52
|
+
else
|
53
|
+
pclass = type_module.const_get ptype
|
54
|
+
create_property_def(resource, pname, pclass)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
resource_name
|
58
|
+
end
|
59
|
+
|
60
|
+
def create_property_def(resource, pname, pclass)
|
61
|
+
resource.class_eval do
|
62
|
+
CfnDsl.method_names(pname) do |method|
|
63
|
+
define_method(method) do |*values, &block|
|
64
|
+
values.push pclass.new if values.empty?
|
65
|
+
@Properties ||= {}
|
66
|
+
@Properties[pname] = PropertyDefinition.new(*values)
|
67
|
+
@Properties[pname].value.instance_eval(&block) if block
|
68
|
+
@Properties[pname].value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_array_property_def(resource, pname, pclass)
|
75
|
+
create_property_def(resource, pname, Array)
|
76
|
+
|
77
|
+
sname = CfnDsl::Plurals.singularize pname
|
78
|
+
|
79
|
+
unless sname == pname
|
80
|
+
resource.class_eval do
|
81
|
+
CfnDsl.method_names(sname) do |method|
|
82
|
+
define_method(method) do |value = nil, &block|
|
83
|
+
@Properties ||= {}
|
84
|
+
@Properties[pname] ||= PropertyDefinition.new([])
|
85
|
+
value = pclass.new unless value
|
86
|
+
@Properties[pname].value.push value
|
87
|
+
value.instance_eval(&block) if block
|
88
|
+
value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_resource_accessor(accessor, resource, type)
|
96
|
+
class_eval do
|
97
|
+
CfnDsl.method_names(accessor) do |method|
|
98
|
+
define_method(method) do |name, *values, &block|
|
99
|
+
name = name.to_s
|
100
|
+
@Resources ||= {}
|
101
|
+
@Resources[name] ||= resource.new(*values)
|
102
|
+
@Resources[name].instance_eval(&block) if block
|
103
|
+
@Resources[name].instance_variable_set('@Type', type)
|
104
|
+
@Resources[name]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize
|
112
|
+
@AWSTemplateFormatVersion = '2010-09-09'
|
113
|
+
end
|
114
|
+
|
26
115
|
# rubocop:disable Metrics/PerceivedComplexity
|
27
116
|
def valid_ref?(ref, origin = nil)
|
28
117
|
ref = ref.to_s
|
@@ -42,13 +131,11 @@ module CfnDsl
|
|
42
131
|
|
43
132
|
def check_refs
|
44
133
|
invalids = check_resource_refs + check_output_refs
|
45
|
-
|
46
|
-
invalids.empty? ? nil : invalids
|
134
|
+
invalids unless invalids.empty?
|
47
135
|
end
|
48
136
|
|
49
137
|
def check_resource_refs
|
50
138
|
invalids = []
|
51
|
-
|
52
139
|
@_resource_refs = {}
|
53
140
|
if @Resources
|
54
141
|
@Resources.keys.each do |resource|
|
@@ -60,13 +147,11 @@ module CfnDsl
|
|
60
147
|
end
|
61
148
|
end
|
62
149
|
end
|
63
|
-
|
64
150
|
invalids
|
65
151
|
end
|
66
152
|
|
67
153
|
def check_output_refs
|
68
154
|
invalids = []
|
69
|
-
|
70
155
|
output_refs = {}
|
71
156
|
if @Outputs
|
72
157
|
@Outputs.keys.each do |resource|
|
@@ -78,103 +163,8 @@ module CfnDsl
|
|
78
163
|
end
|
79
164
|
end
|
80
165
|
end
|
81
|
-
|
82
166
|
invalids
|
83
167
|
end
|
84
|
-
|
85
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
86
|
-
def self.create_types
|
87
|
-
names = {}
|
88
|
-
nametypes = {}
|
89
|
-
template_types['Resources'].each_pair do |name, type|
|
90
|
-
# Subclass ResourceDefintion and generate property methods
|
91
|
-
klass = Class.new(CfnDsl::ResourceDefinition)
|
92
|
-
klassname = name.split('::').join('_')
|
93
|
-
type_module.const_set(klassname, klass)
|
94
|
-
type['Properties'].each_pair do |pname, ptype|
|
95
|
-
if ptype.instance_of?(String)
|
96
|
-
create_klass = type_module.const_get(ptype)
|
97
|
-
|
98
|
-
klass.class_eval do
|
99
|
-
CfnDsl.method_names(pname) do |method|
|
100
|
-
define_method(method) do |*values, &block|
|
101
|
-
values.push create_klass.new if values.empty?
|
102
|
-
|
103
|
-
@Properties ||= {}
|
104
|
-
@Properties[pname] = CfnDsl::PropertyDefinition.new(*values)
|
105
|
-
@Properties[pname].value.instance_eval(&block) if block
|
106
|
-
@Properties[pname].value
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
else
|
111
|
-
# Array version
|
112
|
-
klass.class_eval do
|
113
|
-
CfnDsl.method_names(pname) do |method|
|
114
|
-
define_method(method) do |*values, &block|
|
115
|
-
values.push [] if values.empty?
|
116
|
-
@Properties ||= {}
|
117
|
-
@Properties[pname] ||= PropertyDefinition.new(*values)
|
118
|
-
@Properties[pname].value.instance_eval(&block) if block
|
119
|
-
@Properties[pname].value
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
sing_name = CfnDsl::Plurals.singularize(pname)
|
125
|
-
create_klass = type_module.const_get(ptype[0])
|
126
|
-
sing_names = sing_name == pname ? [ptype[0]] : [ptype[0], sing_name]
|
127
|
-
|
128
|
-
klass.class_eval do
|
129
|
-
sing_names.each do |sname|
|
130
|
-
CfnDsl.method_names(sname) do |method|
|
131
|
-
define_method(method) do |value = nil, &block|
|
132
|
-
@Properties ||= {}
|
133
|
-
@Properties[pname] ||= PropertyDefinition.new([])
|
134
|
-
value = create_klass.new unless value
|
135
|
-
@Properties[pname].value.push value
|
136
|
-
value.instance_eval(&block) if block
|
137
|
-
value
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
parts = name.split('::')
|
145
|
-
until parts.empty?
|
146
|
-
break if parts[0] == 'Resource'
|
147
|
-
abreve_name = parts.join('_')
|
148
|
-
if names.key?(abreve_name)
|
149
|
-
# this only happens if there is an ambiguity
|
150
|
-
names[abreve_name] = nil
|
151
|
-
else
|
152
|
-
names[abreve_name] = type_module.const_get(klassname)
|
153
|
-
nametypes[abreve_name] = name
|
154
|
-
end
|
155
|
-
parts.shift
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Define property setter methods for each of the unambiguous type names
|
160
|
-
names.each_pair do |typename, type|
|
161
|
-
next unless type
|
162
|
-
|
163
|
-
class_eval do
|
164
|
-
CfnDsl.method_names(typename) do |method|
|
165
|
-
define_method(method) do |name, *values, &block|
|
166
|
-
name = name.to_s
|
167
|
-
@Resources ||= {}
|
168
|
-
resource = @Resources[name] ||= type.new(*values)
|
169
|
-
resource.instance_eval(&block) if block
|
170
|
-
resource.instance_variable_set('@Type', nametypes[typename])
|
171
|
-
resource
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
178
168
|
end
|
179
169
|
# rubocop:enable Metrics/ClassLength
|
180
170
|
end
|
data/lib/cfndsl/resources.rb
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
require 'cfndsl/jsonable'
|
2
|
-
require 'cfndsl/metadata'
|
3
2
|
require 'cfndsl/properties'
|
4
3
|
require 'cfndsl/update_policy'
|
5
4
|
|
6
5
|
module CfnDsl
|
7
6
|
# Handles Resource objects
|
8
7
|
class ResourceDefinition < JSONable
|
9
|
-
dsl_attr_setter :Type, :DependsOn, :DeletionPolicy, :Condition
|
10
|
-
dsl_content_object :Property, :
|
8
|
+
dsl_attr_setter :Type, :DependsOn, :DeletionPolicy, :Condition, :Metadata
|
9
|
+
dsl_content_object :Property, :UpdatePolicy, :CreationPolicy
|
11
10
|
|
12
11
|
def addTag(name, value, propagate = nil)
|
13
12
|
add_tag(name, value, propagate)
|
data/lib/cfndsl/version.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Metadata' do
|
4
|
+
let(:template) { CfnDsl::OrchestrationTemplate.new }
|
5
|
+
|
6
|
+
it 'is settable for a template' do
|
7
|
+
template.Metadata(foo: 'bar')
|
8
|
+
expect(template.to_json).to match(/"Metadata":{"foo":"bar"}/)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'is settable for a resource' do
|
12
|
+
resource = template.Resource(:foo) { Metadata(foo: 'bar') }
|
13
|
+
expect(resource.to_json).to match(/"Metadata":{"foo":"bar"}/)
|
14
|
+
end
|
15
|
+
end
|
data/spec/resources_spec.rb
CHANGED
@@ -1,21 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe CfnDsl::ResourceDefinition do
|
4
|
-
subject { CfnDsl::CloudFormationTemplate.new.EC2_Instance(:single_server) }
|
5
|
-
context '#all_refs' do
|
6
|
-
it 'checks that the type is AWS::EC2::Instance' do
|
7
|
-
expect(subject.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
3
|
describe CfnDsl::ResourceDefinition do
|
13
4
|
subject { CfnDsl::CloudFormationTemplate.new.AutoScalingGroup(:web_servers) }
|
14
|
-
|
15
|
-
it 'checks that the type is AWS::AutoScaling::AutoScalingGroup' do
|
16
|
-
expect(subject.instance_variable_get('@Type')).to eq('AWS::AutoScaling::AutoScalingGroup')
|
17
|
-
end
|
18
|
-
end
|
5
|
+
|
19
6
|
context '#addTag' do
|
20
7
|
it 'is a pass-through method to add_tag' do
|
21
8
|
expect(subject).to receive(:add_tag).with('role', 'web-server', true)
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
shared_examples 'an orchestration template' do
|
2
|
+
context '#valid_ref?' do
|
3
|
+
it 'returns true if ref is global' do
|
4
|
+
expect(subject.valid_ref?('AWS::Region')).to eq(true)
|
5
|
+
end
|
6
|
+
|
7
|
+
it 'returns true if ref is a parameter' do
|
8
|
+
subject.Parameter(:foo)
|
9
|
+
expect(subject.valid_ref?(:foo)).to eq(true)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context '#check_refs' do
|
14
|
+
it 'returns an array of invalid refs if present' do
|
15
|
+
subject.EC2_Instance(:foo) { UserData Ref(:bar) }
|
16
|
+
expect(subject.check_refs).to_not be_empty
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns nil if no invalid refs are present' do
|
20
|
+
subject.EC2_Instance(:foo) { UserData Ref('AWS::Region') }
|
21
|
+
expect(subject.check_refs).to eq(nil)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context '#check_resource_refs' do
|
26
|
+
it 'returns an array with an error message if invalid refs are present' do
|
27
|
+
subject.EC2_Instance(:foo) { UserData Ref(:bar) }
|
28
|
+
expect(subject.check_resource_refs).to eq(['Invalid Reference: Resource foo refers to bar'])
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'returns an empty array ' do
|
32
|
+
subject.EC2_Instance(:foo) { UserData Ref('AWS::AccountId') }
|
33
|
+
expect(subject.check_resource_refs).to eq([])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context '#check_output_refs' do
|
38
|
+
it 'returns an array with an error message if invalid refs are present' do
|
39
|
+
subject.EC2_Instance(:foo)
|
40
|
+
subject.Output(:baz) { Value Ref(:bar) }
|
41
|
+
expect(subject.check_output_refs).to eq(['Invalid Reference: Output baz refers to bar'])
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns an empty array' do
|
45
|
+
subject.EC2_Instance(:foo)
|
46
|
+
subject.Output(:baz) { Value Ref(:foo) }
|
47
|
+
expect(subject.check_output_refs).to eq([])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context '.create_types' do
|
52
|
+
it 'creates a type class for each entry' do
|
53
|
+
expect(described_class.type_module).to be_const_defined('AWS_EC2_Instance')
|
54
|
+
expect(described_class.type_module.const_get('AWS_EC2_Instance')).to be < CfnDsl::ResourceDefinition
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'defines case-insensitive properties for each type class' do
|
58
|
+
ec2_instance = described_class.type_module.const_get('AWS_EC2_Instance').new
|
59
|
+
expect(ec2_instance).to respond_to(:imageId)
|
60
|
+
expect(ec2_instance).to respond_to(:ImageId)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'defines singular and plural methods for array properties' do
|
64
|
+
ec2_instance = described_class.type_module.const_get('AWS_EC2_Instance').new
|
65
|
+
ec2_instance.SecurityGroup(foo: 'bar')
|
66
|
+
singular_value = ec2_instance.instance_variable_get('@Properties')['SecurityGroups'].value
|
67
|
+
expect(singular_value).to eq([{ foo: 'bar' }])
|
68
|
+
ec2_instance = described_class.type_module.const_get('AWS_EC2_Instance').new
|
69
|
+
ec2_instance.SecurityGroups([{ foo: 'bar' }])
|
70
|
+
plural_value = ec2_instance.instance_variable_get('@Properties')['SecurityGroups'].value
|
71
|
+
expect(plural_value).to eq([{ foo: 'bar' }])
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'defines accessor methods for each of the entries' do
|
75
|
+
expect(subject).to respond_to(:AWS_EC2_Instance)
|
76
|
+
expect(subject).to respond_to(:EC2_Instance)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'avoids ambiguous accessor methods' do
|
80
|
+
expect(subject).to_not respond_to(:Instance)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'avoids duplicating singular and plural methods' do
|
84
|
+
security_group = described_class.type_module.const_get('AWS_EC2_SecurityGroup').new
|
85
|
+
security_group.SecurityGroupIngress([{ foo: 'bar' }])
|
86
|
+
plural_value = security_group.instance_variable_get('@Properties')['SecurityGroupIngress'].value
|
87
|
+
expect(plural_value).to eq([{ foo: 'bar' }])
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'sets the type of each resource correctly' do
|
91
|
+
ec2_instance = subject.EC2_Instance(:foo)
|
92
|
+
expect(ec2_instance.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
|
93
|
+
ec2_instance = subject.Resource(:bar) { Type 'AWS::EC2::Instance' }
|
94
|
+
expect(ec2_instance.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfndsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Jack
|
@@ -55,7 +55,6 @@ files:
|
|
55
55
|
- lib/cfndsl/json_serialisable_object.rb
|
56
56
|
- lib/cfndsl/jsonable.rb
|
57
57
|
- lib/cfndsl/mappings.rb
|
58
|
-
- lib/cfndsl/metadata.rb
|
59
58
|
- lib/cfndsl/module.rb
|
60
59
|
- lib/cfndsl/names.rb
|
61
60
|
- lib/cfndsl/orchestration_template.rb
|
@@ -88,14 +87,18 @@ files:
|
|
88
87
|
- sample/vpc_with_vpn_example.rb
|
89
88
|
- spec/cfndsl_spec.rb
|
90
89
|
- spec/cli_spec.rb
|
90
|
+
- spec/cloud_formation_template_spec.rb
|
91
91
|
- spec/external_parameters_spec.rb
|
92
92
|
- spec/fixtures/heattest.rb
|
93
93
|
- spec/fixtures/test.rb
|
94
|
+
- spec/heat_template_spec.rb
|
94
95
|
- spec/jsonable_spec.rb
|
96
|
+
- spec/metadata_spec.rb
|
95
97
|
- spec/names_spec.rb
|
96
98
|
- spec/plurals_spec.rb
|
97
99
|
- spec/resources_spec.rb
|
98
100
|
- spec/spec_helper.rb
|
101
|
+
- spec/support/shared_examples/orchestration_template.rb
|
99
102
|
homepage: https://github.com/stevenjack/cfndsl
|
100
103
|
licenses:
|
101
104
|
- MIT
|
@@ -124,11 +127,15 @@ summary: AWS Cloudformation DSL
|
|
124
127
|
test_files:
|
125
128
|
- spec/cfndsl_spec.rb
|
126
129
|
- spec/cli_spec.rb
|
130
|
+
- spec/cloud_formation_template_spec.rb
|
127
131
|
- spec/external_parameters_spec.rb
|
128
132
|
- spec/fixtures/heattest.rb
|
129
133
|
- spec/fixtures/test.rb
|
134
|
+
- spec/heat_template_spec.rb
|
130
135
|
- spec/jsonable_spec.rb
|
136
|
+
- spec/metadata_spec.rb
|
131
137
|
- spec/names_spec.rb
|
132
138
|
- spec/plurals_spec.rb
|
133
139
|
- spec/resources_spec.rb
|
134
140
|
- spec/spec_helper.rb
|
141
|
+
- spec/support/shared_examples/orchestration_template.rb
|
data/lib/cfndsl/metadata.rb
DELETED