cumuliform 0.4.0 → 0.5.1
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 +4 -4
- data/CHANGELOG.md +31 -0
- data/README.md +614 -1
- data/Rakefile +1 -0
- data/cumuliform.gemspec +2 -1
- data/examples/condition_functions.rb +63 -0
- data/examples/fragments.rb +24 -0
- data/examples/intrinsic_functions.rb +90 -0
- data/examples/top-level-declarations.rb +47 -0
- data/exe/cumuliform +0 -0
- data/lib/cumuliform/fragments.rb +35 -16
- data/lib/cumuliform/functions.rb +156 -5
- data/lib/cumuliform/version.rb +1 -1
- metadata +23 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: abff1bdeb60a617a79f889e191789cc21dee2335
|
4
|
+
data.tar.gz: 8320b16dd011e3155aaeef164a4dce008634e46d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49b1742f90f29be0daacbd3c452cdda449100bc452ec831466661639c8fc28402e2cbdf833a04c5d4cecf1eeee97e4ccd03bf4717943032a4eb2813c407df54c
|
7
|
+
data.tar.gz: 20147ec9d4c9790d2ac5fc5fb78c5f3126582e2ed7c86c698be3bf25b6b7cf49bc625759e7a2e07d93309510fc1dba25f714ca1a2dc573e53eb514cce13f26f4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
This project adheres to [Semantic Versioning](http://semver.org/).
|
4
|
+
|
5
|
+
## [0.5.1] - 2015-12-03
|
6
|
+
### Changed
|
7
|
+
- Make some fragment-related functions private
|
8
|
+
|
9
|
+
### Deprecate
|
10
|
+
- Deprecate fragment definition use of `fragment()`: use `def_fragment()` for
|
11
|
+
that instead
|
12
|
+
|
13
|
+
### Added
|
14
|
+
- Add `def_fragment()` replacement for overloaded fragment-definition use of
|
15
|
+
`fragment()`
|
16
|
+
- Better API doc and examples for intrinsic functions
|
17
|
+
- API doc and examples for fragments
|
18
|
+
|
19
|
+
## [0.5.0] - 2015-12-03
|
20
|
+
Yanked - implemented 0.5.1's changes in a breaking way rather than deprecating
|
21
|
+
|
22
|
+
## [0.4.0] - 2015-06-10
|
23
|
+
### Added
|
24
|
+
- Add example to README
|
25
|
+
- Add command-line runner
|
26
|
+
- Add Rake task class
|
27
|
+
|
28
|
+
## [0.3.0] - 2015-05-27
|
29
|
+
### Added
|
30
|
+
- Initial public release covering all the basics (missing a couple of intrinsic
|
31
|
+
functions)
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Cumuliform
|
2
2
|
|
3
|
-
[](https://travis-ci.org/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform/coverage)
|
3
|
+
[](http://badge.fury.io/rb/cumuliform) [](https://travis-ci.org/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform) [](https://codeclimate.com/github/tape-tv/cumuliform/coverage)
|
4
4
|
|
5
5
|
Amazon’s [CloudFormation AWS service][cf] provides a way to describe
|
6
6
|
infrastructure stacks using a JSON template. We love CloudFormation, and use it
|
@@ -120,6 +120,8 @@ $ cumuliform simplest.rb simplest.cform
|
|
120
120
|
}
|
121
121
|
```
|
122
122
|
|
123
|
+
More detailed examples are below the section on Rake...
|
124
|
+
|
123
125
|
# Rake tasks and the Command Line runner
|
124
126
|
|
125
127
|
Cumuliform provides a very simple command-line runner to turn a `.rb` template
|
@@ -165,6 +167,617 @@ TARGETS = Rake::FileList['*.rb'].ext('.cform')
|
|
165
167
|
task :cform => TARGETS
|
166
168
|
```
|
167
169
|
|
170
|
+
# Examples
|
171
|
+
|
172
|
+
## Simple top-level object declarations
|
173
|
+
|
174
|
+
This example declares one of each of the top level objects Cumuliform
|
175
|
+
supports. More details can be found in CloudFormation's [Template
|
176
|
+
Anatomy documentation][cf-ta].
|
177
|
+
|
178
|
+
[cf-ta]: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
Cumuliform.template do
|
182
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
|
183
|
+
parameter 'AMI' do
|
184
|
+
{
|
185
|
+
Description: 'The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)',
|
186
|
+
Type: 'String',
|
187
|
+
Default: 'ami-accff2b1'
|
188
|
+
}
|
189
|
+
end
|
190
|
+
|
191
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html
|
192
|
+
mapping 'RegionAMI' do
|
193
|
+
{
|
194
|
+
'eu-central-1' => {
|
195
|
+
'hvm' => 'ami-accff2b1',
|
196
|
+
'pv' => 'ami-b6cff2ab'
|
197
|
+
},
|
198
|
+
'eu-west-1' => {
|
199
|
+
'hvm' => 'ami-47a23a30',
|
200
|
+
'pv' => 'ami-5da23a2a'
|
201
|
+
}
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html
|
206
|
+
condition 'Ireland' do
|
207
|
+
fn.equals(ref('AWS::Region'), 'eu-west-1')
|
208
|
+
end
|
209
|
+
|
210
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html
|
211
|
+
resource 'PrimaryInstance' do
|
212
|
+
{
|
213
|
+
Type: 'AWS::EC2::Instance',
|
214
|
+
Properties: {
|
215
|
+
ImageId: ref('AMI'),
|
216
|
+
InstanceType: 'm3.medium'
|
217
|
+
}
|
218
|
+
}
|
219
|
+
end
|
220
|
+
|
221
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
|
222
|
+
output 'PrimaryInstanceID' do
|
223
|
+
{
|
224
|
+
Value: ref('PrimaryInstance')
|
225
|
+
}
|
226
|
+
end
|
227
|
+
end
|
228
|
+
```
|
229
|
+
|
230
|
+
The generated template is:
|
231
|
+
|
232
|
+
```json
|
233
|
+
{
|
234
|
+
"Parameters": {
|
235
|
+
"AMI": {
|
236
|
+
"Description": "The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)",
|
237
|
+
"Type": "String",
|
238
|
+
"Default": "ami-accff2b1"
|
239
|
+
}
|
240
|
+
},
|
241
|
+
"Mappings": {
|
242
|
+
"RegionAMI": {
|
243
|
+
"eu-central-1": {
|
244
|
+
"hvm": "ami-accff2b1",
|
245
|
+
"pv": "ami-b6cff2ab"
|
246
|
+
},
|
247
|
+
"eu-west-1": {
|
248
|
+
"hvm": "ami-47a23a30",
|
249
|
+
"pv": "ami-5da23a2a"
|
250
|
+
}
|
251
|
+
}
|
252
|
+
},
|
253
|
+
"Conditions": {
|
254
|
+
"Ireland": {
|
255
|
+
"Fn::Equals": [
|
256
|
+
{
|
257
|
+
"Ref": "AWS::Region"
|
258
|
+
},
|
259
|
+
"eu-west-1"
|
260
|
+
]
|
261
|
+
}
|
262
|
+
},
|
263
|
+
"Resources": {
|
264
|
+
"PrimaryInstance": {
|
265
|
+
"Type": "AWS::EC2::Instance",
|
266
|
+
"Properties": {
|
267
|
+
"ImageId": {
|
268
|
+
"Ref": "AMI"
|
269
|
+
},
|
270
|
+
"InstanceType": "m3.medium"
|
271
|
+
}
|
272
|
+
}
|
273
|
+
},
|
274
|
+
"Outputs": {
|
275
|
+
"PrimaryInstanceID": {
|
276
|
+
"Value": {
|
277
|
+
"Ref": "PrimaryInstance"
|
278
|
+
}
|
279
|
+
}
|
280
|
+
}
|
281
|
+
}
|
282
|
+
```
|
283
|
+
|
284
|
+
Note that the optional `AWSTemplateFormatVersion`, `Description`, and
|
285
|
+
`Metadata` sections are *not* currently supported.
|
286
|
+
|
287
|
+
## Intrinsic functions
|
288
|
+
|
289
|
+
Cumuliform provides convenience wrappers for all the intrinsic functions. See
|
290
|
+
CloudFormation's [Intrinsic Function documentation][cf-if].
|
291
|
+
|
292
|
+
[cf-if]: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html
|
293
|
+
|
294
|
+
```ruby
|
295
|
+
Cumuliform.template do
|
296
|
+
mapping 'RegionAMI' do
|
297
|
+
{
|
298
|
+
'eu-central-1' => {
|
299
|
+
'hvm' => 'ami-accff2b1',
|
300
|
+
'pv' => 'ami-b6cff2ab'
|
301
|
+
},
|
302
|
+
'eu-west-1' => {
|
303
|
+
'hvm' => 'ami-47a23a30',
|
304
|
+
'pv' => 'ami-5da23a2a'
|
305
|
+
}
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
parameter 'VirtualizationMethod' do
|
310
|
+
{
|
311
|
+
Type: 'String',
|
312
|
+
Default: 'hvm'
|
313
|
+
}
|
314
|
+
end
|
315
|
+
|
316
|
+
resource 'PrimaryInstance' do
|
317
|
+
{
|
318
|
+
Type: 'AWS::EC2::Instance',
|
319
|
+
Properties: {
|
320
|
+
ImageId: fn.find_in_map('RegionAMI', ref('AWS::Region'),
|
321
|
+
ref('VirtualizationMethod')),
|
322
|
+
InstanceType: 'm3.medium',
|
323
|
+
AvailabilityZone: fn.select(0, fn.get_azs),
|
324
|
+
UserData: fn.base64(
|
325
|
+
fn.join('', [
|
326
|
+
"#!/bin/bash -xe\n",
|
327
|
+
"apt-get update\n",
|
328
|
+
"apt-get -y install python-pip python-docutils\n",
|
329
|
+
"pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
|
330
|
+
"/usr/local/bin/cfn-init",
|
331
|
+
" --region ", ref("AWS::Region"),
|
332
|
+
" --stack ", ref("AWS::StackId"),
|
333
|
+
" --resource #{xref('PrimaryInstance')}",
|
334
|
+
" --configsets db"
|
335
|
+
])
|
336
|
+
),
|
337
|
+
Metadata: {
|
338
|
+
'AWS::CloudFormation::Init' => {
|
339
|
+
configSets: { db: ['install'] },
|
340
|
+
install: {
|
341
|
+
commands: {
|
342
|
+
'01-apt' => {
|
343
|
+
command: 'apt-get install postgresql postgresql-contrib'
|
344
|
+
},
|
345
|
+
'02-db' => {
|
346
|
+
command: 'sudo -u postgres createdb the-db'
|
347
|
+
}
|
348
|
+
}
|
349
|
+
}
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
}
|
354
|
+
end
|
355
|
+
|
356
|
+
resource 'SiteDNS' do
|
357
|
+
{
|
358
|
+
Type: "AWS::Route53::RecordSet",
|
359
|
+
Properties: {
|
360
|
+
HostedZoneName: 'my-zone.example.org',
|
361
|
+
Name: 'service.my-zone.example.org',
|
362
|
+
ResourceRecords: [fn.get_att(xref('LoadBalancer'), 'DNSName')],
|
363
|
+
TTL: '900',
|
364
|
+
Type: 'CNAME'
|
365
|
+
}
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
resource 'LoadBalancer' do
|
370
|
+
{
|
371
|
+
Type: 'AWS::ElasticLoadBalancing::LoadBalancer',
|
372
|
+
Properties: {
|
373
|
+
AvailabilityZones: [fn.select(0, fn.get_azs)],
|
374
|
+
Listeners: [
|
375
|
+
{
|
376
|
+
InstancePort: 5432,
|
377
|
+
Protocol: 'TCP'
|
378
|
+
}
|
379
|
+
],
|
380
|
+
Instances: [ref('PrimaryInstance')]
|
381
|
+
}
|
382
|
+
}
|
383
|
+
end
|
384
|
+
end
|
385
|
+
```
|
386
|
+
|
387
|
+
The generated template is:
|
388
|
+
|
389
|
+
```json
|
390
|
+
{
|
391
|
+
"Parameters": {
|
392
|
+
"VirtualizationMethod": {
|
393
|
+
"Type": "String",
|
394
|
+
"Default": "hvm"
|
395
|
+
}
|
396
|
+
},
|
397
|
+
"Mappings": {
|
398
|
+
"RegionAMI": {
|
399
|
+
"eu-central-1": {
|
400
|
+
"hvm": "ami-accff2b1",
|
401
|
+
"pv": "ami-b6cff2ab"
|
402
|
+
},
|
403
|
+
"eu-west-1": {
|
404
|
+
"hvm": "ami-47a23a30",
|
405
|
+
"pv": "ami-5da23a2a"
|
406
|
+
}
|
407
|
+
}
|
408
|
+
},
|
409
|
+
"Resources": {
|
410
|
+
"PrimaryInstance": {
|
411
|
+
"Type": "AWS::EC2::Instance",
|
412
|
+
"Properties": {
|
413
|
+
"ImageId": {
|
414
|
+
"Fn::FindInMap": [
|
415
|
+
"RegionAMI",
|
416
|
+
{
|
417
|
+
"Ref": "AWS::Region"
|
418
|
+
},
|
419
|
+
{
|
420
|
+
"Ref": "VirtualizationMethod"
|
421
|
+
}
|
422
|
+
]
|
423
|
+
},
|
424
|
+
"InstanceType": "m3.medium",
|
425
|
+
"AvailabilityZone": {
|
426
|
+
"Fn::Select": [
|
427
|
+
"0",
|
428
|
+
{
|
429
|
+
"Fn::GetAZs": ""
|
430
|
+
}
|
431
|
+
]
|
432
|
+
},
|
433
|
+
"UserData": {
|
434
|
+
"Fn::Base64": {
|
435
|
+
"Fn::Join": [
|
436
|
+
"",
|
437
|
+
[
|
438
|
+
"#!/bin/bash -xe\n",
|
439
|
+
"apt-get update\n",
|
440
|
+
"apt-get -y install python-pip python-docutils\n",
|
441
|
+
"pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
|
442
|
+
"/usr/local/bin/cfn-init",
|
443
|
+
" --region ",
|
444
|
+
{
|
445
|
+
"Ref": "AWS::Region"
|
446
|
+
},
|
447
|
+
" --stack ",
|
448
|
+
{
|
449
|
+
"Ref": "AWS::StackId"
|
450
|
+
},
|
451
|
+
" --resource PrimaryInstance",
|
452
|
+
" --configsets db"
|
453
|
+
]
|
454
|
+
]
|
455
|
+
}
|
456
|
+
},
|
457
|
+
"Metadata": {
|
458
|
+
"AWS::CloudFormation::Init": {
|
459
|
+
"configSets": {
|
460
|
+
"db": [
|
461
|
+
"install"
|
462
|
+
]
|
463
|
+
},
|
464
|
+
"install": {
|
465
|
+
"commands": {
|
466
|
+
"01-apt": {
|
467
|
+
"command": "apt-get install postgresql postgresql-contrib"
|
468
|
+
},
|
469
|
+
"02-db": {
|
470
|
+
"command": "sudo -u postgres createdb the-db"
|
471
|
+
}
|
472
|
+
}
|
473
|
+
}
|
474
|
+
}
|
475
|
+
}
|
476
|
+
}
|
477
|
+
},
|
478
|
+
"SiteDNS": {
|
479
|
+
"Type": "AWS::Route53::RecordSet",
|
480
|
+
"Properties": {
|
481
|
+
"HostedZoneName": "my-zone.example.org",
|
482
|
+
"Name": "service.my-zone.example.org",
|
483
|
+
"ResourceRecords": [
|
484
|
+
{
|
485
|
+
"Fn::GetAtt": [
|
486
|
+
"LoadBalancer",
|
487
|
+
"DNSName"
|
488
|
+
]
|
489
|
+
}
|
490
|
+
],
|
491
|
+
"TTL": "900",
|
492
|
+
"Type": "CNAME"
|
493
|
+
}
|
494
|
+
},
|
495
|
+
"LoadBalancer": {
|
496
|
+
"Type": "AWS::ElasticLoadBalancing::LoadBalancer",
|
497
|
+
"Properties": {
|
498
|
+
"AvailabilityZones": [
|
499
|
+
{
|
500
|
+
"Fn::Select": [
|
501
|
+
"0",
|
502
|
+
{
|
503
|
+
"Fn::GetAZs": ""
|
504
|
+
}
|
505
|
+
]
|
506
|
+
}
|
507
|
+
],
|
508
|
+
"Listeners": [
|
509
|
+
{
|
510
|
+
"InstancePort": 5432,
|
511
|
+
"Protocol": "TCP"
|
512
|
+
}
|
513
|
+
],
|
514
|
+
"Instances": [
|
515
|
+
{
|
516
|
+
"Ref": "PrimaryInstance"
|
517
|
+
}
|
518
|
+
]
|
519
|
+
}
|
520
|
+
}
|
521
|
+
}
|
522
|
+
}
|
523
|
+
```
|
524
|
+
|
525
|
+
## Condition functions
|
526
|
+
|
527
|
+
Cumuliform provides convenience wrappers for all the Condition-related intrinsic functions. See
|
528
|
+
CloudFormation's [Condition Function documentation][cf-cif].
|
529
|
+
|
530
|
+
[cf-cif]: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html
|
531
|
+
|
532
|
+
```ruby
|
533
|
+
Cumuliform.template do
|
534
|
+
parameter 'AMI' do
|
535
|
+
{
|
536
|
+
Type: 'String',
|
537
|
+
Default: 'ami-12345678'
|
538
|
+
}
|
539
|
+
end
|
540
|
+
|
541
|
+
parameter 'UtilAMI' do
|
542
|
+
{
|
543
|
+
Type: 'String',
|
544
|
+
Default: 'ami-abcdef12'
|
545
|
+
}
|
546
|
+
end
|
547
|
+
|
548
|
+
parameter 'InstanceType' do
|
549
|
+
{
|
550
|
+
Description: "The instance type",
|
551
|
+
Type: 'String',
|
552
|
+
Default: 'c4.large'
|
553
|
+
}
|
554
|
+
end
|
555
|
+
|
556
|
+
condition 'InEU' do
|
557
|
+
fn.or(
|
558
|
+
fn.equals('eu-central-1', ref('AWS::Region')),
|
559
|
+
fn.equals('eu-west-1', ref('AWS::Region'))
|
560
|
+
)
|
561
|
+
end
|
562
|
+
|
563
|
+
condition 'UtilBox' do
|
564
|
+
fn.and(
|
565
|
+
fn.equals('m4.large', ref('InstanceType')),
|
566
|
+
{"Condition": xref('InEU')}
|
567
|
+
)
|
568
|
+
end
|
569
|
+
|
570
|
+
condition 'WebBox' do
|
571
|
+
fn.not(ref('UtilBox'))
|
572
|
+
end
|
573
|
+
|
574
|
+
resource 'WebInstance' do
|
575
|
+
{
|
576
|
+
Type: "AWS::EC2::Instance",
|
577
|
+
Condition: xref('WebBox'),
|
578
|
+
Properties: {
|
579
|
+
ImageId: ref('AMI'),
|
580
|
+
InstanceType: ref('InstanceType')
|
581
|
+
}
|
582
|
+
}
|
583
|
+
end
|
584
|
+
|
585
|
+
resource 'UtilInstance' do
|
586
|
+
{
|
587
|
+
Type: "AWS::EC2::Instance",
|
588
|
+
Condition: xref('UtilBox'),
|
589
|
+
Properties: {
|
590
|
+
ImageId: ref('UtilAMI'),
|
591
|
+
InstanceType: ref('InstanceType')
|
592
|
+
}
|
593
|
+
}
|
594
|
+
end
|
595
|
+
end
|
596
|
+
```
|
597
|
+
|
598
|
+
The generated template is:
|
599
|
+
|
600
|
+
```json
|
601
|
+
{
|
602
|
+
"Parameters": {
|
603
|
+
"AMI": {
|
604
|
+
"Type": "String",
|
605
|
+
"Default": "ami-12345678"
|
606
|
+
},
|
607
|
+
"UtilAMI": {
|
608
|
+
"Type": "String",
|
609
|
+
"Default": "ami-abcdef12"
|
610
|
+
},
|
611
|
+
"InstanceType": {
|
612
|
+
"Description": "The instance type",
|
613
|
+
"Type": "String",
|
614
|
+
"Default": "c4.large"
|
615
|
+
}
|
616
|
+
},
|
617
|
+
"Conditions": {
|
618
|
+
"InEU": {
|
619
|
+
"Fn::Or": [
|
620
|
+
{
|
621
|
+
"Fn::Equals": [
|
622
|
+
"eu-central-1",
|
623
|
+
{
|
624
|
+
"Ref": "AWS::Region"
|
625
|
+
}
|
626
|
+
]
|
627
|
+
},
|
628
|
+
{
|
629
|
+
"Fn::Equals": [
|
630
|
+
"eu-west-1",
|
631
|
+
{
|
632
|
+
"Ref": "AWS::Region"
|
633
|
+
}
|
634
|
+
]
|
635
|
+
}
|
636
|
+
]
|
637
|
+
},
|
638
|
+
"UtilBox": {
|
639
|
+
"Fn::And": [
|
640
|
+
{
|
641
|
+
"Fn::Equals": [
|
642
|
+
"m4.large",
|
643
|
+
{
|
644
|
+
"Ref": "InstanceType"
|
645
|
+
}
|
646
|
+
]
|
647
|
+
},
|
648
|
+
{
|
649
|
+
"Condition": "InEU"
|
650
|
+
}
|
651
|
+
]
|
652
|
+
},
|
653
|
+
"WebBox": {
|
654
|
+
"Fn::Not": [
|
655
|
+
{
|
656
|
+
"Ref": "UtilBox"
|
657
|
+
}
|
658
|
+
]
|
659
|
+
}
|
660
|
+
},
|
661
|
+
"Resources": {
|
662
|
+
"WebInstance": {
|
663
|
+
"Type": "AWS::EC2::Instance",
|
664
|
+
"Condition": "WebBox",
|
665
|
+
"Properties": {
|
666
|
+
"ImageId": {
|
667
|
+
"Ref": "AMI"
|
668
|
+
},
|
669
|
+
"InstanceType": {
|
670
|
+
"Ref": "InstanceType"
|
671
|
+
}
|
672
|
+
}
|
673
|
+
},
|
674
|
+
"UtilInstance": {
|
675
|
+
"Type": "AWS::EC2::Instance",
|
676
|
+
"Condition": "UtilBox",
|
677
|
+
"Properties": {
|
678
|
+
"ImageId": {
|
679
|
+
"Ref": "UtilAMI"
|
680
|
+
},
|
681
|
+
"InstanceType": {
|
682
|
+
"Ref": "InstanceType"
|
683
|
+
}
|
684
|
+
}
|
685
|
+
}
|
686
|
+
}
|
687
|
+
}
|
688
|
+
```
|
689
|
+
|
690
|
+
### xref
|
691
|
+
Quite often you'll need to use a Resource, Condition, or Parameter Logical ID
|
692
|
+
outside of a `{ "Ref" => "LogicalID" }`. Because Logical IDs are one of the
|
693
|
+
things we *can* check at evaluation time, we provide a function that simply
|
694
|
+
takes a Logical ID, checks it, then returns it. If the Logical ID isn't there
|
695
|
+
then it explodes with a `Cumuliform::Error::NoSuchLogicalId`.
|
696
|
+
|
697
|
+
```ruby
|
698
|
+
resource "Resource" do
|
699
|
+
{
|
700
|
+
Type: "AWS::EC2::Instance",
|
701
|
+
Condition: xref("TheCondition")
|
702
|
+
}
|
703
|
+
end
|
704
|
+
```
|
705
|
+
|
706
|
+
## Fragments
|
707
|
+
You'll often want to use a collection of resources several times in a template, and it can be pretty verbose and tedious. Cumuliform offers reusable fragments to allow you to reuse similar template chunks.
|
708
|
+
|
709
|
+
You define them with `def_fragment()` and use them with `fragment()`. You pass a name and a block to `def_fragment`. You call `fragment()` with the name of a fragment and an optional hash of options to pass to the fragment block. The fragment block is called and its return value output into the template.
|
710
|
+
|
711
|
+
Here's an example:
|
712
|
+
|
713
|
+
```ruby
|
714
|
+
Cumuliform.template do
|
715
|
+
parameter 'AMI' do
|
716
|
+
{
|
717
|
+
Description: 'The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)',
|
718
|
+
Type: 'String',
|
719
|
+
Default: 'ami-accff2b1'
|
720
|
+
}
|
721
|
+
end
|
722
|
+
|
723
|
+
def_fragment(:instance) do |opts|
|
724
|
+
resource opts[:logical_id] do
|
725
|
+
{
|
726
|
+
Type: 'AWS::EC2::Instance',
|
727
|
+
Properties: {
|
728
|
+
ImageId: ref('AMI'),
|
729
|
+
InstanceType: opts[:instance_type]
|
730
|
+
}
|
731
|
+
}
|
732
|
+
end
|
733
|
+
end
|
734
|
+
|
735
|
+
fragment(:instance, logical_id: 'LittleInstance', instance_type: 't2.micro')
|
736
|
+
fragment(:instance, logical_id: 'BigInstance', instance_type: 'c4.xlarge')
|
737
|
+
end
|
738
|
+
```
|
739
|
+
|
740
|
+
And the output:
|
741
|
+
|
742
|
+
```json
|
743
|
+
{
|
744
|
+
"Parameters": {
|
745
|
+
"AMI": {
|
746
|
+
"Description": "The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)",
|
747
|
+
"Type": "String",
|
748
|
+
"Default": "ami-accff2b1"
|
749
|
+
}
|
750
|
+
},
|
751
|
+
"Resources": {
|
752
|
+
"LittleInstance": {
|
753
|
+
"Type": "AWS::EC2::Instance",
|
754
|
+
"Properties": {
|
755
|
+
"ImageId": {
|
756
|
+
"Ref": "AMI"
|
757
|
+
},
|
758
|
+
"InstanceType": "t2.micro"
|
759
|
+
}
|
760
|
+
},
|
761
|
+
"BigInstance": {
|
762
|
+
"Type": "AWS::EC2::Instance",
|
763
|
+
"Properties": {
|
764
|
+
"ImageId": {
|
765
|
+
"Ref": "AMI"
|
766
|
+
},
|
767
|
+
"InstanceType": "c4.xlarge"
|
768
|
+
}
|
769
|
+
}
|
770
|
+
}
|
771
|
+
}
|
772
|
+
```
|
773
|
+
|
774
|
+
## Importing other templates
|
775
|
+
_TODO_
|
776
|
+
|
777
|
+
## Helpers
|
778
|
+
_TODO_
|
779
|
+
|
780
|
+
|
168
781
|
# Development
|
169
782
|
|
170
783
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
data/Rakefile
CHANGED
data/cumuliform.gemspec
CHANGED
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
|
12
12
|
spec.summary = %q{DSL library for generating AWS CloudFormation templates}
|
13
13
|
spec.description = <<-EOD
|
14
|
-
Simple DSL for
|
14
|
+
Simple DSL for generating AWS CloudFormation templates with an emphasis
|
15
15
|
on ensuring you don't shoot yourself in the foot by, e.g. referencing
|
16
16
|
non-existent resources because you have a typo.
|
17
17
|
EOD
|
@@ -26,4 +26,5 @@ non-existent resources because you have a typo.
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.9"
|
27
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
28
28
|
spec.add_development_dependency "rspec", "~> 3"
|
29
|
+
spec.add_development_dependency "yard", ">= 0.8"
|
29
30
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
Cumuliform.template do
|
2
|
+
parameter 'AMI' do
|
3
|
+
{
|
4
|
+
Type: 'String',
|
5
|
+
Default: 'ami-12345678'
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
parameter 'UtilAMI' do
|
10
|
+
{
|
11
|
+
Type: 'String',
|
12
|
+
Default: 'ami-abcdef12'
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
parameter 'InstanceType' do
|
17
|
+
{
|
18
|
+
Description: "The instance type",
|
19
|
+
Type: 'String',
|
20
|
+
Default: 'c4.large'
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
condition 'InEU' do
|
25
|
+
fn.or(
|
26
|
+
fn.equals('eu-central-1', ref('AWS::Region')),
|
27
|
+
fn.equals('eu-west-1', ref('AWS::Region'))
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
condition 'UtilBox' do
|
32
|
+
fn.and(
|
33
|
+
fn.equals('m4.large', ref('InstanceType')),
|
34
|
+
{"Condition": xref('InEU')}
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
condition 'WebBox' do
|
39
|
+
fn.not(ref('UtilBox'))
|
40
|
+
end
|
41
|
+
|
42
|
+
resource 'WebInstance' do
|
43
|
+
{
|
44
|
+
Type: "AWS::EC2::Instance",
|
45
|
+
Condition: xref('WebBox'),
|
46
|
+
Properties: {
|
47
|
+
ImageId: ref('AMI'),
|
48
|
+
InstanceType: ref('InstanceType')
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
resource 'UtilInstance' do
|
54
|
+
{
|
55
|
+
Type: "AWS::EC2::Instance",
|
56
|
+
Condition: xref('UtilBox'),
|
57
|
+
Properties: {
|
58
|
+
ImageId: ref('UtilAMI'),
|
59
|
+
InstanceType: ref('InstanceType')
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Cumuliform.template do
|
2
|
+
parameter 'AMI' do
|
3
|
+
{
|
4
|
+
Description: 'The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)',
|
5
|
+
Type: 'String',
|
6
|
+
Default: 'ami-accff2b1'
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def_fragment(:instance) do |opts|
|
11
|
+
resource opts[:logical_id] do
|
12
|
+
{
|
13
|
+
Type: 'AWS::EC2::Instance',
|
14
|
+
Properties: {
|
15
|
+
ImageId: ref('AMI'),
|
16
|
+
InstanceType: opts[:instance_type]
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
fragment(:instance, logical_id: 'LittleInstance', instance_type: 't2.micro')
|
23
|
+
fragment(:instance, logical_id: 'BigInstance', instance_type: 'c4.xlarge')
|
24
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
Cumuliform.template do
|
2
|
+
mapping 'RegionAMI' do
|
3
|
+
{
|
4
|
+
'eu-central-1' => {
|
5
|
+
'hvm' => 'ami-accff2b1',
|
6
|
+
'pv' => 'ami-b6cff2ab'
|
7
|
+
},
|
8
|
+
'eu-west-1' => {
|
9
|
+
'hvm' => 'ami-47a23a30',
|
10
|
+
'pv' => 'ami-5da23a2a'
|
11
|
+
}
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
parameter 'VirtualizationMethod' do
|
16
|
+
{
|
17
|
+
Type: 'String',
|
18
|
+
Default: 'hvm'
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
resource 'PrimaryInstance' do
|
23
|
+
{
|
24
|
+
Type: 'AWS::EC2::Instance',
|
25
|
+
Properties: {
|
26
|
+
ImageId: fn.find_in_map('RegionAMI', ref('AWS::Region'),
|
27
|
+
ref('VirtualizationMethod')),
|
28
|
+
InstanceType: 'm3.medium',
|
29
|
+
AvailabilityZone: fn.select(0, fn.get_azs),
|
30
|
+
UserData: fn.base64(
|
31
|
+
fn.join('', [
|
32
|
+
"#!/bin/bash -xe\n",
|
33
|
+
"apt-get update\n",
|
34
|
+
"apt-get -y install python-pip python-docutils\n",
|
35
|
+
"pip install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
|
36
|
+
"/usr/local/bin/cfn-init",
|
37
|
+
" --region ", ref("AWS::Region"),
|
38
|
+
" --stack ", ref("AWS::StackId"),
|
39
|
+
" --resource #{xref('PrimaryInstance')}",
|
40
|
+
" --configsets db"
|
41
|
+
])
|
42
|
+
),
|
43
|
+
Metadata: {
|
44
|
+
'AWS::CloudFormation::Init' => {
|
45
|
+
configSets: { db: ['install'] },
|
46
|
+
install: {
|
47
|
+
commands: {
|
48
|
+
'01-apt' => {
|
49
|
+
command: 'apt-get install postgresql postgresql-contrib'
|
50
|
+
},
|
51
|
+
'02-db' => {
|
52
|
+
command: 'sudo -u postgres createdb the-db'
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
resource 'SiteDNS' do
|
63
|
+
{
|
64
|
+
Type: "AWS::Route53::RecordSet",
|
65
|
+
Properties: {
|
66
|
+
HostedZoneName: 'my-zone.example.org',
|
67
|
+
Name: 'service.my-zone.example.org',
|
68
|
+
ResourceRecords: [fn.get_att(xref('LoadBalancer'), 'DNSName')],
|
69
|
+
TTL: '900',
|
70
|
+
Type: 'CNAME'
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
resource 'LoadBalancer' do
|
76
|
+
{
|
77
|
+
Type: 'AWS::ElasticLoadBalancing::LoadBalancer',
|
78
|
+
Properties: {
|
79
|
+
AvailabilityZones: [fn.select(0, fn.get_azs)],
|
80
|
+
Listeners: [
|
81
|
+
{
|
82
|
+
InstancePort: 5432,
|
83
|
+
Protocol: 'TCP'
|
84
|
+
}
|
85
|
+
],
|
86
|
+
Instances: [ref('PrimaryInstance')]
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
Cumuliform.template do
|
2
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html
|
3
|
+
parameter 'AMI' do
|
4
|
+
{
|
5
|
+
Description: 'The AMI id for our template (defaults to the stock Ubuntu 14.04 image in eu-central-1)',
|
6
|
+
Type: 'String',
|
7
|
+
Default: 'ami-accff2b1'
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html
|
12
|
+
mapping 'RegionAMI' do
|
13
|
+
{
|
14
|
+
'eu-central-1' => {
|
15
|
+
'hvm' => 'ami-accff2b1',
|
16
|
+
'pv' => 'ami-b6cff2ab'
|
17
|
+
},
|
18
|
+
'eu-west-1' => {
|
19
|
+
'hvm' => 'ami-47a23a30',
|
20
|
+
'pv' => 'ami-5da23a2a'
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html
|
26
|
+
condition 'Ireland' do
|
27
|
+
fn.equals(ref('AWS::Region'), 'eu-west-1')
|
28
|
+
end
|
29
|
+
|
30
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html
|
31
|
+
resource 'PrimaryInstance' do
|
32
|
+
{
|
33
|
+
Type: 'AWS::EC2::Instance',
|
34
|
+
Properties: {
|
35
|
+
ImageId: ref('AMI'),
|
36
|
+
InstanceType: 'm3.medium'
|
37
|
+
}
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# See http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html
|
42
|
+
output 'PrimaryInstanceID' do
|
43
|
+
{
|
44
|
+
Value: ref('PrimaryInstance')
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
data/exe/cumuliform
CHANGED
File without changes
|
data/lib/cumuliform/fragments.rb
CHANGED
@@ -2,18 +2,39 @@ require_relative 'error'
|
|
2
2
|
|
3
3
|
module Cumuliform
|
4
4
|
module Fragments
|
5
|
-
|
6
|
-
|
5
|
+
# Define a fragment for later use.
|
6
|
+
#
|
7
|
+
# Essentially stores a block under the name given for later use.
|
8
|
+
#
|
9
|
+
# @param name [Symbol] name of the fragment to define
|
10
|
+
# @yieldparam opts [Hash] will yield the options hash passed to
|
11
|
+
# <tt>#fragment()</tt> when called
|
12
|
+
# @raise [Error::FragmentAlreadyDefined] if the <tt>name</tt> is not unique
|
13
|
+
# in this template
|
14
|
+
def def_fragment(name, &block)
|
15
|
+
if fragments.has_key?(name)
|
16
|
+
raise Error::FragmentAlreadyDefined, name
|
17
|
+
end
|
18
|
+
fragments[name] = block
|
7
19
|
end
|
8
20
|
|
21
|
+
# Use an already-defined fragment
|
22
|
+
#
|
23
|
+
# Retrieves the block stored under <tt>name</tt> and calls it, passing any options.
|
24
|
+
#
|
25
|
+
# @param name [Symbol] The name of the fragment to use
|
26
|
+
# @param opts [Hash] Options to be passed to the fragment
|
27
|
+
# @return [Object<JSON-serialisable>] the return value of the called block
|
9
28
|
def fragment(name, *args, &block)
|
10
29
|
if block_given?
|
11
|
-
|
30
|
+
warn "fragment definition form (with block) is deprecated. Use #def_fragment instead"
|
31
|
+
def_fragment(name, *args, &block)
|
12
32
|
else
|
13
|
-
|
33
|
+
use_fragment(name, *args)
|
14
34
|
end
|
15
35
|
end
|
16
36
|
|
37
|
+
# @api private
|
17
38
|
def find_fragment(name)
|
18
39
|
local_fragment = fragments[name]
|
19
40
|
imports.reverse.reduce(local_fragment) { |fragment, import|
|
@@ -21,24 +42,22 @@ module Cumuliform
|
|
21
42
|
}
|
22
43
|
end
|
23
44
|
|
24
|
-
def has_fragment?(name)
|
25
|
-
!find_fragment(name).nil?
|
26
|
-
end
|
27
|
-
|
28
45
|
private
|
29
46
|
|
30
|
-
def
|
31
|
-
if fragments.has_key?(name)
|
32
|
-
raise Error::FragmentAlreadyDefined, name
|
33
|
-
end
|
34
|
-
fragments[name] = block
|
35
|
-
end
|
36
|
-
|
37
|
-
def include_fragment(name, opts = {})
|
47
|
+
def use_fragment(name, opts = {})
|
38
48
|
unless has_fragment?(name)
|
39
49
|
raise Error::FragmentNotFound, name
|
40
50
|
end
|
41
51
|
instance_exec(opts, &find_fragment(name))
|
42
52
|
end
|
53
|
+
|
54
|
+
|
55
|
+
def fragments
|
56
|
+
@fragments ||= {}
|
57
|
+
end
|
58
|
+
|
59
|
+
def has_fragment?(name)
|
60
|
+
!find_fragment(name).nil?
|
61
|
+
end
|
43
62
|
end
|
44
63
|
end
|
data/lib/cumuliform/functions.rb
CHANGED
@@ -2,55 +2,199 @@ require_relative 'error'
|
|
2
2
|
|
3
3
|
module Cumuliform
|
4
4
|
module Functions
|
5
|
+
# implements wrappers for the intrinsic functions Fn::*
|
5
6
|
class IntrinsicFunctions
|
6
7
|
attr_reader :template
|
7
8
|
|
9
|
+
# @api private
|
8
10
|
def initialize(template)
|
9
11
|
@template = template
|
10
12
|
end
|
11
13
|
|
14
|
+
# Wraps Fn::FindInMap
|
15
|
+
#
|
16
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-findinmap.html
|
17
|
+
#
|
18
|
+
# @param mapping_logical_id [String] The logical ID of the mapping we
|
19
|
+
# want to look up a value from
|
20
|
+
# @param level_1_key [String] Key 1
|
21
|
+
# @param level_2_key [String] Key 2
|
22
|
+
# @return [Hash] the Fn::FindInMap object
|
12
23
|
def find_in_map(mapping_logical_id, level_1_key, level_2_key)
|
13
24
|
template.verify_mapping_logical_id!(mapping_logical_id)
|
14
25
|
{"Fn::FindInMap" => [mapping_logical_id, level_1_key, level_2_key]}
|
15
26
|
end
|
16
27
|
|
28
|
+
# Wraps Fn::GetAtt
|
29
|
+
#
|
30
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
|
31
|
+
#
|
32
|
+
# @param resource_logical_id [String] The Logical ID of resource we want
|
33
|
+
# to get an attribute of
|
34
|
+
# @param attr_name [String] The name of the attribute to get the value of
|
35
|
+
# @return [Hash] the Fn::GetAtt object
|
17
36
|
def get_att(resource_logical_id, attr_name)
|
18
37
|
template.verify_resource_logical_id!(resource_logical_id)
|
19
38
|
{"Fn::GetAtt" => [resource_logical_id, attr_name]}
|
20
39
|
end
|
21
40
|
|
41
|
+
# Wraps Fn::Join
|
42
|
+
#
|
43
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-join.html
|
44
|
+
#
|
45
|
+
# @param separator [String] The separator string to join the array
|
46
|
+
# elements with
|
47
|
+
# @param args [Array<String>] The array of strings to join
|
48
|
+
# @return [Hash] the Fn::Join object
|
22
49
|
def join(separator, args)
|
23
50
|
raise ArgumentError, "Second argument must be an Array" unless args.is_a?(Array)
|
24
51
|
{"Fn::Join" => [separator, args]}
|
25
52
|
end
|
26
53
|
|
54
|
+
# Wraps Fn::Base64
|
55
|
+
#
|
56
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-base64.html
|
57
|
+
#
|
58
|
+
# The argument should either be a string or an intrinsic function that
|
59
|
+
# evaluates to a string when CloudFormation executes the template
|
60
|
+
#
|
61
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-base64.html
|
62
|
+
#
|
63
|
+
# @param value [String, Hash<string-returning instrinsic function>]
|
64
|
+
# The separator string to join the array elements with
|
65
|
+
# @return [Hash] the Fn::Base64 object
|
27
66
|
def base64(value)
|
28
67
|
{"Fn::Base64" => value}
|
29
68
|
end
|
30
69
|
|
70
|
+
# Wraps Fn::GetAZs
|
71
|
+
#
|
72
|
+
# CloudFormation evaluates this to an array of availability zone names.
|
73
|
+
#
|
74
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getavailabilityzones.html
|
75
|
+
# @param value [String, Hash<ref('AWS::Region')>] The AWS region to get
|
76
|
+
# the array of Availability Zones of. Empty string (the default) is
|
77
|
+
# equivalent to specifying `ref('AWS::Region')` which evaluates to the
|
78
|
+
# region the stack is being created in
|
31
79
|
def get_azs(value = "")
|
32
80
|
{"Fn::GetAZs" => value}
|
33
81
|
end
|
34
82
|
|
83
|
+
# Wraps Fn::Equals
|
84
|
+
#
|
85
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#d0e86148
|
86
|
+
#
|
87
|
+
# The arguments should be the literal values or refs you want to
|
88
|
+
# compare. Returns true or false when CloudFormation evaluates the
|
89
|
+
# template.
|
90
|
+
# @param value [String, Hash<value-returning ref>]
|
91
|
+
# @param other_value [String, Hash<value-returning ref>]
|
92
|
+
# @return [Hash] the Fn::Equals object
|
35
93
|
def equals(value, other_value)
|
36
94
|
{"Fn::Equals" => [value, other_value]}
|
37
95
|
end
|
38
96
|
|
97
|
+
# Wraps Fn::If
|
98
|
+
#
|
99
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#d0e86223
|
100
|
+
#
|
101
|
+
# CloudFormation evaluates the Condition referred to the logical ID in
|
102
|
+
# the <tt>condition</tt> arg and returns the <tt>true_value</tt> if
|
103
|
+
# <tt>true</tt> and <tt>false_value</tt> otherwise. <tt>condition</tt>
|
104
|
+
# cannot be an <tt>Fn::Ref</tt>, but you can use our <tt>xref()</tt>
|
105
|
+
# helper to ensure the logical ID is valid.
|
106
|
+
#
|
107
|
+
# @param condition[String] the Logical ID of the Condition to be checked
|
108
|
+
# @param true_value the value to be returned if <tt>condition</tt>
|
109
|
+
# evaluates true
|
110
|
+
# @param false_value the value to be returned if <tt>condition</tt>
|
111
|
+
# evaluates false
|
112
|
+
# @return [Hash] the Fn::If object
|
39
113
|
def if(condition, true_value, false_value)
|
40
114
|
{"Fn::If" => [condition, true_value, false_value]}
|
41
115
|
end
|
42
116
|
|
117
|
+
# Wraps Fn::Select
|
118
|
+
#
|
119
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-select.html
|
120
|
+
#
|
121
|
+
# CloudFormation evaluates the <tt>index</tt> (which can be an
|
122
|
+
# integer-as-a-string or a <tt>ref</tt> which evaluates to a number) and
|
123
|
+
# returns the corresponding item from the array (which can be an array
|
124
|
+
# literal, or the result of <tt>Fn::GetAZs</tt>, or one of
|
125
|
+
# <tt>Fn::GetAtt</tt>, <tt>Fn::If</tt>, and <tt>Ref</tt> (if they would
|
126
|
+
# return an Array).
|
127
|
+
#
|
128
|
+
# @param index [Integer, Hash<value-returning ref>] The index to
|
129
|
+
# retrieve from <tt>array</tt>
|
130
|
+
# @param array [Array, Hash<array-returning ref of intrinsic function>]
|
131
|
+
# The array to retrieve from
|
43
132
|
def select(index, array)
|
44
|
-
|
45
|
-
|
133
|
+
ref_style_index = index.is_a?(Hash) && index.has_key?("Fn::Ref")
|
134
|
+
positive_int_style_index = index.is_a?(Integer) && index >= 0
|
135
|
+
unless ref_style_index || positive_int_style_index
|
136
|
+
raise ArgumentError, "index must be a positive integer or Fn::Ref"
|
46
137
|
end
|
47
|
-
if
|
48
|
-
|
138
|
+
if positive_int_style_index
|
139
|
+
if array.is_a?(Array) && index >= array.length
|
140
|
+
raise IndexError, "index must be in the range 0 <= index < array.length"
|
141
|
+
end
|
142
|
+
index = index.to_s
|
49
143
|
end
|
50
|
-
{"Fn::Select" => [index
|
144
|
+
{"Fn::Select" => [index, array]}
|
145
|
+
end
|
146
|
+
|
147
|
+
# Wraps Fn::And
|
148
|
+
#
|
149
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#d0e86066
|
150
|
+
#
|
151
|
+
# Behaves as a logical AND operator for CloudFormation conditions. Arguments should be other conditions or things that will evaluate to <tt>true</tt> or <tt>false</tt>.
|
152
|
+
#
|
153
|
+
# @param condition_1 [Hash<boolean-returning ref, intrinsic function, or condition>] Condition / value to be ANDed
|
154
|
+
# @param condition_n [Hash<boolean-returning ref, intrinsic function, or condition>] Condition / value to be ANDed (min 2, max 10 condition args)
|
155
|
+
def and(*conditions)
|
156
|
+
unless (2..10).cover?(conditions.length)
|
157
|
+
raise ArgumentError, "You must specify AT LEAST 2 and AT MOST 10 conditions"
|
158
|
+
end
|
159
|
+
{"Fn::And" => conditions}
|
160
|
+
end
|
161
|
+
|
162
|
+
# Wraps Fn::Or
|
163
|
+
#
|
164
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#d0e86490
|
165
|
+
#
|
166
|
+
# Behaves as a logical OR operator for CloudFormation conditions. Arguments should be other conditions or things that will evaluate to <tt>true</tt> or <tt>false</tt>.
|
167
|
+
#
|
168
|
+
# @param condition_1 [Hash<boolean-returning ref, intrinsic function, or condition>] Condition / value to be ORed
|
169
|
+
# @param condition_n [Hash<boolean-returning ref, intrinsic function, or condition>] Condition / value to be ORed (min 2, max 10 condition args)
|
170
|
+
def or(*conditions)
|
171
|
+
unless (2..10).cover?(conditions.length)
|
172
|
+
raise ArgumentError, "You must specify AT LEAST 2 and AT MOST 10 conditions"
|
173
|
+
end
|
174
|
+
{"Fn::Or" => conditions}
|
175
|
+
end
|
176
|
+
|
177
|
+
# Wraps Fn::Not
|
178
|
+
#
|
179
|
+
# see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-conditions.html#d0e86402
|
180
|
+
#
|
181
|
+
# Behaves as a logical NOT operator for CloudFormation conditions. The argument should be another condition or something that will evaluate to <tt>true</tt> or <tt>false</tt>
|
182
|
+
# @param condition [Hash<boolean-returning ref, intrinsic function, or condition>] Condition / value to be NOTed
|
183
|
+
def not(condition)
|
184
|
+
{"Fn::Not" => [condition]}
|
51
185
|
end
|
52
186
|
end
|
53
187
|
|
188
|
+
# Checks <tt>logical_id</tt> is present and either returns <tt>logical_id</tt> or raises
|
189
|
+
# Cumuliform::Error::NoSuchLogicalId.
|
190
|
+
#
|
191
|
+
# You can use it anywhere you need a string Logical ID and want the
|
192
|
+
# protection of having it be verified, for example in the <tt>cfn-init</tt>
|
193
|
+
# invocation in a Cfn::Init metadata block or the condition name field
|
194
|
+
# of, e.g. Fn::And.
|
195
|
+
#
|
196
|
+
# @param logical_id [String] the logical ID you want to check
|
197
|
+
# @return [String] the logical_id param
|
54
198
|
def xref(logical_id)
|
55
199
|
unless has_logical_id?(logical_id)
|
56
200
|
raise Error::NoSuchLogicalId, logical_id
|
@@ -58,10 +202,17 @@ module Cumuliform
|
|
58
202
|
logical_id
|
59
203
|
end
|
60
204
|
|
205
|
+
# Wraps Ref
|
206
|
+
#
|
207
|
+
# CloudFormation evaluates the <tt>Ref</tt> and returns the value of the Parameter or Resource with Logical ID <tt>logical_id</tt>.
|
208
|
+
#
|
209
|
+
# @param logical_id [String] The logical ID of the parameter or resource
|
61
210
|
def ref(logical_id)
|
62
211
|
{"Ref" => xref(logical_id)}
|
63
212
|
end
|
64
213
|
|
214
|
+
# returns an instance of IntrinsicFunctions which provides wrappers for
|
215
|
+
# Fn::* functions
|
65
216
|
def fn
|
66
217
|
@fn ||= IntrinsicFunctions.new(self)
|
67
218
|
end
|
data/lib/cumuliform/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cumuliform
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Patterson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,8 +52,22 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
55
69
|
description: |
|
56
|
-
Simple DSL for
|
70
|
+
Simple DSL for generating AWS CloudFormation templates with an emphasis
|
57
71
|
on ensuring you don't shoot yourself in the foot by, e.g. referencing
|
58
72
|
non-existent resources because you have a typo.
|
59
73
|
email:
|
@@ -66,6 +80,7 @@ files:
|
|
66
80
|
- ".gitignore"
|
67
81
|
- ".ruby-version"
|
68
82
|
- ".travis.yml"
|
83
|
+
- CHANGELOG.md
|
69
84
|
- CODE_OF_CONDUCT.md
|
70
85
|
- Gemfile
|
71
86
|
- LICENSE.txt
|
@@ -75,7 +90,11 @@ files:
|
|
75
90
|
- bin/setup
|
76
91
|
- cumuliform.gemspec
|
77
92
|
- examples/Rakefile
|
93
|
+
- examples/condition_functions.rb
|
94
|
+
- examples/fragments.rb
|
95
|
+
- examples/intrinsic_functions.rb
|
78
96
|
- examples/simplest.rb
|
97
|
+
- examples/top-level-declarations.rb
|
79
98
|
- exe/cumuliform
|
80
99
|
- lib/cumuliform.rb
|
81
100
|
- lib/cumuliform/error.rb
|
@@ -111,3 +130,4 @@ signing_key:
|
|
111
130
|
specification_version: 4
|
112
131
|
summary: DSL library for generating AWS CloudFormation templates
|
113
132
|
test_files: []
|
133
|
+
has_rdoc:
|