aws-cfn-dsl 0.6.0 → 0.7.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 +5 -13
- data/Gemfile +0 -2
- data/aws-cfn-dsl.gemspec +3 -2
- data/lib/aws/cfn/dsl/base.rb +55 -443
- data/lib/aws/cfn/dsl/main.rb +2 -17
- data/lib/aws/cfn/dsl/mixins/dsl.rb +52 -0
- data/lib/aws/cfn/dsl/mixins/maintainer.rb +42 -0
- data/lib/aws/cfn/dsl/mixins/options.rb +126 -0
- data/lib/aws/cfn/dsl/mixins/output.rb +53 -0
- data/lib/aws/cfn/dsl/mixins/prettyprint.rb +296 -0
- data/lib/aws/cfn/dsl/mixins/simplify.rb +59 -0
- data/lib/aws/cfn/dsl/template.rb +0 -164
- data/lib/aws/cfn/dsl/version.rb +1 -1
- metadata +51 -25
@@ -0,0 +1,59 @@
|
|
1
|
+
module Aws
|
2
|
+
module Cfn
|
3
|
+
module Dsl
|
4
|
+
module Simplify
|
5
|
+
|
6
|
+
def simplify(val,start=false)
|
7
|
+
logStep "Simplify a block ..." if start
|
8
|
+
if val.is_a?(Hash)
|
9
|
+
val = Hash[val.map { |k,v| [k, simplify(v)] }]
|
10
|
+
if val.length != 1
|
11
|
+
val
|
12
|
+
else
|
13
|
+
k, v = val.entries[0]
|
14
|
+
case @config[:functions]
|
15
|
+
when @on_yes_regex then
|
16
|
+
case
|
17
|
+
# CloudFormation functions
|
18
|
+
when k == 'Fn::Base64'
|
19
|
+
FnCall.new 'base64', [v], true
|
20
|
+
when k == 'Fn::FindInMap'
|
21
|
+
FnCall.new 'find_in_map', v
|
22
|
+
when k == 'Fn::GetAtt'
|
23
|
+
FnCall.new 'get_att', v
|
24
|
+
when k == 'Fn::GetAZs'
|
25
|
+
FnCall.new 'get_azs', v != '' ? [v] : []
|
26
|
+
when k == 'Fn::Join'
|
27
|
+
FnCall.new 'join', [v[0]] + v[1], true
|
28
|
+
when k == 'Fn::Select'
|
29
|
+
FnCall.new 'select', v
|
30
|
+
when k == 'Ref' && v == 'AWS::Region'
|
31
|
+
FnCall.new 'aws_region', []
|
32
|
+
when k == 'Ref' && v == 'AWS::StackName'
|
33
|
+
FnCall.new 'aws_stack_name', []
|
34
|
+
# CloudFormation internal references
|
35
|
+
when k == 'Ref'
|
36
|
+
FnCall.new 'ref', [v]
|
37
|
+
else
|
38
|
+
val
|
39
|
+
end
|
40
|
+
else
|
41
|
+
val
|
42
|
+
end
|
43
|
+
end
|
44
|
+
elsif val.is_a?(Array)
|
45
|
+
val.map { |v| simplify(v) }
|
46
|
+
else
|
47
|
+
val
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def self.included(includer)
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/aws/cfn/dsl/template.rb
CHANGED
@@ -91,170 +91,6 @@ module Aws
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
-
def exec!(argv=ARGV)
|
95
|
-
@opts = Slop.parse(help: true) do
|
96
|
-
banner "usage: #{$PROGRAM_NAME} <expand|diff|validate|create|update|delete>"
|
97
|
-
on :o, :output=, 'The template file to save this DSL expansion to', as: String
|
98
|
-
end
|
99
|
-
|
100
|
-
action = argv[0] || 'expand'
|
101
|
-
unless %w(expand diff validate create update delete).include? action
|
102
|
-
$stderr.puts "usage: #{$PROGRAM_NAME} <expand|diff|validate|create|update|delete>"
|
103
|
-
exit(2)
|
104
|
-
end
|
105
|
-
unless (argv & %w(--template-file --template-url)).empty?
|
106
|
-
$stderr.puts "#{File.basename($PROGRAM_NAME)}: The --template-file and --template-url command-line options are not allowed. (You are running the template itself right now ... !)"
|
107
|
-
exit(2)
|
108
|
-
end
|
109
|
-
|
110
|
-
# Find parameters where extension attribute :Immutable is true then remove it from the
|
111
|
-
# cfn template since we can't pass it to CloudFormation.
|
112
|
-
immutable_parameters = excise_parameter_attribute!(:Immutable)
|
113
|
-
|
114
|
-
# Tag CloudFormation stacks based on :Tags defined in the template
|
115
|
-
cfn_tags = excise_tags!
|
116
|
-
# The command line string looks like: --tag "Key=key; Value=value" --tag "Key2=key2; Value2=value"
|
117
|
-
cfn_tags_options = cfn_tags.sort.map { |tag| ["--tag", "Key=%s; Value=%s" % tag.split('=')] }.flatten
|
118
|
-
|
119
|
-
# example: <template.rb> cfn-create-stack my-stack-name --parameters "Env=prod" --region eu-west-1
|
120
|
-
# Execute the AWS CLI cfn-cmd command to validate/create/update a CloudFormation stack.
|
121
|
-
if action == 'diff' or (action == 'expand' and not nopretty)
|
122
|
-
template_string = JSON.pretty_generate(self)
|
123
|
-
else
|
124
|
-
template_string = JSON.generate(self)
|
125
|
-
end
|
126
|
-
|
127
|
-
if action == 'expand'
|
128
|
-
# Write the pretty-printed JSON template to stdout and exit. [--nopretty] option writes output with minimal whitespace
|
129
|
-
# example: <template.rb> expand --parameters "Env=prod" --region eu-west-1 --nopretty
|
130
|
-
if @opts[:output]
|
131
|
-
dest = @opts[:output]
|
132
|
-
if File.directory? dest
|
133
|
-
file = File.basename $PROGRAM_NAME
|
134
|
-
file.gsub!(%r'\.rb', '.json')
|
135
|
-
dest = File.join dest, file
|
136
|
-
end
|
137
|
-
IO.write(dest, template_string)
|
138
|
-
else
|
139
|
-
puts template_string
|
140
|
-
end
|
141
|
-
exit(true)
|
142
|
-
end
|
143
|
-
|
144
|
-
temp_file = File.absolute_path("#{$PROGRAM_NAME}.expanded.json")
|
145
|
-
File.write(temp_file, template_string)
|
146
|
-
|
147
|
-
cmdline = ['cfn-cmd'] + argv + ['--template-file', temp_file] + cfn_tags_options
|
148
|
-
|
149
|
-
case action
|
150
|
-
when 'diff'
|
151
|
-
# example: <template.rb> diff my-stack-name --parameters "Env=prod" --region eu-west-1
|
152
|
-
# Diff the current template for an existing stack with the expansion of this template.
|
153
|
-
|
154
|
-
# The --parameters and --tag options were used to expand the template but we don't need them anymore. Discard.
|
155
|
-
_, cfn_options = extract_options(argv[1..-1], %w(), %w(--parameters --tag))
|
156
|
-
|
157
|
-
# Separate the remaining command-line options into options for 'cfn-cmd' and options for 'diff'.
|
158
|
-
cfn_options, diff_options = extract_options(cfn_options, %w(),
|
159
|
-
%w(--stack-name --region --parameters --connection-timeout -I --access-key-id -S --secret-key -K --ec2-private-key-file-path -U --url))
|
160
|
-
|
161
|
-
# If the first argument is a stack name then shift it from diff_options over to cfn_options.
|
162
|
-
if diff_options[0] && !(/^-/ =~ diff_options[0])
|
163
|
-
cfn_options.unshift(diff_options.shift)
|
164
|
-
end
|
165
|
-
|
166
|
-
# Run CloudFormation commands to describe the existing stack
|
167
|
-
cfn_options_string = cfn_options.map { |arg| "'#{arg}'" }.join(' ')
|
168
|
-
old_template_raw = exec_capture_stdout("cfn-cmd cfn-get-template #{cfn_options_string}")
|
169
|
-
# ec2 template output is not valid json: TEMPLATE "<json>\n"\n
|
170
|
-
old_template_object = JSON.parse(old_template_raw[11..-3])
|
171
|
-
old_template_string = JSON.pretty_generate(old_template_object)
|
172
|
-
old_stack_attributes = exec_describe_stack(cfn_options_string)
|
173
|
-
old_tags_string = old_stack_attributes["TAGS"]
|
174
|
-
old_parameters_string = old_stack_attributes["PARAMETERS"]
|
175
|
-
|
176
|
-
# Sort the tag strings alphabetically to make them easily comparable
|
177
|
-
old_tags_string = (old_tags_string || '').split(';').sort.map { |tag| %Q(TAG "#{tag}"\n) }.join
|
178
|
-
tags_string = cfn_tags.sort.map { |tag| "TAG \"#{tag}\"\n" }.join
|
179
|
-
|
180
|
-
# Sort the parameter strings alphabetically to make them easily comparable
|
181
|
-
old_parameters_string = (old_parameters_string || '').split(';').sort.map { |param| %Q(PARAMETER "#{param}"\n) }.join
|
182
|
-
parameters_string = parameters.sort.map { |key, value| "PARAMETER \"#{key}=#{value}\"\n" }.join
|
183
|
-
|
184
|
-
# Diff the expanded template with the template from CloudFormation.
|
185
|
-
old_temp_file = File.absolute_path("#{$PROGRAM_NAME}.current.json")
|
186
|
-
new_temp_file = File.absolute_path("#{$PROGRAM_NAME}.expanded.json")
|
187
|
-
File.write(old_temp_file, old_tags_string + old_parameters_string + old_template_string)
|
188
|
-
File.write(new_temp_file, tags_string + parameters_string + template_string)
|
189
|
-
|
190
|
-
# Compare templates
|
191
|
-
system(*["diff"] + diff_options + [old_temp_file, new_temp_file])
|
192
|
-
|
193
|
-
File.delete(old_temp_file)
|
194
|
-
File.delete(new_temp_file)
|
195
|
-
|
196
|
-
exit(true)
|
197
|
-
|
198
|
-
when 'cfn-validate-template'
|
199
|
-
# The cfn-validate-template command doesn't support --parameters so remove it if it was provided for template expansion.
|
200
|
-
_, cmdline = extract_options(cmdline, %w(), %w(--parameters --tag))
|
201
|
-
|
202
|
-
when 'cfn-update-stack'
|
203
|
-
# Pick out the subset of cfn-update-stack options that apply to cfn-describe-stacks.
|
204
|
-
cfn_options, other_options = extract_options(argv[1..-1], %w(),
|
205
|
-
%w(--stack-name --region --connection-timeout -I --access-key-id -S --secret-key -K --ec2-private-key-file-path -U --url))
|
206
|
-
|
207
|
-
# If the first argument is a stack name then shift it over to cfn_options.
|
208
|
-
if other_options[0] && !(/^-/ =~ other_options[0])
|
209
|
-
cfn_options.unshift(other_options.shift)
|
210
|
-
end
|
211
|
-
|
212
|
-
# Run CloudFormation command to describe the existing stack
|
213
|
-
cfn_options_string = cfn_options.map { |arg| "'#{arg}'" }.join(' ')
|
214
|
-
old_stack_attributes = exec_describe_stack(cfn_options_string)
|
215
|
-
|
216
|
-
# If updating a stack and some parameters are marked as immutable, fail if the new parameters don't match the old ones.
|
217
|
-
if not immutable_parameters.empty?
|
218
|
-
old_parameters_string = old_stack_attributes["PARAMETERS"]
|
219
|
-
old_parameters = Hash[(old_parameters_string || '').split(';').map { |pair| pair.split('=', 2) }]
|
220
|
-
new_parameters = parameters
|
221
|
-
|
222
|
-
immutable_parameters.sort.each do |param|
|
223
|
-
if old_parameters[param].to_s != new_parameters[param].to_s
|
224
|
-
$stderr.puts "Error: cfn-update-stack may not update immutable parameter " +
|
225
|
-
"'#{param}=#{old_parameters[param]}' to '#{param}=#{new_parameters[param]}'."
|
226
|
-
exit(false)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
# Tags are immutable in CloudFormation. The cfn-update-stack command doesn't support --tag options, so remove
|
232
|
-
# the argument (if it exists) and validate against the existing stack to ensure tags haven't changed.
|
233
|
-
# Compare the sorted arrays for an exact match
|
234
|
-
old_cfn_tags = old_stack_attributes['TAGS'].split(';').sort rescue [] # Use empty Array if .split fails
|
235
|
-
if cfn_tags != old_cfn_tags
|
236
|
-
$stderr.puts "CloudFormation stack tags do not match and cannot be updated. You must either use the same tags or create a new stack." +
|
237
|
-
"\n" + (old_cfn_tags - cfn_tags).map {|tag| "< #{tag}" }.join("\n") +
|
238
|
-
"\n" + "---" +
|
239
|
-
"\n" + (cfn_tags - old_cfn_tags).map {|tag| "> #{tag}"}.join("\n")
|
240
|
-
exit(false)
|
241
|
-
end
|
242
|
-
_, cmdline = extract_options(cmdline, %w(), %w(--tag))
|
243
|
-
end
|
244
|
-
|
245
|
-
# Execute command cmdline
|
246
|
-
unless system(*cmdline)
|
247
|
-
$stderr.puts "\nExecution of 'cfn-cmd' failed. To facilitate debugging, the generated JSON template " +
|
248
|
-
"file was not deleted. You may delete the file manually if it isn't needed: #{temp_file}"
|
249
|
-
exit(false)
|
250
|
-
end
|
251
|
-
|
252
|
-
File.delete(temp_file)
|
253
|
-
|
254
|
-
exit(true)
|
255
|
-
end
|
256
|
-
|
257
|
-
|
258
94
|
end
|
259
95
|
end
|
260
96
|
end
|
data/lib/aws/cfn/dsl/version.rb
CHANGED
metadata
CHANGED
@@ -1,77 +1,97 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-cfn-dsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Christo DeLange
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_print
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.2'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.2.0
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - "~>"
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
29
|
+
version: '1.2'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.2.0
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
34
|
+
name: psych
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
|
-
- -
|
37
|
+
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
33
39
|
version: '0'
|
34
40
|
type: :runtime
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
38
|
-
- -
|
44
|
+
- - ">="
|
39
45
|
- !ruby/object:Gem::Version
|
40
46
|
version: '0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
48
|
+
name: json
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
44
50
|
requirements:
|
45
|
-
- -
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.4'
|
48
|
-
- - ! '>='
|
51
|
+
- - ">="
|
49
52
|
- !ruby/object:Gem::Version
|
50
|
-
version: 0
|
53
|
+
version: '0'
|
51
54
|
type: :runtime
|
52
55
|
prerelease: false
|
53
56
|
version_requirements: !ruby/object:Gem::Requirement
|
54
57
|
requirements:
|
55
|
-
- -
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
version: '0.4'
|
58
|
-
- - ! '>='
|
58
|
+
- - ">="
|
59
59
|
- !ruby/object:Gem::Version
|
60
|
-
version: 0
|
60
|
+
version: '0'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
62
|
name: slop
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
|
-
- -
|
65
|
+
- - ">="
|
66
66
|
- !ruby/object:Gem::Version
|
67
67
|
version: '0'
|
68
68
|
type: :runtime
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
|
-
- -
|
72
|
+
- - ">="
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: cloudformation-ruby-dsl
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.4'
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 0.4.3
|
85
|
+
type: :runtime
|
86
|
+
prerelease: false
|
87
|
+
version_requirements: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - "~>"
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0.4'
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 0.4.3
|
75
95
|
description: Ruby DSL for creating Cloudformation templates
|
76
96
|
email:
|
77
97
|
- rubygems@dldinternet.com
|
@@ -80,7 +100,7 @@ executables:
|
|
80
100
|
extensions: []
|
81
101
|
extra_rdoc_files: []
|
82
102
|
files:
|
83
|
-
- .gitignore
|
103
|
+
- ".gitignore"
|
84
104
|
- Gemfile
|
85
105
|
- LICENSE
|
86
106
|
- LICENSE.txt
|
@@ -93,6 +113,12 @@ files:
|
|
93
113
|
- lib/aws/cfn/dsl/base.rb
|
94
114
|
- lib/aws/cfn/dsl/fncall.rb
|
95
115
|
- lib/aws/cfn/dsl/main.rb
|
116
|
+
- lib/aws/cfn/dsl/mixins/dsl.rb
|
117
|
+
- lib/aws/cfn/dsl/mixins/maintainer.rb
|
118
|
+
- lib/aws/cfn/dsl/mixins/options.rb
|
119
|
+
- lib/aws/cfn/dsl/mixins/output.rb
|
120
|
+
- lib/aws/cfn/dsl/mixins/prettyprint.rb
|
121
|
+
- lib/aws/cfn/dsl/mixins/simplify.rb
|
96
122
|
- lib/aws/cfn/dsl/template.rb
|
97
123
|
- lib/aws/cfn/dsl/version.rb
|
98
124
|
homepage: ''
|
@@ -105,12 +131,12 @@ require_paths:
|
|
105
131
|
- lib
|
106
132
|
required_ruby_version: !ruby/object:Gem::Requirement
|
107
133
|
requirements:
|
108
|
-
- -
|
134
|
+
- - ">="
|
109
135
|
- !ruby/object:Gem::Version
|
110
136
|
version: '0'
|
111
137
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
138
|
requirements:
|
113
|
-
- -
|
139
|
+
- - ">="
|
114
140
|
- !ruby/object:Gem::Version
|
115
141
|
version: '0'
|
116
142
|
requirements: []
|