cardtapp-cloudformation-ruby-dsl 0.0.1.pre.pre1
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 +7 -0
- data/.gitignore +30 -0
- data/.travis.yml +10 -0
- data/Gemfile +4 -0
- data/LICENSE +13 -0
- data/OWNERS +3 -0
- data/PULL_REQUEST_TEMPLATE.md +30 -0
- data/README.md +128 -0
- data/Rakefile +9 -0
- data/bin/cfntemplate-to-ruby +347 -0
- data/cloudformation-ruby-dsl.gemspec +46 -0
- data/docs/Contributing.md +22 -0
- data/docs/Releasing.md +20 -0
- data/examples/cloudformation-ruby-script.rb +274 -0
- data/examples/maps/domains.txt +4 -0
- data/examples/maps/map.json +9 -0
- data/examples/maps/map.rb +5 -0
- data/examples/maps/map.yaml +5 -0
- data/examples/maps/more_maps/map1.json +8 -0
- data/examples/maps/more_maps/map2.json +8 -0
- data/examples/maps/more_maps/map3.json +8 -0
- data/examples/maps/table.txt +5 -0
- data/examples/maps/vpc.txt +25 -0
- data/examples/simple_template.rb +21 -0
- data/examples/userdata.sh +4 -0
- data/initial_contributions.md +5 -0
- data/lib/cloudformation-ruby-dsl/cfntemplate.rb +731 -0
- data/lib/cloudformation-ruby-dsl/dsl.rb +380 -0
- data/lib/cloudformation-ruby-dsl/spotprice.rb +50 -0
- data/lib/cloudformation-ruby-dsl/table.rb +123 -0
- data/lib/cloudformation-ruby-dsl/version.rb +21 -0
- data/lib/cloudformation-ruby-dsl.rb +2 -0
- data/spec/spec_helper.rb +161 -0
- data/spec/validation_spec.rb +26 -0
- metadata +234 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9e310b865545fc3d4ee18fe2a2aea573fd0d6bc22dabc56cfb7f6005745fd2c8
|
4
|
+
data.tar.gz: 8c02987d1d661411c09fc0f8d915bacbf303f4b807420d1fe09c32ccab5a91ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0524a7f152ddc375dacdc5c8beb65027a0d7c9adc6869004c5ab41bd11cd52bc2dd14a4fb00ea35ac45055c2950df8f35d4b74daf4fd4a009c36ba50dbf7e6c3
|
7
|
+
data.tar.gz: 9f40c1be8e66ff6d068d7942fd0a72fa21e3163da9ad7e00223bd749486ecc4bb2391c8f72b52a88d0c67d444885a07ec6acbf775d95dc35d4479358069f07c9
|
data/.gitignore
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
*.swp
|
2
|
+
maestro.json
|
3
|
+
.rakeTasks
|
4
|
+
.DS_Store
|
5
|
+
target
|
6
|
+
*.iml
|
7
|
+
.idea/
|
8
|
+
*.swo
|
9
|
+
/manifests
|
10
|
+
/modules
|
11
|
+
cloudformation/expanded/
|
12
|
+
*.gem
|
13
|
+
*.rbc
|
14
|
+
.bundle
|
15
|
+
.config
|
16
|
+
.yardoc
|
17
|
+
Gemfile.lock
|
18
|
+
InstalledFiles
|
19
|
+
_yardoc
|
20
|
+
coverage
|
21
|
+
doc/
|
22
|
+
lib/bundler/man
|
23
|
+
pkg
|
24
|
+
rdoc
|
25
|
+
spec/reports
|
26
|
+
test/tmp
|
27
|
+
test/version_tmp
|
28
|
+
tmp
|
29
|
+
.rvmrc
|
30
|
+
/vendor/bundle
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2015 Bazaarvoice, Inc.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/OWNERS
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
## Description
|
2
|
+
Describe the objective of this PR.
|
3
|
+
|
4
|
+
## Steps to Test or Reproduce
|
5
|
+
Outline the steps to test or reproduce the PR here.
|
6
|
+
```
|
7
|
+
Code that reproduces the behaviour of the PR/to be fixed by the PR
|
8
|
+
```
|
9
|
+
### Environment
|
10
|
+
Describe the environment the bug or feature can be observed in. This may include platform, ruby version, DSL info, etc.
|
11
|
+
|
12
|
+
## Deploy Notes
|
13
|
+
If the change affects the way in which the gem is released, or requires a bump in version, note this here.
|
14
|
+
|
15
|
+
## Related Issues and PRs
|
16
|
+
|
17
|
+
### Issues
|
18
|
+
- [issue_link]()
|
19
|
+
|
20
|
+
### PRs
|
21
|
+
- [pr1]()
|
22
|
+
- [pr2]()
|
23
|
+
|
24
|
+
## Todos
|
25
|
+
- [x] Pull Request
|
26
|
+
- [ ] Tests
|
27
|
+
- [ ] Documentation
|
28
|
+
|
29
|
+
## Request to Review
|
30
|
+
@jonaf/@temujin9
|
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# cloudformation-ruby-dsl
|
2
|
+
|
3
|
+
A Ruby DSL and helper utilities for building CloudFormation templates dynamically.
|
4
|
+
|
5
|
+
Written by [Bazaarvoice](http://www.bazaarvoice.com): see [the contributors page](https://github.com/bazaarvoice/cloudformation-ruby-dsl/graphs/contributors) and [the initial contributions](https://github.com/bazaarvoice/cloudformation-ruby-dsl/blob/master/initial_contributions.md) for more details.
|
6
|
+
|
7
|
+
## Motivation
|
8
|
+
|
9
|
+
CloudFormation templates often contain repeated stanzas, information which must be loaded from external sources, and other functionality that would be easier handled as code, instead of configuration.
|
10
|
+
|
11
|
+
Consider when a userdata script needs to be added to a CloudFormation template. Traditionally, you would re-write the script by hand in a valid JSON format. Using the DSL, you can specify the file containing the script and generate the correct information at runtime.
|
12
|
+
|
13
|
+
:UserData => base64(interpolate(file('userdata.sh')))
|
14
|
+
|
15
|
+
Additionally, CloudFormation templates are just massive JSON documents, making general readability and reusability an issue. The DSL allows not only a cleaner format (and comments!), but will also allow the same DSL template to be reused as needed.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
Run `gem install cloudformation-ruby-dsl` to install system-wide.
|
20
|
+
|
21
|
+
To use in a specific project, add `gem 'cloudformation-ruby-dsl'` to your Gemfile, and then run `bundle`.
|
22
|
+
|
23
|
+
## Releasing
|
24
|
+
|
25
|
+
See [Releasing](docs/Releasing.md).
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
See [Contributing](docs/Contributing.md).
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
To convert existing JSON templates to use the DSL, run
|
34
|
+
|
35
|
+
cfntemplate-to-ruby [EXISTING_CFN] > [NEW_NAME.rb]
|
36
|
+
|
37
|
+
You may need to preface this with `bundle exec` if you installed via Bundler.
|
38
|
+
|
39
|
+
Make the resulting file executable (`chmod +x [NEW_NAME.rb]`). It can respond to the following subcommands (which are listed if you run without parameters):
|
40
|
+
- `expand`: output the JSON template to the command line (takes optional `--nopretty` to minimize the output)
|
41
|
+
- `diff`: compare an existing stack with your template. Produces following exit codes:
|
42
|
+
```
|
43
|
+
0 - no differences, nothing to update
|
44
|
+
1 - stack does not exist, template Validation error
|
45
|
+
2 - there are differences between an existing stack and your template
|
46
|
+
```
|
47
|
+
- `validate`: run validation against the stack definition
|
48
|
+
- `create`: create a new stack from the output (takes optional `--s3-bucket` to upload the template to the specified S3 bucket prior to creating the stack)
|
49
|
+
- `update`: update an existing stack from the output (takes optional `--s3-bucket` to upload the template to the specified S3 bucket prior to creating the stack). Produces following exit codes:
|
50
|
+
```
|
51
|
+
0 - update finished successfully
|
52
|
+
1 - no updates to perform, stack doesn't exist, unable to update immutable parameter or tag, AWS ServiceError exception
|
53
|
+
```
|
54
|
+
- `cancel-update`: cancel updating a stack
|
55
|
+
- `delete`: delete a stack (with prompt)
|
56
|
+
- `describe`: get output of an existing stack and output it (takes optional `--nopretty` to minimize output)
|
57
|
+
- `describe-resource`: given two arguments: stack-name and logical-resource-id, get output from a stack concerning the specific resource (takes optional `--nopretty` to minimize output)
|
58
|
+
- `get-template`: get entire template output of an existing stack
|
59
|
+
|
60
|
+
Command line options similar to cloudformation commands, but parsed by the dsl.
|
61
|
+
- `--profile`
|
62
|
+
- `--stack-name`
|
63
|
+
- `--region`
|
64
|
+
- `--parameters`
|
65
|
+
- `--tag`
|
66
|
+
|
67
|
+
Any other parameters are passed directly onto cloudformation. (--disable-rollback for instance)
|
68
|
+
|
69
|
+
Using the ruby scripts:
|
70
|
+
```
|
71
|
+
template.rb create --stack-name my_stack --parameters "BucketName=bucket-s3-static;SnsQueue=mysnsqueue"
|
72
|
+
```
|
73
|
+
|
74
|
+
Below are the various functions currently available in the DSL. See [the example script](examples/cloudformation-ruby-script.rb) for more usage information.
|
75
|
+
|
76
|
+
### DSL Statements
|
77
|
+
|
78
|
+
Add the named object to the appropriate collection.
|
79
|
+
- `parameter(name, options)` (may be marked :Immutable, which will raise error on a later change)
|
80
|
+
- `mapping(name, options)`
|
81
|
+
- `condition(name, conditions)`
|
82
|
+
- `resource(name, options)`
|
83
|
+
- `output(name, options)`
|
84
|
+
- `metadata(object)`
|
85
|
+
|
86
|
+
### CloudFormation Function Calls
|
87
|
+
|
88
|
+
Invoke an intrinsic CloudFormation function.
|
89
|
+
- `base64(value)`
|
90
|
+
- `find_in_map(map, key, name)`
|
91
|
+
- `get_att(resource, attribute)`
|
92
|
+
- `get_azs(region)`
|
93
|
+
- `split(delim, string)`
|
94
|
+
- `join(delim, *list)`
|
95
|
+
- `select(index, list)`
|
96
|
+
- `ref(name)`
|
97
|
+
- `import_value(value)`
|
98
|
+
- `sub(sub_string)`
|
99
|
+
- `sub(sub_string, var_map)`
|
100
|
+
|
101
|
+
Intrinsic conditionals are also supported, with some syntactic sugar.
|
102
|
+
- `fn_not(condition)`
|
103
|
+
- `fn_or(*condition_list)`
|
104
|
+
- `fn_and(*condition_list)`
|
105
|
+
- `fn_if(condition, value_if_true, value_if_false)`
|
106
|
+
- `equal(lhsOperand, rhsOperand)`
|
107
|
+
- `not_equal(lhsOperand, rhsOperand)`
|
108
|
+
|
109
|
+
Reference a CloudFormation pseudo parameter.
|
110
|
+
- `aws_account_id()`
|
111
|
+
- `aws_notification_arns()`
|
112
|
+
- `aws_no_value()`
|
113
|
+
- `aws_region()`
|
114
|
+
- `aws_stack_id()`
|
115
|
+
- `aws_stack_name()`
|
116
|
+
|
117
|
+
### Utility Functions
|
118
|
+
|
119
|
+
Additional capabilities for file inclusion, etc.
|
120
|
+
- `tag(tag_name, tag_options_hash)`: add tags to the stack, which are inherited by all resources in that stack. `tag_options_hash` includes `:Value=>value` and `:Immutable=>true` properties. `tag(tag_value_hash)` is deprecated and will be removed in a future version.
|
121
|
+
- `file(name)`: return the named file as a string, for further use
|
122
|
+
- `load_from_file(filename)`: load the named file by a given type; currently handles YAML, JSON, and Ruby
|
123
|
+
- `interpolate(string)`: embed CFN references into a string (`{{ref('Service')}}`) for later interpretation by the CFN engine
|
124
|
+
- `Table.load(filename)`: load a table from the listed file, which can then be turned into mappings (via `get_map`)
|
125
|
+
|
126
|
+
### Default Region
|
127
|
+
|
128
|
+
The tool defaults to region `us-east-1`. To change this set either `EC2_REGION` or `AWS_DEFAULT_REGION` in your environment.
|
data/Rakefile
ADDED
@@ -0,0 +1,347 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Copyright 2013-2014 Bazaarvoice, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
unless RUBY_VERSION >= '1.9'
|
18
|
+
# Ruby 1.9 preserves order within Hash objects which avoid scrambling the template output.
|
19
|
+
$stderr.puts "This script requires ruby 1.9+. On OS/X use Homebrew to install ruby 1.9:"
|
20
|
+
$stderr.puts " brew install ruby"
|
21
|
+
exit(2)
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'json'
|
25
|
+
|
26
|
+
def main(argv)
|
27
|
+
unless (argv & %w(-h --help -?)).empty?
|
28
|
+
$stderr.puts <<"EOF"
|
29
|
+
usage: #{$PROGRAM_NAME} [cloudformation-template.json] ...
|
30
|
+
|
31
|
+
Converts the specified CloudFormation JSON template or template fragment to
|
32
|
+
Ruby DSL syntax. Reads from stdin or from the specified json files. Note
|
33
|
+
that the input must be valid JSON.
|
34
|
+
|
35
|
+
Examples:
|
36
|
+
|
37
|
+
# Convert a JSON CloudFormation template to Ruby DSL syntax
|
38
|
+
#{$PROGRAM_NAME} my-template.json > my-template.rb
|
39
|
+
chmod +x my-template.rb
|
40
|
+
|
41
|
+
# Convert the JSON fragment in the clipboard to Ruby DSL syntax
|
42
|
+
pbpaste | #{$PROGRAM_NAME} | less
|
43
|
+
|
44
|
+
EOF
|
45
|
+
exit(2)
|
46
|
+
end
|
47
|
+
if argv.empty?
|
48
|
+
pprint(simplify(JSON.parse($stdin.read)))
|
49
|
+
else
|
50
|
+
argv.each do |filename|
|
51
|
+
pprint(simplify(JSON.parse(File.read(filename))))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# The user should make the resulting template executable w/chmod +x
|
55
|
+
end
|
56
|
+
|
57
|
+
class FnCall
|
58
|
+
attr_reader :name, :arguments, :multiline
|
59
|
+
|
60
|
+
def initialize(name, arguments, multiline = false)
|
61
|
+
@name = name
|
62
|
+
@arguments = arguments
|
63
|
+
@multiline = multiline
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s()
|
67
|
+
@name + "(" + @arguments.join(', ') + ")"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def pprint(val)
|
72
|
+
case detect_type(val)
|
73
|
+
when :template
|
74
|
+
pprint_cfn_template(val)
|
75
|
+
when :parameter
|
76
|
+
pprint_cfn_section 'parameter', 'TODO', val
|
77
|
+
when :resource
|
78
|
+
pprint_cfn_resource 'TODO', val
|
79
|
+
when :parameters
|
80
|
+
val.each { |k, v| pprint_cfn_section 'parameter', k, v }
|
81
|
+
when :resources
|
82
|
+
val.each { |k, v| pprint_cfn_resource k, v }
|
83
|
+
else
|
84
|
+
pprint_value(val, '')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Attempt to figure out what fragment of the template we have. This is imprecise and can't
|
89
|
+
# detect Mappings and Outputs sections reliably, so it doesn't attempt to.
|
90
|
+
def detect_type(val)
|
91
|
+
if val.is_a?(Hash) && val['AWSTemplateFormatVersion']
|
92
|
+
:template
|
93
|
+
elsif val.is_a?(Hash) && /^(String|Number)$/ =~ val['Type']
|
94
|
+
:parameter
|
95
|
+
elsif val.is_a?(Hash) && val['Type']
|
96
|
+
:resource
|
97
|
+
elsif val.is_a?(Hash) && val.values.all? { |v| detect_type(v) == :parameter }
|
98
|
+
:parameters
|
99
|
+
elsif val.is_a?(Hash) && val.values.all? { |v| detect_type(v) == :resource }
|
100
|
+
:resources
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def pprint_cfn_template(tpl)
|
105
|
+
puts "#!/usr/bin/env ruby"
|
106
|
+
puts
|
107
|
+
puts "require 'bundler/setup'"
|
108
|
+
puts "require 'cloudformation-ruby-dsl/cfntemplate'"
|
109
|
+
puts "require 'cloudformation-ruby-dsl/spotprice'"
|
110
|
+
puts "require 'cloudformation-ruby-dsl/table'"
|
111
|
+
puts
|
112
|
+
puts "template do"
|
113
|
+
puts
|
114
|
+
tpl.each do |section, v|
|
115
|
+
case section
|
116
|
+
when 'Metadata'
|
117
|
+
v.each { |name, options| pprint_cfn_section 'metadata', name, options }
|
118
|
+
when 'Parameters'
|
119
|
+
v.each { |name, options| pprint_cfn_section 'parameter', name, options }
|
120
|
+
when 'Mappings'
|
121
|
+
v.each { |name, options| pprint_cfn_section 'mapping', name, options }
|
122
|
+
when 'Resources'
|
123
|
+
v.each { |name, options| pprint_cfn_resource name, options }
|
124
|
+
when 'Conditions'
|
125
|
+
v.each { |name, options| pprint_cfn_section 'condition', name, options }
|
126
|
+
when 'Outputs'
|
127
|
+
v.each { |name, options| pprint_cfn_section 'output', name, options }
|
128
|
+
else
|
129
|
+
print " value #{fmt_key(section)} => "
|
130
|
+
pprint_value v, ' '
|
131
|
+
puts
|
132
|
+
puts
|
133
|
+
end
|
134
|
+
end
|
135
|
+
puts "end.exec!"
|
136
|
+
end
|
137
|
+
|
138
|
+
def pprint_cfn_section(section, name, options)
|
139
|
+
print " #{section} #{fmt_string(name)}"
|
140
|
+
indent = ' ' + (' ' * section.length) + ' '
|
141
|
+
options.each do |k, v|
|
142
|
+
puts ","
|
143
|
+
print indent, fmt_key(k), " => "
|
144
|
+
pprint_value v, indent
|
145
|
+
end
|
146
|
+
puts
|
147
|
+
puts
|
148
|
+
end
|
149
|
+
|
150
|
+
def pprint_cfn_resource(name, options)
|
151
|
+
print " resource #{fmt_string(name)}"
|
152
|
+
indent = ' '
|
153
|
+
options.each do |k, v|
|
154
|
+
unless k == 'Properties'
|
155
|
+
print ", #{fmt_key(k)} => #{fmt(v)}"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
if options.key?('Properties')
|
159
|
+
print ", #{fmt_key('Properties')} => "
|
160
|
+
pprint_value options['Properties'], indent
|
161
|
+
end
|
162
|
+
puts
|
163
|
+
puts
|
164
|
+
end
|
165
|
+
|
166
|
+
def pprint_value(val, indent)
|
167
|
+
# Prefer to print the value on a single line if it's reasonable to do so
|
168
|
+
single_line = is_single_line(val) || is_single_line_hack(val)
|
169
|
+
if single_line && !is_multi_line_hack(val)
|
170
|
+
s = fmt(val)
|
171
|
+
if s.length < 120 || is_single_line_hack(val)
|
172
|
+
print s
|
173
|
+
return
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Print the value across multiple lines
|
178
|
+
if val.is_a?(Hash)
|
179
|
+
puts "{"
|
180
|
+
val.each do |k, v|
|
181
|
+
print "#{indent} #{fmt_key(k)} => "
|
182
|
+
pprint_value v, indent + ' '
|
183
|
+
puts ","
|
184
|
+
end
|
185
|
+
print "#{indent}}"
|
186
|
+
|
187
|
+
elsif val.is_a?(Array)
|
188
|
+
puts "["
|
189
|
+
val.each do |v|
|
190
|
+
print "#{indent} "
|
191
|
+
pprint_value v, indent + ' '
|
192
|
+
puts ","
|
193
|
+
end
|
194
|
+
print "#{indent}]"
|
195
|
+
|
196
|
+
elsif val.is_a?(FnCall) && val.multiline
|
197
|
+
print val.name, "("
|
198
|
+
args = val.arguments
|
199
|
+
sep = ''
|
200
|
+
sub_indent = indent + ' '
|
201
|
+
if val.name == 'join' && args.length > 1
|
202
|
+
pprint_value args[0], indent + ' '
|
203
|
+
args = args[1..-1]
|
204
|
+
sep = ','
|
205
|
+
sub_indent = indent + ' '
|
206
|
+
end
|
207
|
+
unless args.empty?
|
208
|
+
args.each do |v|
|
209
|
+
puts sep
|
210
|
+
print sub_indent
|
211
|
+
pprint_value v, sub_indent
|
212
|
+
sep = ','
|
213
|
+
end
|
214
|
+
if val.name == 'join' && args.length > 1
|
215
|
+
print ","
|
216
|
+
end
|
217
|
+
puts
|
218
|
+
print indent
|
219
|
+
end
|
220
|
+
print ")"
|
221
|
+
|
222
|
+
else
|
223
|
+
print fmt(val)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def is_single_line(val)
|
228
|
+
if val.is_a?(Hash)
|
229
|
+
is_single_line(val.values)
|
230
|
+
elsif val.is_a?(Array)
|
231
|
+
val.empty? ||
|
232
|
+
(val.length == 1 && is_single_line(val[0]) && !val[0].is_a?(Hash)) ||
|
233
|
+
val.all? { |v| v.is_a?(String) }
|
234
|
+
else
|
235
|
+
true
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
# Emo-specific hacks to force the desired output formatting
|
240
|
+
def is_single_line_hack(val)
|
241
|
+
is_array_of_strings_hack(val)
|
242
|
+
end
|
243
|
+
|
244
|
+
# Emo-specific hacks to force the desired output formatting
|
245
|
+
def is_multi_line_hack(val)
|
246
|
+
val.is_a?(Hash) && val['email']
|
247
|
+
end
|
248
|
+
|
249
|
+
# Emo-specific hacks to force the desired output formatting
|
250
|
+
def is_array_of_strings_hack(val)
|
251
|
+
val.is_a?(Array) && val.all? { |v| v.is_a?(String) } && val.grep(/\s/).empty? && (
|
252
|
+
val.include?('autoscaling:EC2_INSTANCE_LAUNCH') ||
|
253
|
+
val.include?('m1.small')
|
254
|
+
)
|
255
|
+
end
|
256
|
+
|
257
|
+
def fmt(val)
|
258
|
+
if val == {}
|
259
|
+
'{}'
|
260
|
+
elsif val == []
|
261
|
+
'[]'
|
262
|
+
elsif val.is_a?(Hash)
|
263
|
+
'{ ' + (val.map { |k,v| fmt_key(k) + ' => ' + fmt(v) }).join(', ') + ' }'
|
264
|
+
elsif val.is_a?(Array) && is_array_of_strings_hack(val)
|
265
|
+
'%w(' + val.join(' ') + ')'
|
266
|
+
elsif val.is_a?(Array)
|
267
|
+
'[ ' + (val.map { |v| fmt(v) }).join(', ') + ' ]'
|
268
|
+
elsif val.is_a?(FnCall) && val.arguments.empty?
|
269
|
+
val.name
|
270
|
+
elsif val.is_a?(FnCall)
|
271
|
+
val.name + '(' + (val.arguments.map { |v| fmt(v) }).join(', ') + ')'
|
272
|
+
elsif val.is_a?(String)
|
273
|
+
fmt_string(val)
|
274
|
+
elsif val == nil
|
275
|
+
'null'
|
276
|
+
else
|
277
|
+
val.to_s # number, boolean
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def fmt_key(s)
|
282
|
+
':' + (/^[a-zA-Z_]\w+$/ =~ s ? s : fmt_string(s)) # returns a symbol like :Foo or :'us-east-1'
|
283
|
+
end
|
284
|
+
|
285
|
+
def fmt_string(s)
|
286
|
+
if /[^ -~]/ =~ s
|
287
|
+
s.dump # contains, non-ascii or control char, return double-quoted string
|
288
|
+
else
|
289
|
+
'\'' + s.gsub(/([\\'])/, '\\\\\1') + '\'' # return single-quoted string, escape \ and '
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def many(val)
|
294
|
+
if val.is_a?(Array)
|
295
|
+
val
|
296
|
+
else
|
297
|
+
[ val ]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def simplify(val)
|
302
|
+
if val.is_a?(Hash)
|
303
|
+
val = Hash[val.map { |k,v| [k, simplify(v)] }]
|
304
|
+
if val.length != 1
|
305
|
+
val
|
306
|
+
else
|
307
|
+
k, v = val.entries[0]
|
308
|
+
# CloudFormation functions
|
309
|
+
case
|
310
|
+
when k == 'Fn::Base64'
|
311
|
+
FnCall.new 'base64', [v], true
|
312
|
+
when k == 'Fn::FindInMap'
|
313
|
+
FnCall.new 'find_in_map', v
|
314
|
+
when k == 'Fn::GetAtt'
|
315
|
+
FnCall.new 'get_att', v
|
316
|
+
when k == 'Fn::GetAZs'
|
317
|
+
FnCall.new 'get_azs', v != '' ? [v] : []
|
318
|
+
when k == 'Fn::Join'
|
319
|
+
FnCall.new 'join', [v[0]] + many(v[1]), true
|
320
|
+
when k == 'Fn::Select'
|
321
|
+
FnCall.new 'select', v
|
322
|
+
when k == 'Ref' && v == 'AWS::AccountId'
|
323
|
+
FnCall.new 'aws_account_id', []
|
324
|
+
when k == 'Ref' && v == 'AWS::NotificationARNs'
|
325
|
+
FnCall.new 'aws_notification_arns', []
|
326
|
+
when k == 'Ref' && v == 'AWS::NoValue'
|
327
|
+
FnCall.new 'aws_no_value', []
|
328
|
+
when k == 'Ref' && v == 'AWS::Region'
|
329
|
+
FnCall.new 'aws_region', []
|
330
|
+
when k == 'Ref' && v == 'AWS::StackId'
|
331
|
+
FnCall.new 'aws_stack_id', []
|
332
|
+
when k == 'Ref' && v == 'AWS::StackName'
|
333
|
+
FnCall.new 'aws_stack_name', []
|
334
|
+
when k == 'Ref'
|
335
|
+
FnCall.new 'ref', [v]
|
336
|
+
else
|
337
|
+
val
|
338
|
+
end
|
339
|
+
end
|
340
|
+
elsif val.is_a?(Array)
|
341
|
+
val.map { |v| simplify(v) }
|
342
|
+
else
|
343
|
+
val
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
main(ARGV)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
# Copyright 2013-2014 Bazaarvoice, Inc.
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
lib = File.expand_path('../lib', __FILE__)
|
18
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
19
|
+
require 'cloudformation-ruby-dsl/version'
|
20
|
+
|
21
|
+
Gem::Specification.new do |gem|
|
22
|
+
gem.name = "cardtapp-cloudformation-ruby-dsl"
|
23
|
+
gem.version = Cfn::Ruby::Dsl::VERSION
|
24
|
+
gem.authors = ["Shawn Smith", "Dave Barcelo", "Morgan Fletcher", "Csongor Gyuricza", "Igor Polishchuk", "Nathaniel Eliot", "Jona Fenocchi", "Tony Cui"]
|
25
|
+
gem.email = ["Shawn.Smith@bazaarvoice.com", "Dave.Barcelo@bazaarvoice.com", "Morgan.Fletcher@bazaarvoice.com", "Csongor.Gyuricza@bazaarvoice.com", "Igor.Polishchuk@bazaarvoice.com", "Nathaniel.Eliot@bazaarvoice.com", "Jona.Fenocchi@bazaarvoice.com", "Tony.Cui@bazaarvoice.com"]
|
26
|
+
gem.description = %q{Ruby DSL library that provides a wrapper around the CloudFormation.}
|
27
|
+
gem.summary = %q{Ruby DSL library that provides a wrapper around the CloudFormation. Written by [Bazaarvoice](http://www.bazaarvoice.com).}
|
28
|
+
gem.homepage = "http://github.com/bazaarvoice/cloudformation-ruby-dsl"
|
29
|
+
|
30
|
+
gem.files = `git ls-files`.split($/)
|
31
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
32
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
33
|
+
gem.require_paths = %w{lib bin}
|
34
|
+
|
35
|
+
gem.add_runtime_dependency 'detabulator'
|
36
|
+
gem.add_runtime_dependency 'json'
|
37
|
+
gem.add_runtime_dependency 'bundler'
|
38
|
+
gem.add_runtime_dependency 'aws-sdk-s3'
|
39
|
+
gem.add_runtime_dependency 'aws-sdk-cloudformation'
|
40
|
+
gem.add_runtime_dependency 'diffy'
|
41
|
+
gem.add_runtime_dependency 'highline'
|
42
|
+
gem.add_runtime_dependency 'rake'
|
43
|
+
|
44
|
+
gem.add_development_dependency 'rspec'
|
45
|
+
gem.add_development_dependency 'pry'
|
46
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Contributing
|
2
|
+
|
3
|
+
Thanks for your interest in contributing! Here are some things you should know.
|
4
|
+
|
5
|
+
## Getting started
|
6
|
+
|
7
|
+
To get started:
|
8
|
+
|
9
|
+
- fork this project on github
|
10
|
+
- create a new branch named after the change you want to make; i.e., `git checkout -b mynewfeature`
|
11
|
+
- make your changes (including tests) and commit them
|
12
|
+
- run the tests to make sure you haven't broken anything: ```rspec```
|
13
|
+
- send a pull request to this project from your fork/branch
|
14
|
+
|
15
|
+
Once you've sent your pull request, one of the project collaborators will review it and provide feedback. Please accept this commentary as constructive! It is intended as such.
|
16
|
+
|
17
|
+
## git
|
18
|
+
|
19
|
+
We're opinionated about git. Don't be surprised if we ask you to update your pull request to meet the following standards.
|
20
|
+
|
21
|
+
- rebase+squash your branch into a single commit. For clean git history, we'd prefer we merged in just 1 commit that contains the entire set of changes.
|
22
|
+
- don't write commit messages longer than 50 characters. See [How to Write a Git Commit Message](http://chris.beams.io/posts/git-commit/) for some examples of how to achieve this, and why.
|
data/docs/Releasing.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Releasing
|
2
|
+
|
3
|
+
## Performing releases
|
4
|
+
|
5
|
+
0. Merge the desired commits to master. But merge them cleanly! See: [merging](#merging)
|
6
|
+
1. Edit and commit the version file in `lib/cloudformation-ruby-dsl/version.rb`. Bump the version based on the [version specification](#versioning-specification)
|
7
|
+
2. `git push` to origin/master
|
8
|
+
3. `rake release`
|
9
|
+
|
10
|
+
## Versioning specification
|
11
|
+
|
12
|
+
For this project, we will follow the methodology proposed by http://semver.org/spec/v2.0.0.html.
|
13
|
+
|
14
|
+
1. Major versions break existing interfaces.
|
15
|
+
2. Minor versions are additive only.
|
16
|
+
3. Patch versions are for backward-compatible bug fixes.
|
17
|
+
|
18
|
+
## Merging
|
19
|
+
|
20
|
+
When you use the shiny green "Merge" button on a pull request, github creates a separate commit for the merge (because of the use of the `--no-ff` option). This is noisy and makes git history confusing. Instead of using the green merge button, merge the branch into master using [git-land](https://github.com/bazaarvoice/git-land#git-land) (or manually follow the steps described in the project).
|