cfndsl 0.15.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +3 -0
- data/.rubocop.yml +6 -6
- data/.travis.yml +10 -5
- data/CHANGELOG.md +721 -400
- data/Gemfile +2 -0
- data/README.md +85 -23
- data/Rakefile +28 -6
- data/TODO.md +18 -0
- data/UPGRADING.md +22 -0
- data/cfndsl.gemspec +17 -16
- data/exe/cfndsl +5 -0
- data/lib/cfndsl.rb +3 -113
- data/lib/cfndsl/aws/cloud_formation_template.rb +10 -1
- data/lib/cfndsl/aws/patches/000_CloudFormationResourceSpecification.json +51726 -0
- data/lib/cfndsl/aws/patches/000_sam.spec.json +1242 -0
- data/lib/cfndsl/aws/patches/100_sam.spec_DeploymentPreference_patch.json +64 -0
- data/lib/cfndsl/aws/patches/200_Scrutinies_patch.json +86 -0
- data/lib/cfndsl/aws/patches/500_Cognito_IdentityPoolRoleAttachment_patches.json +25 -0
- data/lib/cfndsl/aws/patches/500_IoT1Click_patch_PlacementTemplate_DeviceTemplates.json +20 -0
- data/lib/cfndsl/aws/patches/500_NetworkAclEntry_patch.json +16 -0
- data/lib/cfndsl/aws/patches/500_SAM_Serverless_Function_S3Event_Events_patch.json +16 -0
- data/lib/cfndsl/aws/patches/500_SAM_Serverless_Function_S3Location_Version_patch.json +16 -0
- data/lib/cfndsl/aws/patches/500_SSM_AssociationName_patch.json +16 -0
- data/lib/cfndsl/aws/patches/500_VPCEndpoint_patch.json +17 -0
- data/lib/cfndsl/aws/patches/510_ElasticSearch_Domain_patches.json +15 -0
- data/lib/cfndsl/aws/patches/520_ServiceDiscovery_InstanceAttributes_patch.json +16 -0
- data/lib/cfndsl/aws/patches/600_RefKinds_patch.json +3654 -0
- data/lib/cfndsl/aws/patches/700_SAM_Serverless_Function_InlineCode_patch.json +20 -0
- data/lib/cfndsl/aws/patches/800_List_types_patch.json +115 -0
- data/lib/cfndsl/aws/resource_specification.json +39955 -9293
- data/lib/cfndsl/aws/types.rb +5 -3
- data/lib/cfndsl/cfnlego.rb +34 -0
- data/lib/{cfnlego → cfndsl/cfnlego}/cloudformation.erb +0 -0
- data/lib/{cfnlego → cfndsl/cfnlego}/cloudformation.rb +3 -1
- data/lib/{cfnlego → cfndsl/cfnlego}/resource.rb +5 -8
- data/lib/cfndsl/cloudformation.rb +114 -0
- data/lib/cfndsl/conditions.rb +13 -1
- data/lib/cfndsl/creation_policy.rb +3 -1
- data/lib/cfndsl/deep_merge.rb +4 -0
- data/lib/cfndsl/external_parameters.rb +12 -13
- data/lib/cfndsl/globals.rb +51 -10
- data/lib/cfndsl/json_serialisable_object.rb +4 -2
- data/lib/cfndsl/jsonable.rb +51 -68
- data/lib/cfndsl/mappings.rb +3 -1
- data/lib/cfndsl/module.rb +18 -5
- data/lib/cfndsl/names.rb +2 -0
- data/lib/cfndsl/orchestration_template.rb +193 -73
- data/lib/cfndsl/outputs.rb +7 -1
- data/lib/cfndsl/parameters.rb +3 -1
- data/lib/cfndsl/plurals.rb +23 -10
- data/lib/cfndsl/properties.rb +3 -1
- data/lib/cfndsl/rake_task.rb +217 -15
- data/lib/cfndsl/ref_check.rb +21 -11
- data/lib/cfndsl/resources.rb +8 -19
- data/lib/cfndsl/rules.rb +46 -0
- data/lib/cfndsl/runner.rb +143 -0
- data/lib/cfndsl/specification.rb +82 -84
- data/lib/cfndsl/types.rb +212 -95
- data/lib/cfndsl/update_policy.rb +3 -1
- data/lib/cfndsl/version.rb +3 -1
- data/lib/deep_merge/core.rb +10 -6
- data/lib/deep_merge/deep_merge.rb +3 -1
- data/sample/autoscale.rb +1 -1
- data/sample/autoscale2.rb +4 -3
- data/sample/circular.rb +2 -0
- data/sample/codedeploy.rb +3 -1
- data/sample/config_service.rb +5 -3
- data/sample/ecs.rb +3 -1
- data/sample/export.rb +5 -3
- data/sample/iam_policies.rb +2 -0
- data/sample/import.rb +4 -2
- data/sample/lambda.rb +3 -1
- data/sample/s3.rb +2 -0
- data/sample/t1.rb +3 -1
- data/sample/vpc_example.rb +3 -1
- data/sample/vpc_with_vpn_example.rb +3 -1
- data/spec/aws/ec2_security_group_spec.rb +2 -0
- data/spec/aws/ecs_task_definition_spec.rb +2 -0
- data/spec/aws/iam_managed_policy_spec.rb +2 -0
- data/spec/aws/kms_alias_spec.rb +2 -0
- data/spec/aws/list_type_patches_spec.rb +35 -0
- data/spec/aws/logs_log_group_spec.rb +2 -0
- data/spec/aws/nested_arrays_spec.rb +194 -0
- data/spec/aws/rds_db_instance_spec.rb +2 -0
- data/spec/aws/serverless_spec.rb +2 -2
- data/spec/cfndsl_spec.rb +102 -63
- data/spec/cli_spec.rb +52 -49
- data/spec/cloud_formation_template_spec.rb +235 -0
- data/spec/condition_spec.rb +24 -0
- data/spec/deep_merge_spec.rb +2 -0
- data/spec/direct_ruby_spec.rb +19 -0
- data/spec/external_parameters_spec.rb +25 -20
- data/spec/fixtures/condition-assertion.json +1 -0
- data/spec/fixtures/params.json +1 -0
- data/spec/fixtures/params.yaml +2 -0
- data/spec/fixtures/params_struct1.yaml +4 -0
- data/spec/fixtures/params_struct2.yaml +4 -0
- data/spec/fixtures/rule-assertion.json +1 -0
- data/spec/fixtures/test.rb +12 -4
- data/spec/generate_spec.rb +4 -0
- data/spec/jsonable_spec.rb +2 -0
- data/spec/metadata_spec.rb +2 -0
- data/spec/names_spec.rb +2 -0
- data/spec/output_spec.rb +2 -0
- data/spec/plurals_spec.rb +2 -0
- data/spec/resource_name_spec.rb +21 -0
- data/spec/resources_spec.rb +2 -7
- data/spec/rule_spec.rb +17 -0
- data/spec/spec_helper.rb +4 -7
- data/spec/support/shared_examples/orchestration_template.rb +17 -2
- data/spec/transform_spec.rb +2 -0
- data/spec/types_definition_spec.rb +6 -7
- metadata +79 -25
- data/bin/cfndsl +0 -143
- data/lib/cfndsl/errors.rb +0 -29
- data/lib/cfndsl/os/heat_template.rb +0 -16
- data/lib/cfndsl/os/types.rb +0 -12
- data/lib/cfndsl/os/types.yaml +0 -2423
- data/lib/cfndsl/patches.rb +0 -98
- data/lib/cfnlego.rb +0 -42
- data/spec/fixtures/heattest.rb +0 -22
- data/spec/heat_template_spec.rb +0 -5
data/spec/aws/serverless_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
def read_json_fixture(filename)
|
@@ -28,7 +30,6 @@ describe CfnDsl::CloudFormationTemplate do
|
|
28
30
|
}
|
29
31
|
)
|
30
32
|
end
|
31
|
-
# File.open('/tmp/dump', 'w') { |f| f.write(template.to_json) }
|
32
33
|
expect(JSON.parse(template.to_json)).to eq(read_json_fixture('serverless-function.json'))
|
33
34
|
end
|
34
35
|
|
@@ -40,7 +41,6 @@ describe CfnDsl::CloudFormationTemplate do
|
|
40
41
|
CacheClusterSize '512M'
|
41
42
|
Variables(Var1: 'value1')
|
42
43
|
end
|
43
|
-
File.open('/tmp/dump', 'w') { |f| f.write(template.to_json) }
|
44
44
|
expect(JSON.parse(template.to_json)).to eq(read_json_fixture('serverless-api.json'))
|
45
45
|
end
|
46
46
|
end
|
data/spec/cfndsl_spec.rb
CHANGED
@@ -1,30 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe CfnDsl do
|
6
|
+
let(:test_template_file_name) { "#{File.dirname(__FILE__)}/fixtures/test.rb" }
|
7
|
+
|
4
8
|
after(:example) { CfnDsl::ExternalParameters.refresh! }
|
5
9
|
|
6
10
|
it 'evaluates a cloud formation' do
|
7
|
-
|
8
|
-
subject.eval_file_with_extras(filename, [[:raw, 'test=123']])
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'evaluates a heat' do
|
12
|
-
filename = "#{File.dirname(__FILE__)}/fixtures/heattest.rb"
|
13
|
-
subject.eval_file_with_extras(filename)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
describe CfnDsl::HeatTemplate do
|
18
|
-
it 'honors last-set value for non-array properties' do
|
19
|
-
spec = self
|
20
|
-
subject.declare do
|
21
|
-
Server('myserver') do
|
22
|
-
flavor 'foo'
|
23
|
-
flavor 'bar'
|
24
|
-
f = @Properties['flavor'].value
|
25
|
-
spec.expect(f).to spec.eq('bar')
|
26
|
-
end
|
27
|
-
end
|
11
|
+
subject.eval_file_with_extras(test_template_file_name, [[:raw, 'test=123']])
|
28
12
|
end
|
29
13
|
end
|
30
14
|
|
@@ -53,16 +37,97 @@ describe CfnDsl::CloudFormationTemplate do
|
|
53
37
|
end
|
54
38
|
end
|
55
39
|
|
56
|
-
it '
|
40
|
+
it 'detects cyclic Resource references' do
|
57
41
|
q = subject.Resource('q') { DependsOn ['r'] }
|
58
42
|
r = subject.Resource('r') { Property('z', Ref('q')) }
|
59
|
-
q_refs = q.build_references
|
60
|
-
r_refs = r.build_references
|
61
|
-
expect(q_refs).to
|
62
|
-
expect(q_refs).to_not
|
63
|
-
expect(r_refs).to
|
64
|
-
expect(r_refs).to_not
|
65
|
-
expect(subject.check_refs.
|
43
|
+
q_refs = q.build_references
|
44
|
+
r_refs = r.build_references
|
45
|
+
expect(q_refs).to include('r')
|
46
|
+
expect(q_refs).to_not include('q')
|
47
|
+
expect(r_refs).to include('q')
|
48
|
+
expect(r_refs).to_not include('r')
|
49
|
+
expect(subject.check_refs.first).to match(/cyclic reference/i)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'detects a self reference in a Resource' do
|
53
|
+
q = subject.Resource('q') { Property('p', SomeDeepPropery: ['x', Ref('q')]) }
|
54
|
+
q_refs = q.build_references
|
55
|
+
expect(q_refs).to include('q')
|
56
|
+
messages = subject.check_refs
|
57
|
+
expect(messages.size).to eq(1) # Expect a self reference
|
58
|
+
expect(messages.first).to match(/references itself/i)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'detects a self reference in a Condition' do
|
62
|
+
q = subject.Condition('q', subject.FnAnd([subject.FnEquals('x', 'x'), subject.Condition('q')]))
|
63
|
+
q_refs = q.build_references([], nil, :condition_refs)
|
64
|
+
expect(q_refs).to include('q')
|
65
|
+
messages = subject.check_refs
|
66
|
+
expect(messages.size).to eq(1) # Expect a self reference
|
67
|
+
expect(messages.first).to match(/references itself/i)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'detects deep cycles in a Resource' do
|
71
|
+
subject.Condition('c', subject.FnEquals('a', 'b'))
|
72
|
+
subject.Resource('q') { Property('p', Ref('r')) }
|
73
|
+
subject.Resource('r') { Property('p', FnIf('c', FnGetAtt('s', 'attr'), 'x')) }
|
74
|
+
subject.Resource('s') { Property('p', FnSub('Something ${q}')) }
|
75
|
+
messages = subject.check_refs
|
76
|
+
expect(messages.size).to eq(1)
|
77
|
+
expect(messages.first).to match(/cyclic reference/i)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'detects deep cycles in Conditions' do
|
81
|
+
subject.Condition('c', subject.FnEquals('a', 'b'))
|
82
|
+
subject.Condition('d', subject.FnAnd([subject.FnEquals('x', 'x'), subject.Condition('c')]))
|
83
|
+
subject.Condition('q', subject.FnAnd([subject.FnEquals('x', 'x'), subject.Condition('r')]))
|
84
|
+
subject.Condition('r', subject.FnAnd([subject.FnEquals('x', 'x'), subject.Condition('s')]))
|
85
|
+
subject.Condition('s', subject.FnAnd([subject.FnEquals('x', 'x'), subject.Condition('q')]))
|
86
|
+
messages = subject.check_refs
|
87
|
+
expect(messages.size).to eq(1)
|
88
|
+
expect(messages.first).to match(/cyclic reference/i)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'detects invalid parameter references in Condition expressions' do
|
92
|
+
subject.Condition('x', subject.FnEquals('a', subject.Ref('p')))
|
93
|
+
messages = subject.check_refs
|
94
|
+
expect(messages.size).to eq(1)
|
95
|
+
expect(messages.first).to match(/^Invalid Reference: Conditions.*x.*p/)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'detects invalid condition references in Condition expressions' do
|
99
|
+
subject.Condition('d', subject.FnAnd([subject.FnEquals('x', 'x'), subject.Condition('c')]))
|
100
|
+
messages = subject.check_refs
|
101
|
+
expect(messages.size).to eq(1)
|
102
|
+
expect(messages.first).to match(/^Invalid Reference: Conditions.*d.*c/)
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'detects invalid condition references in Resource Conditions' do
|
106
|
+
subject.Resource('r') { Condition 'd' }
|
107
|
+
messages = subject.check_refs
|
108
|
+
expect(messages.size).to eq(1)
|
109
|
+
expect(messages.first).to match(/^Invalid Reference: Resources.*r.*d/)
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'detects invalid condition references in FnIf expressions deep inside Resources' do
|
113
|
+
subject.Resource('r') { Property(:p, FnIf(:d, 'vx', 'vy')) }
|
114
|
+
messages = subject.check_refs
|
115
|
+
expect(messages.size).to eq(1)
|
116
|
+
expect(messages.first).to match(/^Invalid Reference: Resources.*r.*d/)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'detects invalid condition references in Output Conditions' do
|
120
|
+
subject.Output('o') { Condition 'd' }
|
121
|
+
messages = subject.check_refs
|
122
|
+
expect(messages.size).to eq(1)
|
123
|
+
expect(messages.first).to match(/^Invalid Reference: Outputs.*o.*d/)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'detects invalid condition references in FnIf expressions deep inside Outputs' do
|
127
|
+
subject.Output('o') { Value(FnIf(:d, 'vx', 'vy')) }
|
128
|
+
messages = subject.check_refs
|
129
|
+
expect(messages.size).to eq(1)
|
130
|
+
expect(messages.first).to match(/^Invalid Reference: Outputs.*o.*d/)
|
66
131
|
end
|
67
132
|
|
68
133
|
it 'is a data-driven language' do
|
@@ -80,7 +145,7 @@ describe CfnDsl::CloudFormationTemplate do
|
|
80
145
|
end
|
81
146
|
|
82
147
|
it 'singularizes indirectly' do
|
83
|
-
user = subject.
|
148
|
+
user = subject.IAM_User 'TestUser'
|
84
149
|
policy = user.Policy 'stuff'
|
85
150
|
expect(policy).to eq('stuff')
|
86
151
|
|
@@ -104,8 +169,8 @@ describe CfnDsl::CloudFormationTemplate do
|
|
104
169
|
].each do |param|
|
105
170
|
ref = subject.Ref param
|
106
171
|
expect(ref.to_json).to eq("{\"Ref\":\"#{param}\"}")
|
107
|
-
refs = ref.build_references
|
108
|
-
expect(refs).to
|
172
|
+
refs = ref.build_references
|
173
|
+
expect(refs).to include(param)
|
109
174
|
end
|
110
175
|
end
|
111
176
|
|
@@ -140,8 +205,8 @@ describe CfnDsl::CloudFormationTemplate do
|
|
140
205
|
it 'Ref' do
|
141
206
|
ref = subject.Ref 'X'
|
142
207
|
expect(ref.to_json).to eq('{"Ref":"X"}')
|
143
|
-
refs = ref.build_references
|
144
|
-
expect(refs).to
|
208
|
+
refs = ref.build_references
|
209
|
+
expect(refs).to include('X')
|
145
210
|
end
|
146
211
|
|
147
212
|
it 'FnBase64' do
|
@@ -202,36 +267,10 @@ describe CfnDsl::CloudFormationTemplate do
|
|
202
267
|
end
|
203
268
|
end
|
204
269
|
|
205
|
-
context '
|
206
|
-
it 'formats correctly' do
|
207
|
-
func = subject.FnFormat('abc%0def%1ghi%%x', 'A', 'B')
|
208
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["abc","A","def","B","ghi","%","x"]]}')
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
context 'FnFormat', 'Hash' do
|
213
|
-
it 'formats correctly' do
|
214
|
-
func = subject.FnFormat('abc%{first}def%{second}ghi%%x', first: 'A', second: 'B')
|
215
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["abc","A","def","B","ghi","%","x"]]}')
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
|
-
context 'FnFormat', 'Multiline' do
|
220
|
-
it 'formats correctly' do
|
221
|
-
multiline = <<-EOF.gsub(/^ {10}/, '')
|
222
|
-
This is the first line
|
223
|
-
This is the %0 line
|
224
|
-
This is a %% sign
|
225
|
-
EOF
|
226
|
-
func = subject.FnFormat(multiline, 'second')
|
227
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["This is the first line\nThis is the ","second"," line\nThis is a ","%"," sign\n"]]}')
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
context 'FnFormat', 'Ref' do
|
270
|
+
context 'FnCidr', 'Array' do
|
232
271
|
it 'formats correctly' do
|
233
|
-
func = subject.
|
234
|
-
expect(func.to_json).to eq('{"Fn::
|
272
|
+
func = subject.FnCidr('10.0.0.0', '256', '8')
|
273
|
+
expect(func.to_json).to eq('{"Fn::Cidr":["10.0.0.0","256","8"]}')
|
235
274
|
end
|
236
275
|
end
|
237
276
|
end
|
data/spec/cli_spec.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe 'cfndsl', type: :aruba do
|
@@ -6,18 +8,18 @@ describe 'cfndsl', type: :aruba do
|
|
6
8
|
Usage: cfndsl [options] FILE
|
7
9
|
-o, --output FILE Write output to file
|
8
10
|
-y, --yaml FILE Import yaml file as local variables
|
9
|
-
-r, --ruby FILE Evaluate ruby file before template
|
10
11
|
-j, --json FILE Import json file as local variables
|
11
12
|
-p, --pretty Pretty-format output JSON
|
12
13
|
-f, --format FORMAT Specify the output format (JSON default)
|
13
14
|
-D, --define "VARIABLE=VALUE" Directly set local VARIABLE as VALUE
|
14
15
|
-v, --verbose Turn on verbose ouptut
|
15
|
-
-b, --disable-binding Disable binding configuration
|
16
16
|
-m, --disable-deep-merge Disable deep merging of yaml
|
17
17
|
-s, --specification-file FILE Location of Cloudformation Resource Specification file
|
18
|
-
-u,
|
18
|
+
-u [VERSION], Update the Resource Specification file to latest, or specific version
|
19
|
+
--update-specification
|
19
20
|
-g RESOURCE_TYPE,RESOURCE_LOGICAL_NAME,
|
20
21
|
--generate Add resource type and logical name
|
22
|
+
-a, --assetversion Print out the specification version
|
21
23
|
-l, --list List supported resources
|
22
24
|
-h, --help Display this screen
|
23
25
|
USAGE
|
@@ -33,20 +35,43 @@ describe 'cfndsl', type: :aruba do
|
|
33
35
|
|
34
36
|
before(:each) { write_file('template.rb', template_content) }
|
35
37
|
|
36
|
-
|
38
|
+
# The known working version is the embedded version
|
39
|
+
WORKING_SPEC_VERSION = JSON.parse(File.read(CfnDsl::LOCAL_SPEC_FILE))['ResourceSpecificationVersion']
|
40
|
+
|
41
|
+
context "cfndsl -u #{WORKING_SPEC_VERSION}" do
|
37
42
|
it 'updates the specification file' do
|
38
|
-
|
43
|
+
run_command "cfndsl -u #{WORKING_SPEC_VERSION}"
|
39
44
|
expect(last_command_started).to have_output_on_stderr(<<-OUTPUT.gsub(/^ {8}/, '').chomp)
|
40
45
|
Updating specification file
|
41
|
-
Specification successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json
|
46
|
+
Specification #{WORKING_SPEC_VERSION} successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json
|
42
47
|
OUTPUT
|
43
48
|
expect(last_command_started).to have_exit_status(0)
|
44
49
|
end
|
45
50
|
end
|
46
51
|
|
52
|
+
context 'cfndsl -u' do
|
53
|
+
it 'updates the specification file' do
|
54
|
+
run_command 'cfndsl -u'
|
55
|
+
|
56
|
+
expected = %r{Updating specification file
|
57
|
+
Specification ([0-9]+\.){2}[0-9]+ successfully written to #{ENV['HOME']}/.cfndsl/resource_specification.json}
|
58
|
+
|
59
|
+
expect(last_command_started).to have_output_on_stderr(expected)
|
60
|
+
expect(last_command_started).to have_exit_status(0)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'cfndsl -a' do
|
65
|
+
it 'prints out the specification file version' do
|
66
|
+
run_command 'cfndsl -a'
|
67
|
+
expect(last_command_started).to have_output_on_stderr(/([0-9]+\.){2}[0-9]+/)
|
68
|
+
expect(last_command_started).to have_exit_status(0)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
47
72
|
context 'cfndsl' do
|
48
73
|
it 'displays the usage' do
|
49
|
-
|
74
|
+
run_command'cfndsl'
|
50
75
|
expect(last_command_started).to have_output(usage)
|
51
76
|
expect(last_command_started).to have_exit_status(1)
|
52
77
|
end
|
@@ -54,31 +79,21 @@ describe 'cfndsl', type: :aruba do
|
|
54
79
|
|
55
80
|
context 'cfndsl --help' do
|
56
81
|
it 'displays the usage' do
|
57
|
-
|
82
|
+
run_command_and_stop 'cfndsl --help'
|
58
83
|
expect(last_command_started).to have_output(usage)
|
59
84
|
end
|
60
85
|
end
|
61
86
|
|
62
87
|
context 'cfndsl FILE' do
|
63
|
-
it 'gives a deprecation warning about bindings' do
|
64
|
-
run_simple 'cfndsl template.rb'
|
65
|
-
expect(last_command_started).to have_output_on_stderr(<<-WARN.gsub(/^ {8}/, '').chomp)
|
66
|
-
The creation of constants as config is deprecated!
|
67
|
-
Please switch to the #external_parameters method within your templates to access variables
|
68
|
-
See https://github.com/stevenjack/cfndsl/issues/170
|
69
|
-
Use the --disable-binding flag to suppress this message
|
70
|
-
WARN
|
71
|
-
end
|
72
|
-
|
73
88
|
it 'generates a JSON CloudFormation template' do
|
74
|
-
|
89
|
+
run_command_and_stop 'cfndsl template.rb'
|
75
90
|
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":"default"}')
|
76
91
|
end
|
77
92
|
end
|
78
93
|
|
79
94
|
context 'cfndsl FILE --pretty' do
|
80
95
|
it 'generates a pretty JSON CloudFormation template' do
|
81
|
-
|
96
|
+
run_command_and_stop 'cfndsl template.rb --pretty'
|
82
97
|
expect(last_command_started).to have_output_on_stdout(<<-OUTPUT.gsub(/^ {8}/, '').chomp)
|
83
98
|
{
|
84
99
|
"AWSTemplateFormatVersion": "2010-09-09",
|
@@ -90,7 +105,7 @@ describe 'cfndsl', type: :aruba do
|
|
90
105
|
|
91
106
|
context 'cfndsl FILE --output FILE' do
|
92
107
|
it 'writes the JSON CloudFormation template to a file' do
|
93
|
-
|
108
|
+
run_command_and_stop 'cfndsl template.rb --output template.json'
|
94
109
|
expect(read('template.json')).to eq(['{"AWSTemplateFormatVersion":"2010-09-09","Description":"default"}'])
|
95
110
|
end
|
96
111
|
end
|
@@ -99,7 +114,7 @@ describe 'cfndsl', type: :aruba do
|
|
99
114
|
before { write_file('params.yaml', 'DESC: yaml') }
|
100
115
|
|
101
116
|
it 'interpolates the YAML file in the CloudFormation template' do
|
102
|
-
|
117
|
+
run_command_and_stop 'cfndsl template.rb --yaml params.yaml'
|
103
118
|
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":"yaml"}')
|
104
119
|
end
|
105
120
|
end
|
@@ -108,40 +123,29 @@ describe 'cfndsl', type: :aruba do
|
|
108
123
|
before { write_file('params.json', '{"DESC":"json"}') }
|
109
124
|
|
110
125
|
it 'interpolates the JSON file in the CloudFormation template' do
|
111
|
-
|
126
|
+
run_command_and_stop 'cfndsl template.rb --json params.json'
|
112
127
|
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":"json"}')
|
113
128
|
end
|
114
129
|
end
|
115
130
|
|
116
|
-
context 'cfndsl FILE --
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
DESC = 'default' unless defined? DESC
|
121
|
-
Description DESC
|
122
|
-
end
|
123
|
-
TEMPLATE
|
124
|
-
end
|
125
|
-
|
126
|
-
before(:each) { write_file('params.rb', 'DESC = "ruby"') }
|
127
|
-
|
128
|
-
it 'interpolates the Ruby file in the CloudFormation template' do
|
129
|
-
run_simple 'cfndsl template.rb --ruby params.rb'
|
130
|
-
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":"ruby"}')
|
131
|
+
context 'cfndsl FILE --define VARIABLE=VALUE' do
|
132
|
+
it 'interpolates the command line variables in the CloudFormation template' do
|
133
|
+
run_command "cfndsl template.rb --define \"DESC='cli'\""
|
134
|
+
expect(last_command_started).to have_output_on_stdout("{\"AWSTemplateFormatVersion\":\"2010-09-09\",\"Description\":\"'cli'\"}")
|
131
135
|
end
|
136
|
+
end
|
132
137
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
expect(last_command_started).to
|
137
|
-
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":"default"}')
|
138
|
+
context 'cfndsl FILE --define VARIABLE=true' do
|
139
|
+
it 'interpolates the command line variable with value true in the CloudFormation template ' do
|
140
|
+
run_command 'cfndsl template.rb --define "DESC=true"'
|
141
|
+
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":true}')
|
138
142
|
end
|
139
143
|
end
|
140
144
|
|
141
|
-
context 'cfndsl FILE --define VARIABLE=
|
142
|
-
it 'interpolates the command line
|
143
|
-
|
144
|
-
expect(last_command_started).to have_output_on_stdout(
|
145
|
+
context 'cfndsl FILE --define VARIABLE=false' do
|
146
|
+
it 'interpolates the command line variable with value false in the CloudFormation template ' do
|
147
|
+
run_command 'cfndsl template.rb --define "DESC=false"'
|
148
|
+
expect(last_command_started).to have_output_on_stdout('{"AWSTemplateFormatVersion":"2010-09-09","Description":"default"}')
|
145
149
|
end
|
146
150
|
end
|
147
151
|
|
@@ -149,11 +153,10 @@ describe 'cfndsl', type: :aruba do
|
|
149
153
|
before { write_file('params.yaml', 'DESC: yaml') }
|
150
154
|
|
151
155
|
it 'displays the variables as they are interpolated in the CloudFormation template' do
|
152
|
-
|
156
|
+
run_command_and_stop 'cfndsl template.rb --yaml params.yaml --verbose'
|
153
157
|
verbose = /
|
154
158
|
Using \s specification \s file .* \.json \n
|
155
159
|
Loading \s YAML \s file \s .* params\.yaml \n
|
156
|
-
Setting \s local \s variable \s DESC \s to \s yaml \n
|
157
160
|
Loading \s template \s file \s .* template.rb \n
|
158
161
|
Writing \s to \s STDOUT
|
159
162
|
/x
|
@@ -1,5 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
|
3
5
|
describe CfnDsl::CloudFormationTemplate do
|
4
6
|
it_behaves_like 'an orchestration template'
|
7
|
+
|
8
|
+
context '#validate' do
|
9
|
+
it 'returns self if the template is empty' do
|
10
|
+
# TODO: Strictly Cloudformation requires at least one resource, but this is not validated yet.
|
11
|
+
expect(subject.validate).to equal(subject)
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'resources' do
|
15
|
+
it 'returns self if there are valid Refs to parameters' do
|
16
|
+
subject.Parameter('TestParameter').Type('String')
|
17
|
+
r = subject.Resource(:TestResource)
|
18
|
+
r.Type('Custom-TestType')
|
19
|
+
r.Property(:AProperty, r.Ref(:TestParameter))
|
20
|
+
expect(subject.validate).to equal(subject)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns self if there are valid Refs to other resources' do
|
24
|
+
tr = subject.Resource(:TestResource)
|
25
|
+
tr.Type('Custom-TestType')
|
26
|
+
tr.Property(:AProperty, tr.Ref(:TestResource2))
|
27
|
+
|
28
|
+
t2 = subject.Resource('TestResource2')
|
29
|
+
t2.Type('Custom-TestType')
|
30
|
+
expect(subject.validate).to equal(subject)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'returns self if there are valid Fn::GetAtt references to other resources' do
|
34
|
+
tr = subject.Resource(:TestResource)
|
35
|
+
tr.Type('Custom-TestType')
|
36
|
+
tr.Property(:AProperty, tr.FnGetAtt(:TestResource2, :AnAttribute))
|
37
|
+
|
38
|
+
t2 = subject.Resource('TestResource2')
|
39
|
+
t2.Type('Custom-TestType')
|
40
|
+
expect(subject.validate).to equal(subject)
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'returns self if there are valid DependsOn to other resources' do
|
44
|
+
tr = subject.Resource(:TestResource)
|
45
|
+
tr.Type('Custom-TestType')
|
46
|
+
tr.DependsOn(:TestResource2)
|
47
|
+
|
48
|
+
t2 = subject.Resource('TestResource2')
|
49
|
+
t2.Type('Custom-TestType')
|
50
|
+
expect(subject.validate).to equal(subject)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'raises CfnDsl::Error if references a non existent condition' do
|
54
|
+
tr = subject.Resource(:TestResource)
|
55
|
+
tr.Condition('NoCondition')
|
56
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*NoCondition/)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises CfnDsl::Error if there are invalid Refs' do
|
60
|
+
tr = subject.Resource(:TestResource)
|
61
|
+
tr.Type('Custom-TestType')
|
62
|
+
tr.Property(:AProperty, tr.Ref(:TestResource2))
|
63
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /^Invalid Reference:.*TestResource.*TestResource2/)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'raises CfnDsl::Error if there are invalid Fn::GetAtt references' do
|
67
|
+
tr = subject.Resource(:TestResource)
|
68
|
+
tr.Type('Custom-TestType')
|
69
|
+
tr.Property(:AProperty, tr.FnGetAtt(:TestResource2, :AnAttribute))
|
70
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /^Invalid Reference:.*TestResource.*TestResource2/)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'raises CfnDsl::Error if there are invalid Fn::Sub attribute references' do
|
74
|
+
tr = subject.Resource(:TestResource)
|
75
|
+
tr.Type('Custom-TestType')
|
76
|
+
tr.Property(:AProperty, tr.FnBase64(tr.FnSub('${TestResource2.AnAttribute}')))
|
77
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /^Invalid Reference:.*TestResource.*TestResource2/)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'raises CfnDsl::Error if there are invalid Fn::Sub references' do
|
81
|
+
tr = subject.Resource(:TestResource)
|
82
|
+
tr.Type('Custom-TestType')
|
83
|
+
tr.Property(:AProperty, tr.FnBase64(tr.FnSub('${TestResource2}')))
|
84
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /^Invalid Reference:.*TestResource.*TestResource2/)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'raises CfnDsl::Error if there are invalid DependsOn' do
|
88
|
+
tr = subject.Resource(:TestResource)
|
89
|
+
tr.Type('Custom-TestType')
|
90
|
+
tr.DependsOn(['TestResource2'])
|
91
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /^Invalid Reference:.*TestResource.*TestResource2/)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'raises CfnDsl::Error if a resource explicitly DependsOn itself' do
|
95
|
+
tr = subject.Resource(:TestResource)
|
96
|
+
tr.Type('Custom-TestType')
|
97
|
+
tr.DependsOn(['TestResource'])
|
98
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*references itself/)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'raises CfnDsl::Error if a resource Refs itself' do
|
102
|
+
tr = subject.Resource(:TestResource)
|
103
|
+
tr.Type('Custom-TestType')
|
104
|
+
tr.Property(:AProperty, tr.Ref(:TestResource))
|
105
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*itself/i)
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'raises CfnDsl::Error if a resource references itself in Fn::GetAtt' do
|
109
|
+
tr = subject.Resource(:TestResource)
|
110
|
+
tr.Type('Custom-TestType')
|
111
|
+
tr.Property(:AProperty, tr.FnGetAtt(:TestResource, :AnAttr))
|
112
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*itself/i)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'raises CfnDsl::Error if a resourcs references itself in Fn::Sub expression' do
|
116
|
+
tr = subject.Resource(:TestResource)
|
117
|
+
tr.Type('Custom-TestType')
|
118
|
+
tr.Property(:AProperty, tr.FnSub('${TestResource}'))
|
119
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestResource.*itself/i)
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'raises CfnDsl::Error if there are cyclic DependsOn references' do
|
123
|
+
tr = subject.Resource(:ResourceOne)
|
124
|
+
tr.Type('Custom-TestType')
|
125
|
+
tr.DependsOn(:ResourceTwo)
|
126
|
+
|
127
|
+
t2 = subject.Resource('ResourceTwo')
|
128
|
+
t2.Type('Custom-TestType')
|
129
|
+
t2.DependsOn(:ResourceOne)
|
130
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /cyclic.*ResourceTwo.*ResourceOne/i)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'raises CfnDsl::Error if there are cyclic Refs' do
|
134
|
+
tr = subject.Resource(:TestResourceOne)
|
135
|
+
tr.Type('Custom-TestType')
|
136
|
+
tr.Property(:AProperty, tr.Ref(:TestResourceTwo))
|
137
|
+
|
138
|
+
t2 = subject.Resource('TestResourceTwo')
|
139
|
+
t2.Type('Custom-TestType')
|
140
|
+
t2.DependsOn('TestResourceOne')
|
141
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /cyclic.*Two.*One/i)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'raises CfnDsl::Error if there are cyclic Fn::GetAtt references' do
|
145
|
+
tr = subject.Resource(:TestResource1)
|
146
|
+
tr.Property(:AProperty, tr.Ref(:TestResource2))
|
147
|
+
|
148
|
+
t2 = subject.Resource('TestResource2')
|
149
|
+
t2.DependsOn('TestResource3')
|
150
|
+
|
151
|
+
t3 = subject.Resource('TestResource3')
|
152
|
+
t3.Property(:OtherProperty, subject.FnGetAtt('TestResource1', :OtherAttribute))
|
153
|
+
|
154
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /cyclic.*3.*2.*1/i)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'raises CfnDsl::Error if there are cyclic Fn::Sub references' do
|
158
|
+
subject.Resource(:TestResource1)
|
159
|
+
|
160
|
+
t2 = subject.Resource('TestResource2')
|
161
|
+
t2.DependsOn(%w[TestResource3 TestResource1])
|
162
|
+
|
163
|
+
t3 = subject.Resource('TestResource3')
|
164
|
+
t3.Property(:OtherProperty, subject.FnSub('SomeValue ${TestResource2}'))
|
165
|
+
|
166
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /cyclic*.3?.*2/i)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
context 'conditions' do
|
171
|
+
it 'returns self if there are valid condition references' do
|
172
|
+
subject.Parameter('TestParameter').Type('String')
|
173
|
+
subject.Condition(:TestCondition, subject.FnEquals(subject.Ref(:TestParameter), 'testvalue'))
|
174
|
+
expect(subject.validate).to equal(subject)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'raises CfnDsl::Error if invalid ref in condition' do
|
178
|
+
subject.Condition(:TestCondition, subject.FnEquals(subject.Ref(:NoParam), 'testvalue'))
|
179
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /^Invalid Reference:.*TestCondition.*NoParam/)
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'raises CfnDsl::Error if null value in Condition' do
|
183
|
+
subject.Condition(:TestCondition, subject.FnEquals(nil, 'testvalue'))
|
184
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /Condition.*TestCondition.*null/)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'raises CfnDsl::Error if null value deep in Condition' do
|
188
|
+
subject.Condition(:TestCondition, subject.FnEquals({ Condition: nil }, 'testvalue'))
|
189
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /Condition.*TestCondition.*null/)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Note cycles in conditions tested in cfndsl_spec.rb
|
193
|
+
end
|
194
|
+
|
195
|
+
context 'outputs' do
|
196
|
+
it 'returns self if there are valid Refs to parameters' do
|
197
|
+
subject.Parameter('TestParameter').Type('String')
|
198
|
+
subject.Output('TestOutput').Value(subject.Ref(:TestParameter))
|
199
|
+
expect(subject.validate).to equal(subject)
|
200
|
+
end
|
201
|
+
|
202
|
+
it 'returns self if there are valid Refs to resources' do
|
203
|
+
subject.Resource(:TestResource)
|
204
|
+
subject.Output('TestResourceOutput').Value(subject.Ref(:TestResource))
|
205
|
+
expect(subject.validate).to equal(subject)
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'returns self if there are valid Fn::GetAtt references to resources' do
|
209
|
+
subject.Resource(:TestResource)
|
210
|
+
subject.Output('TestResourceOutput').Value(subject.FnGetAtt(:TestResource, :AnAtt))
|
211
|
+
expect(subject.validate).to equal(subject)
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'raises CfnDsl::Error if references a non existent condition' do
|
215
|
+
subject.Output(:TestOutput).Condition('NoCondition')
|
216
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /TestOutput.*NoCondition/)
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'raises CfnDsl::Error if there are invalid Refs' do
|
220
|
+
subject.Output('TestResourceOutput').Value(subject.Ref(:TestResource))
|
221
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /Outputs.*TestResource/)
|
222
|
+
end
|
223
|
+
|
224
|
+
it 'raises CfnDsl::Error if there are invalid Fn::GetAtt references' do
|
225
|
+
subject.Output('TestResourceOutput').Value(subject.FnGetAtt(:TestResource, :Attr))
|
226
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /Outputs.*TestResource/)
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'raises CfnDsl::Error if there are invalid Fn::Sub attribute references' do
|
230
|
+
subject.Output('TestResourceOutput').Value(subject.FnSub('prefix ${SomeRef.attr}suffix'))
|
231
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /Outputs.*TestResource/)
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'raises CfnDsl::Error if there are invalid Fn::Sub references' do
|
235
|
+
subject.Output('TestResourceOutput').Value(subject.FnSub('prefix ${SomeRef}suffix'))
|
236
|
+
expect { subject.validate }.to raise_error(CfnDsl::Error, /Outputs.*TestResource/)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
5
240
|
end
|