cfndsl 1.0.4 → 1.2.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 +4 -4
- data/.rubocop.yml +7 -2
- data/CHANGELOG.md +198 -36
- data/README.md +8 -10
- data/Rakefile +4 -2
- data/cfndsl.gemspec +1 -1
- data/lib/cfndsl/aws/patches/500_BadTagsv13.0.0_patch.json +51 -0
- data/lib/cfndsl/aws/patches/500_BadTagsv16.2.0_patch.json +23 -0
- data/lib/cfndsl/aws/resource_specification.json +11493 -3147
- data/lib/cfndsl/cfnlego/cloudformation.rb +1 -1
- data/lib/cfndsl/cloudformation.rb +3 -2
- data/lib/cfndsl/jsonable.rb +2 -2
- data/lib/cfndsl/orchestration_template.rb +3 -3
- data/lib/cfndsl/rake_task.rb +3 -2
- data/lib/cfndsl/ref_check.rb +2 -2
- data/lib/cfndsl/resources.rb +18 -1
- data/lib/cfndsl/runner.rb +0 -2
- data/lib/cfndsl/types.rb +4 -4
- data/lib/cfndsl/version.rb +1 -1
- data/lib/deep_merge/core.rb +3 -2
- data/sample/t1-extra.yaml +1 -0
- data/sample/t1.yaml +0 -1
- data/spec/cfndsl_spec.rb +20 -0
- data/spec/cloud_formation_template_spec.rb +34 -0
- data/spec/generate_spec.rb +1 -0
- data/spec/support/shared_examples/orchestration_template.rb +7 -0
- data/spec/types_definition_spec.rb +3 -2
- metadata +7 -5
@@ -43,9 +43,10 @@ module CfnDsl
|
|
43
43
|
params.load_file file
|
44
44
|
when :raw
|
45
45
|
file_parts = file.split('=')
|
46
|
-
|
46
|
+
case file_parts[1].downcase
|
47
|
+
when 'true'
|
47
48
|
params.set_param(file_parts[0], true)
|
48
|
-
|
49
|
+
when 'false'
|
49
50
|
params.set_param(file_parts[0], false)
|
50
51
|
else
|
51
52
|
params.set_param(*file.split('='))
|
data/lib/cfndsl/jsonable.rb
CHANGED
@@ -87,7 +87,7 @@ module CfnDsl
|
|
87
87
|
def FnSub(string, substitutions = nil)
|
88
88
|
raise ArgumentError, 'The first argument passed to Fn::Sub must be a string' unless string.is_a? String
|
89
89
|
|
90
|
-
refs = string.scan(FN_SUB_SCANNER).map(&:first)
|
90
|
+
refs = string.scan(FN_SUB_SCANNER).map(&:first).map { |r| r.split('.', 2).first }
|
91
91
|
|
92
92
|
if substitutions
|
93
93
|
raise ArgumentError, 'The second argument passed to Fn::Sub must be a Hash' unless substitutions.is_a? Hash
|
@@ -169,7 +169,7 @@ module CfnDsl
|
|
169
169
|
def check_names
|
170
170
|
return if instance_variable_get('@Resources').nil?
|
171
171
|
|
172
|
-
instance_variable_get('@Resources').
|
172
|
+
instance_variable_get('@Resources').each_key do |name|
|
173
173
|
next unless name !~ /\A\p{Alnum}+\z/
|
174
174
|
|
175
175
|
warn "Resource name: #{name} is invalid"
|
@@ -22,7 +22,7 @@ module CfnDsl
|
|
22
22
|
# Handles the overall template object
|
23
23
|
# rubocop:disable Metrics/ClassLength
|
24
24
|
class OrchestrationTemplate < JSONable
|
25
|
-
dsl_attr_setter :AWSTemplateFormatVersion, :Description, :Metadata, :Transform
|
25
|
+
dsl_attr_setter :AWSTemplateFormatVersion, :Description, :Metadata, :Transform, :Hooks
|
26
26
|
dsl_content_object :Condition, :Parameter, :Output, :Resource, :Mapping, :Rule
|
27
27
|
|
28
28
|
GLOBAL_REFS = {
|
@@ -245,7 +245,7 @@ module CfnDsl
|
|
245
245
|
end
|
246
246
|
end
|
247
247
|
|
248
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
248
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
249
249
|
def _check_refs(container_name, method, source_containers)
|
250
250
|
container = instance_variable_get("@#{container_name}s")
|
251
251
|
return [] unless container
|
@@ -279,7 +279,7 @@ module CfnDsl
|
|
279
279
|
|
280
280
|
invalids
|
281
281
|
end
|
282
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
282
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
283
283
|
|
284
284
|
def validate
|
285
285
|
errors = check_refs || []
|
data/lib/cfndsl/rake_task.rb
CHANGED
@@ -135,7 +135,8 @@ module CfnDsl
|
|
135
135
|
# @param [Hash] yaml_opts, other options to pass to YAML generator
|
136
136
|
def yaml(name:, files:, pathmap:, extras: [], **yaml_opts)
|
137
137
|
generate_model_tasks(name: name, files: files, pathmap: pathmap, extras: extras) do |model, f|
|
138
|
-
|
138
|
+
simple_model = JSON.parse(model.to_json) # convert model to a simple ruby object to avoid yaml tags
|
139
|
+
YAML.dump(simple_model, f, **yaml_opts)
|
139
140
|
end
|
140
141
|
self
|
141
142
|
end
|
@@ -189,7 +190,7 @@ module CfnDsl
|
|
189
190
|
files.each do |source|
|
190
191
|
matched_extras = build_extras_filelist(source, extras)
|
191
192
|
|
192
|
-
file source.pathmap(pathmap) => [source, :load_spec_types, matched_extras] do |task|
|
193
|
+
file source.pathmap(pathmap) => [source, :load_spec_types, *matched_extras] do |task|
|
193
194
|
eval_extras = matched_extras.map { |e| [:yaml, e] } # eval treats yaml and json same
|
194
195
|
puts "Generating Cloudformation for #{source} to #{task.name}"
|
195
196
|
model = CfnDsl.eval_file_with_extras(source, eval_extras, verbose)
|
data/lib/cfndsl/ref_check.rb
CHANGED
@@ -10,7 +10,7 @@ module RefCheck
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# Build up a set of references.
|
13
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
13
|
+
# rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
14
14
|
def build_references(refs = [], origin = nil, method = :all_refs)
|
15
15
|
if respond_to?(method)
|
16
16
|
send(method).each do |ref|
|
@@ -30,7 +30,7 @@ module RefCheck
|
|
30
30
|
|
31
31
|
refs
|
32
32
|
end
|
33
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
33
|
+
# rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
34
34
|
|
35
35
|
def ref_children
|
36
36
|
[]
|
data/lib/cfndsl/resources.rb
CHANGED
@@ -5,7 +5,7 @@ require_relative 'jsonable'
|
|
5
5
|
module CfnDsl
|
6
6
|
# Handles Resource objects
|
7
7
|
class ResourceDefinition < JSONable
|
8
|
-
dsl_attr_setter :Type, :
|
8
|
+
dsl_attr_setter :Type, :UpdateReplacePolicy, :DeletionPolicy, :Condition, :Metadata
|
9
9
|
dsl_content_object :Property, :UpdatePolicy, :CreationPolicy
|
10
10
|
|
11
11
|
def add_tag(name, value, propagate = nil)
|
@@ -16,6 +16,23 @@ module CfnDsl
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
+
# DependsOn can be a single value or a list
|
20
|
+
def DependsOn(value)
|
21
|
+
case @DependsOn
|
22
|
+
when nil
|
23
|
+
@DependsOn = value
|
24
|
+
when Array
|
25
|
+
@DependsOn << value
|
26
|
+
else
|
27
|
+
@DependsOn = [@DependsOn, value]
|
28
|
+
end
|
29
|
+
if @DependsOn.is_a?(Array)
|
30
|
+
@DependsOn.flatten!
|
31
|
+
@DependsOn.uniq!
|
32
|
+
end
|
33
|
+
@DependsOn
|
34
|
+
end
|
35
|
+
|
19
36
|
def condition_refs
|
20
37
|
[@Condition].flatten.compact.map(&:to_s)
|
21
38
|
end
|
data/lib/cfndsl/runner.rb
CHANGED
@@ -7,7 +7,6 @@ require_relative 'globals'
|
|
7
7
|
|
8
8
|
module CfnDsl
|
9
9
|
# Runner class to handle commandline invocation
|
10
|
-
# rubocop:disable Metrics/ClassLength
|
11
10
|
class Runner
|
12
11
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
13
12
|
def self.invoke!
|
@@ -143,4 +142,3 @@ module CfnDsl
|
|
143
142
|
end
|
144
143
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
|
145
144
|
end
|
146
|
-
# rubocop:enable Metrics/ClassLength
|
data/lib/cfndsl/types.rb
CHANGED
@@ -16,7 +16,7 @@ module CfnDsl
|
|
16
16
|
{ 'Resources' => resources, 'Types' => types, 'Version' => spec.version, 'File' => spec.file }
|
17
17
|
end
|
18
18
|
|
19
|
-
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength
|
19
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
|
20
20
|
def self.extract_resources(spec)
|
21
21
|
spec.each_with_object({}) do |(resource_name, resource_info), resources|
|
22
22
|
properties = resource_info['Properties'].each_with_object({}) do |(property_name, property_info), extracted|
|
@@ -54,7 +54,7 @@ module CfnDsl
|
|
54
54
|
end
|
55
55
|
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength
|
56
56
|
|
57
|
-
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/
|
57
|
+
# rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength
|
58
58
|
def self.extract_types(spec)
|
59
59
|
primitive_types = {
|
60
60
|
'String' => 'String',
|
@@ -117,9 +117,9 @@ module CfnDsl
|
|
117
117
|
types
|
118
118
|
end
|
119
119
|
end
|
120
|
-
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/
|
120
|
+
# rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength
|
121
121
|
|
122
|
-
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
122
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Metrics/PerceivedComplexity
|
123
123
|
def self.included(type_def)
|
124
124
|
types_list = extract_from_resource_spec
|
125
125
|
type_def.const_set('Types_Internal', types_list)
|
data/lib/cfndsl/version.rb
CHANGED
data/lib/deep_merge/core.rb
CHANGED
@@ -215,9 +215,10 @@ module DeepMerge
|
|
215
215
|
knockout_prefix = options[:knockout_prefix] || false
|
216
216
|
di = options[:debug_indent] || ''
|
217
217
|
if knockout_prefix && overwrite_unmergeable
|
218
|
-
src_tmp =
|
218
|
+
src_tmp = case source
|
219
|
+
when String # remove knockout string from source before overwriting dest
|
219
220
|
source.gsub(/^#{knockout_prefix}/, '')
|
220
|
-
|
221
|
+
when Array # remove all knockout elements before overwriting dest
|
221
222
|
source.delete_if { |ko_item| ko_item.is_a?(String) && ko_item.match(/^#{knockout_prefix}/) }
|
222
223
|
else
|
223
224
|
source
|
@@ -0,0 +1 @@
|
|
1
|
+
description: 5 machine cluster
|
data/sample/t1.yaml
CHANGED
data/spec/cfndsl_spec.rb
CHANGED
@@ -188,6 +188,26 @@ describe CfnDsl::CloudFormationTemplate do
|
|
188
188
|
end
|
189
189
|
end
|
190
190
|
|
191
|
+
it 'composes DependsOn' do
|
192
|
+
spec = self
|
193
|
+
subject.Resource('SomeResource') do
|
194
|
+
d = DependsOn('X')
|
195
|
+
spec.expect(d).to spec.eq('X') # start with a single value, stays a single value
|
196
|
+
d = DependsOn(%w[Y Z])
|
197
|
+
spec.expect(d).to spec.eq(%w[X Y Z]) # concatenates values
|
198
|
+
d = DependsOn('Y') # uniqeness
|
199
|
+
spec.expect(d).to spec.eq(%w[X Y Z])
|
200
|
+
end
|
201
|
+
expect(subject.to_json).to eq('{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"SomeResource":{"DependsOn":["X","Y","Z"]}}}')
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'supports single value DependsOn' do
|
205
|
+
subject.Resource('SomeResource') do
|
206
|
+
DependsOn(:ADependency)
|
207
|
+
end
|
208
|
+
expect(subject.to_json).to eq('{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"SomeResource":{"DependsOn":"ADependency"}}}')
|
209
|
+
end
|
210
|
+
|
191
211
|
context 'built-in functions' do
|
192
212
|
it 'FnGetAtt' do
|
193
213
|
func = subject.FnGetAtt('A', 'B')
|
@@ -40,10 +40,31 @@ describe CfnDsl::CloudFormationTemplate do
|
|
40
40
|
expect(subject.validate).to equal(subject)
|
41
41
|
end
|
42
42
|
|
43
|
+
it 'returns self if there are valid Fn::Sub references to other resources' do
|
44
|
+
tr = subject.Resource(:TestResource)
|
45
|
+
tr.Type('Custom-TestType')
|
46
|
+
tr.Property(:AProperty, tr.FnSub('prefix ${TestResource2}suffix'))
|
47
|
+
|
48
|
+
t2 = subject.Resource('TestResource2')
|
49
|
+
t2.Type('Custom-TestType')
|
50
|
+
expect(subject.validate).to equal(subject)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'returns self if there are valid Fn::Sub references to other resource attributes' do
|
54
|
+
tr = subject.Resource(:TestResource)
|
55
|
+
tr.Type('Custom-TestType')
|
56
|
+
tr.Property(:AProperty, tr.FnSub('prefix ${TestResource2.AnAttribute}suffix'))
|
57
|
+
|
58
|
+
t2 = subject.Resource('TestResource2')
|
59
|
+
t2.Type('Custom-TestType')
|
60
|
+
expect(subject.validate).to equal(subject)
|
61
|
+
end
|
62
|
+
|
43
63
|
it 'returns self if there are valid DependsOn to other resources' do
|
44
64
|
tr = subject.Resource(:TestResource)
|
45
65
|
tr.Type('Custom-TestType')
|
46
66
|
tr.DependsOn(:TestResource2)
|
67
|
+
tr.DependsOn(:TestResource2)
|
47
68
|
|
48
69
|
t2 = subject.Resource('TestResource2')
|
49
70
|
t2.Type('Custom-TestType')
|
@@ -119,6 +140,13 @@ describe CfnDsl::CloudFormationTemplate do
|
|
119
140
|
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*itself/i)
|
120
141
|
end
|
121
142
|
|
143
|
+
it 'raises CfnDsl::Error if a resourcs references itself in Fn::Sub attribute expression' do
|
144
|
+
tr = subject.Resource(:TestResource)
|
145
|
+
tr.Type('Custom-TestType')
|
146
|
+
tr.Property(:AProperty, tr.FnSub('${TestResource.attr}'))
|
147
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*itself/i)
|
148
|
+
end
|
149
|
+
|
122
150
|
it 'raises CfnDsl::Error if there are cyclic DependsOn references' do
|
123
151
|
tr = subject.Resource(:ResourceOne)
|
124
152
|
tr.Type('Custom-TestType')
|
@@ -211,6 +239,12 @@ describe CfnDsl::CloudFormationTemplate do
|
|
211
239
|
expect(subject.validate).to equal(subject)
|
212
240
|
end
|
213
241
|
|
242
|
+
it 'returns self if there are valid Fn::Sub references to resource attributes' do
|
243
|
+
subject.Resource(:TestResource)
|
244
|
+
subject.Output('TestResourceOutput').Value(subject.FnSub('prefix ${TestResource.attr}suffix'))
|
245
|
+
expect(subject.validate).to equal(subject)
|
246
|
+
end
|
247
|
+
|
214
248
|
it 'raises CfnDsl::Error if references a non existent condition' do
|
215
249
|
subject.Output(:TestOutput).Condition('NoCondition')
|
216
250
|
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestOutput.*NoCondition/)
|
data/spec/generate_spec.rb
CHANGED
@@ -12,6 +12,7 @@ describe Cfnlego do
|
|
12
12
|
output << '# http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-domain'
|
13
13
|
output << "\n InstanceId String # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-instanceid"
|
14
14
|
output << "\n PublicIpv4Pool String # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-publicipv4pool"
|
15
|
+
output << "\n Tags [List] # http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-eip.html#cfn-ec2-eip-tags"
|
15
16
|
output << "\n end\nend\n"
|
16
17
|
expect(template).to eq output
|
17
18
|
end
|
@@ -102,6 +102,13 @@ shared_examples 'an orchestration template' do
|
|
102
102
|
expect(plural_value).to eq([{ foo: 'bar' }])
|
103
103
|
end
|
104
104
|
|
105
|
+
it 'allows array returning function for otherwise array value when singular name == plural name' do
|
106
|
+
security_group = described_class.type_module.const_get('AWS_EC2_SecurityGroup').new
|
107
|
+
security_group.Property('SecurityGroupIngress', security_group.FnFindInMap('x', 'y', 'z'))
|
108
|
+
plural_value = security_group.instance_variable_get('@Properties')['SecurityGroupIngress'].value
|
109
|
+
expect(plural_value.to_json).to eql('{"Fn::FindInMap":["x","y","z"]}')
|
110
|
+
end
|
111
|
+
|
105
112
|
it 'sets the type of each resource correctly' do
|
106
113
|
ec2_instance = subject.EC2_Instance(:foo)
|
107
114
|
expect(ec2_instance.instance_variable_get('@Type')).to eq('AWS::EC2::Instance')
|
@@ -27,9 +27,10 @@ RSpec.describe 'Type Definitions' do
|
|
27
27
|
it "#{name} has all property types defined" do
|
28
28
|
type = type['Properties'] if type.is_a?(Hash) && type.key?('Properties')
|
29
29
|
type = type.first if type.is_a?(Array)
|
30
|
-
|
30
|
+
case type
|
31
|
+
when String
|
31
32
|
expect(types).to have_key(type)
|
32
|
-
|
33
|
+
when Hash
|
33
34
|
type.values.flatten.each { |t| expect(types).to have_key(t) }
|
34
35
|
else
|
35
36
|
raise "A defined type should only be of the form String, Array or Hash, got #{type.class}"
|
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: 1.0
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Jack
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-07-29 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -70,6 +70,8 @@ files:
|
|
70
70
|
- lib/cfndsl/aws/patches/000_sam.spec.json
|
71
71
|
- lib/cfndsl/aws/patches/100_sam.spec_DeploymentPreference_patch.json
|
72
72
|
- lib/cfndsl/aws/patches/200_Scrutinies_patch.json
|
73
|
+
- lib/cfndsl/aws/patches/500_BadTagsv13.0.0_patch.json
|
74
|
+
- lib/cfndsl/aws/patches/500_BadTagsv16.2.0_patch.json
|
73
75
|
- lib/cfndsl/aws/patches/500_Cognito_IdentityPoolRoleAttachment_patches.json
|
74
76
|
- lib/cfndsl/aws/patches/500_IoT1Click_patch_PlacementTemplate_DeviceTemplates.json
|
75
77
|
- lib/cfndsl/aws/patches/500_NetworkAclEntry_patch.json
|
@@ -128,6 +130,7 @@ files:
|
|
128
130
|
- sample/import.rb
|
129
131
|
- sample/lambda.rb
|
130
132
|
- sample/s3.rb
|
133
|
+
- sample/t1-extra.yaml
|
131
134
|
- sample/t1.rb
|
132
135
|
- sample/t1.yaml
|
133
136
|
- sample/vpc_example.rb
|
@@ -182,15 +185,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
182
185
|
requirements:
|
183
186
|
- - "~>"
|
184
187
|
- !ruby/object:Gem::Version
|
185
|
-
version: '2.
|
188
|
+
version: '2.4'
|
186
189
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
190
|
requirements:
|
188
191
|
- - ">="
|
189
192
|
- !ruby/object:Gem::Version
|
190
193
|
version: '0'
|
191
194
|
requirements: []
|
192
|
-
|
193
|
-
rubygems_version: 2.7.7
|
195
|
+
rubygems_version: 3.0.8
|
194
196
|
signing_key:
|
195
197
|
specification_version: 4
|
196
198
|
summary: AWS Cloudformation DSL
|