cfndsl 0.17.5 → 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -4
- data/.travis.yml +6 -1
- data/CHANGELOG.md +32 -34
- data/README.md +68 -21
- data/Rakefile +25 -3
- data/TODO.md +18 -0
- data/UPGRADING.md +22 -0
- data/cfndsl.gemspec +3 -3
- data/exe/cfndsl +5 -0
- data/lib/cfndsl.rb +2 -116
- data/lib/cfndsl/aws/cloud_formation_template.rb +8 -1
- data/lib/cfndsl/aws/patches/000_sam.spec.json +574 -0
- data/lib/cfndsl/aws/patches/100_sam.spec_DeploymentPreference_patch.json +64 -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_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/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 +35809 -11627
- data/lib/cfndsl/aws/types.rb +3 -3
- data/lib/cfndsl/cfnlego.rb +34 -0
- data/lib/{cfnlego → cfndsl/cfnlego}/cloudformation.erb +0 -0
- data/lib/{cfnlego → cfndsl/cfnlego}/cloudformation.rb +0 -0
- data/lib/{cfnlego → cfndsl/cfnlego}/resource.rb +3 -8
- data/lib/cfndsl/cloudformation.rb +107 -0
- data/lib/cfndsl/conditions.rb +11 -1
- data/lib/cfndsl/creation_policy.rb +1 -1
- data/lib/cfndsl/deep_merge.rb +4 -0
- data/lib/cfndsl/external_parameters.rb +4 -13
- data/lib/cfndsl/globals.rb +48 -9
- data/lib/cfndsl/jsonable.rb +22 -60
- data/lib/cfndsl/mappings.rb +1 -1
- data/lib/cfndsl/module.rb +16 -5
- data/lib/cfndsl/orchestration_template.rb +185 -83
- data/lib/cfndsl/outputs.rb +5 -1
- data/lib/cfndsl/parameters.rb +1 -1
- data/lib/cfndsl/plurals.rb +12 -1
- data/lib/cfndsl/properties.rb +1 -1
- data/lib/cfndsl/rake_task.rb +206 -12
- data/lib/cfndsl/ref_check.rb +19 -11
- data/lib/cfndsl/resources.rb +6 -19
- data/lib/cfndsl/rules.rb +1 -1
- data/lib/cfndsl/runner.rb +143 -0
- data/lib/cfndsl/specification.rb +80 -95
- data/lib/cfndsl/types.rb +205 -91
- data/lib/cfndsl/update_policy.rb +1 -1
- data/lib/cfndsl/version.rb +1 -1
- data/sample/autoscale.rb +0 -1
- data/sample/autoscale2.rb +0 -1
- data/sample/config_service.rb +2 -2
- data/sample/t1.rb +1 -1
- data/sample/vpc_example.rb +1 -1
- data/sample/vpc_with_vpn_example.rb +1 -1
- data/spec/aws/list_type_patches_spec.rb +35 -0
- data/spec/aws/nested_arrays_spec.rb +155 -3
- data/spec/aws/serverless_spec.rb +0 -2
- data/spec/cfndsl_spec.rb +94 -78
- data/spec/cli_spec.rb +16 -54
- data/spec/cloud_formation_template_spec.rb +233 -0
- data/spec/condition_spec.rb +24 -0
- data/spec/direct_ruby_spec.rb +19 -0
- data/spec/external_parameters_spec.rb +2 -15
- data/spec/fixtures/condition-assertion.json +1 -0
- data/spec/fixtures/test.rb +2 -1
- data/spec/generate_spec.rb +4 -2
- data/spec/resources_spec.rb +0 -7
- data/spec/spec_helper.rb +2 -7
- data/spec/support/shared_examples/orchestration_template.rb +15 -2
- data/spec/types_definition_spec.rb +3 -6
- metadata +52 -23
- data/bin/cfndsl +0 -160
- data/lib/cfndsl/errors.rb +0 -31
- data/lib/cfndsl/os/heat_template.rb +0 -18
- data/lib/cfndsl/os/types.rb +0 -14
- data/lib/cfndsl/os/types.yaml +0 -2423
- data/lib/cfndsl/patches.rb +0 -226
- data/lib/cfnlego.rb +0 -44
- data/spec/fixtures/heattest.rb +0 -24
- data/spec/heat_template_spec.rb +0 -7
data/lib/cfndsl/update_policy.rb
CHANGED
data/lib/cfndsl/version.rb
CHANGED
data/sample/autoscale.rb
CHANGED
@@ -16,7 +16,6 @@ availability zones in a region. The instances are load balanced with a
|
|
16
16
|
simple health check. The web site is available on port 80, however,
|
17
17
|
the instances can be configured to listen on any port (8888 by
|
18
18
|
default).
|
19
|
-
|
20
19
|
**WARNING** This template creates one or more Amazon EC2
|
21
20
|
instances. You will be billed for the AWS resources used if you create
|
22
21
|
a stack from this template.
|
data/sample/autoscale2.rb
CHANGED
@@ -16,7 +16,6 @@ availability zones in a region. The instances are load balanced with a
|
|
16
16
|
simple health check. The web site is available on port 80, however,
|
17
17
|
the instances can be configured to listen on any port (8888 by
|
18
18
|
default).
|
19
|
-
|
20
19
|
**WARNING** This template creates one or more Amazon EC2
|
21
20
|
instances. You will be billed for the AWS resources used if you create
|
22
21
|
a stack from this template.
|
data/sample/config_service.rb
CHANGED
@@ -12,7 +12,7 @@ CloudFormation do
|
|
12
12
|
Bucket('ConfigServiceBucket') do
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
IAM_Policy('ConfigServiceS3BucketAccessPolicy') do
|
16
16
|
PolicyName 'ConfigServiceS3BucketAccessPolicy'
|
17
17
|
PolicyDocument(
|
18
18
|
'Version' => '2012-10-17',
|
@@ -68,7 +68,7 @@ CloudFormation do
|
|
68
68
|
}]
|
69
69
|
end
|
70
70
|
|
71
|
-
|
71
|
+
IAM_Policy('ConfigServiceSNSTopicAccessPolicy') do
|
72
72
|
PolicyName 'ConfigServiceSNSTopicAccessPolicy'
|
73
73
|
PolicyDocument(
|
74
74
|
'Version' => '2012-10-17',
|
data/sample/t1.rb
CHANGED
data/sample/vpc_example.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe CfnDsl::CloudFormationTemplate do
|
6
|
+
subject(:template) { described_class.new }
|
7
|
+
|
8
|
+
describe 'Bogus Properties that are type List' do
|
9
|
+
it 'fixes Resource property type to a List of its item type' do
|
10
|
+
template.LakeFormation_DataLakeSettings('lake') do
|
11
|
+
Admin { DataLakePrincipalIdentifier 'principal' }
|
12
|
+
end
|
13
|
+
expect(template.to_json).to include('"Type":"AWS::LakeFormation::DataLakeSettings","Properties":{"Admins":[{"DataLakePrincipalIdentifier":"principal"')
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'fixes Subtype property Tags to a list of Tags' do
|
17
|
+
template.AppSync_GraphQLApi('api') do
|
18
|
+
Tag do
|
19
|
+
Key 'tagkey'
|
20
|
+
Value 'tagvalue'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
expect(template.to_json).to include('"Properties":{"Tags":[{"Key":"tagkey","Value":"tagvalue"}]}}}')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'fixes Subtype property type to a List of its item type' do
|
27
|
+
template.Glue_SecurityConfiguration('glue') do
|
28
|
+
EncryptionConfiguration do
|
29
|
+
S3Encryption { S3EncryptionMode 'mode' }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
expect(template.to_json).to include('"EncryptionConfiguration":{"S3Encryptions":[{"S3EncryptionMode":"mode"}]}}}}}')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -5,7 +5,7 @@ require 'spec_helper'
|
|
5
5
|
describe CfnDsl::CloudFormationTemplate do
|
6
6
|
subject(:template) { described_class.new }
|
7
7
|
|
8
|
-
describe '
|
8
|
+
describe 'Nested_Arrays' do
|
9
9
|
it 'ensure nested arrays are not duplicated' do
|
10
10
|
template.DirectoryService_SimpleAD(:Test) do
|
11
11
|
VpcSettings do
|
@@ -17,7 +17,7 @@ describe CfnDsl::CloudFormationTemplate do
|
|
17
17
|
expect(template.to_json).not_to include('"SubnetIds":[["subnet-a","subnet-b"],["subnet-a","subnet-b"]]')
|
18
18
|
end
|
19
19
|
|
20
|
-
it '
|
20
|
+
it 'appends entries with multiple invocations' do
|
21
21
|
template.DirectoryService_SimpleAD(:Test) do
|
22
22
|
VpcSettings do
|
23
23
|
SubnetId 'subnet-a'
|
@@ -28,7 +28,7 @@ describe CfnDsl::CloudFormationTemplate do
|
|
28
28
|
expect(template.to_json).to include('"SubnetIds":["subnet-a","subnet-b"]}')
|
29
29
|
end
|
30
30
|
|
31
|
-
it '
|
31
|
+
it 'appends entries with multiple array invocations' do
|
32
32
|
template.DirectoryService_SimpleAD(:Test) do
|
33
33
|
VpcSettings do
|
34
34
|
SubnetId %w[subnet-a subnet-b]
|
@@ -38,5 +38,157 @@ describe CfnDsl::CloudFormationTemplate do
|
|
38
38
|
|
39
39
|
expect(template.to_json).to include('"SubnetIds":["subnet-a","subnet-b","subnet-c","subnet-d"]')
|
40
40
|
end
|
41
|
+
|
42
|
+
# Previous behaviour produces valid result, but appends rather than replaces (inconsistent with top level item)
|
43
|
+
it 'appends entries when plural form is used' do
|
44
|
+
template.DirectoryService_SimpleAD(:Test) do
|
45
|
+
VpcSettings do
|
46
|
+
SubnetId 'subnet-x'
|
47
|
+
SubnetIds %w[subnet-a subnet-b]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(template.to_json).to include('"SubnetIds":["subnet-a","subnet-b"]}')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'plural form accepts a Ref' do
|
55
|
+
template.EC2_LaunchTemplate(:test) do
|
56
|
+
LaunchTemplateData do
|
57
|
+
SecurityGroupIds Ref('AListParam')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
expect(template.to_json).to include('"LaunchTemplateData":{"SecurityGroupIds":{"Ref":"AListParam"}')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'Nested Arrays With Subtype items' do
|
65
|
+
it 'can add a subtype to a list' do
|
66
|
+
template.EC2_SpotFleet('SpotFleet') do
|
67
|
+
SpotFleetRequestConfigData do
|
68
|
+
LaunchSpecification do
|
69
|
+
ImageId 'ami-1234'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
expect(template.to_json).to include('"LaunchSpecifications":[{"ImageId":"ami-1234"}]')
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'can conditionally add a subtype to a list' do
|
77
|
+
template.EC2_SpotFleet('SpotFleet') do
|
78
|
+
SpotFleetRequestConfigData do
|
79
|
+
# We want all the DSL goodness
|
80
|
+
LaunchSpecification fn_if: 'ACondition' do
|
81
|
+
ImageId 'ami-1234'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
json = template.to_json
|
86
|
+
expect(json).to include('"LaunchSpecifications":[{"Fn::If":["ACondition",{"ImageId":"ami-1234"},{"Ref":"AWS::NoValue"}]}]')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe 'Top level Lists' do
|
91
|
+
it 'appends item if singular form != plural form is passed a single item' do
|
92
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
93
|
+
AvailabilityZone 'region-2a'
|
94
|
+
AvailabilityZone 'region-2b'
|
95
|
+
end
|
96
|
+
expect(template.to_json).to include('"AvailabilityZones":["region-2a","region-2b"]')
|
97
|
+
end
|
98
|
+
|
99
|
+
# Change from prior behaviour which produced an invalid result
|
100
|
+
it 'appends multiple items if singular form != plural form is passed an array' do
|
101
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
102
|
+
AvailabilityZone %w[region-2a region-2b]
|
103
|
+
AvailabilityZone ['region-2c']
|
104
|
+
end
|
105
|
+
expect(template.to_json).to include('"AvailabilityZones":["region-2a","region-2b","region-2c"]')
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'replaces items if plural form is passed an array' do
|
109
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
110
|
+
AvailabilityZones ['region-2z']
|
111
|
+
AvailabilityZones ['region-2a']
|
112
|
+
end
|
113
|
+
expect(template.to_json).to include('"AvailabilityZones":["region-2a"]')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'accepts a Ref in plural form' do
|
117
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
118
|
+
AvailabilityZones Ref('AListParam')
|
119
|
+
end
|
120
|
+
expect(template.to_json).to include('"AvailabilityZones":{"Ref":"AListParam"}')
|
121
|
+
end
|
122
|
+
|
123
|
+
# This produces an invalid result (item not encapsulated as a List), so could be changed
|
124
|
+
# later
|
125
|
+
it 'replaces items if plural form is passed a single item' do
|
126
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
127
|
+
AvailabilityZones 'region-xx'
|
128
|
+
AvailabilityZones 'region-2a'
|
129
|
+
end
|
130
|
+
expect(template.to_json).to include('"AvailabilityZones":"region-2a"')
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'replaces items if list attribute is singlar, and plural form is passed an array' do
|
134
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
135
|
+
VPCZoneIdentifiers ['subnet-9999']
|
136
|
+
VPCZoneIdentifiers ['subnet-1234']
|
137
|
+
end
|
138
|
+
expect(template.to_json).to include('"VPCZoneIdentifier":["subnet-1234"]')
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'replaces items if list attribute is singlar, and plural form is passed an array' do
|
142
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
143
|
+
VPCZoneIdentifier 'subnet-9999'
|
144
|
+
VPCZoneIdentifiers ['subnet-1234']
|
145
|
+
end
|
146
|
+
expect(template.to_json).to include('"VPCZoneIdentifier":["subnet-1234"]')
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'appends items if list attribute is singlar and passed arrays' do
|
150
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
151
|
+
VPCZoneIdentifier ['subnet-9999']
|
152
|
+
VPCZoneIdentifier ['subnet-1234']
|
153
|
+
end
|
154
|
+
expect(template.to_json).to include('"VPCZoneIdentifier":["subnet-9999","subnet-1234"]')
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'appends items if plural form == singular form and passed a single item' do
|
158
|
+
template.AutoScaling_AutoScalingGroup('ASG') do
|
159
|
+
VPCZoneIdentifier 'subnet-9999'
|
160
|
+
VPCZoneIdentifier 'subnet-1234'
|
161
|
+
end
|
162
|
+
expect(template.to_json).to include('"VPCZoneIdentifier":["subnet-9999","subnet-1234"]')
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'does not create plural method if both singlular and plural forms are legitimate properties' do
|
166
|
+
template.CodePipeline_Pipeline('pipeline') do
|
167
|
+
ArtifactStore do
|
168
|
+
Location 'abucketname'
|
169
|
+
Type 'S3'
|
170
|
+
end
|
171
|
+
ArtifactStores do
|
172
|
+
ArtifactStore do
|
173
|
+
Location 'a different bucket'
|
174
|
+
Type 'S3'
|
175
|
+
end
|
176
|
+
Region 'ax-eastwest-5'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
json = template.to_json
|
180
|
+
expect(json).to include('"ArtifactStore":{"Location":"abucketname"')
|
181
|
+
expect(json).to include('"ArtifactStores":[{"ArtifactStore":{')
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'can conditionally add a subtype to a list property' do
|
185
|
+
template.SSM_MaintenanceWindowTask('Task') do
|
186
|
+
# We want all the DSL goodness
|
187
|
+
Target fn_if: 'ACondition' do
|
188
|
+
Key 'AKey'
|
189
|
+
end
|
190
|
+
end
|
191
|
+
expect(template.to_json).to include('"Targets":[{"Fn::If":["ACondition",{"Key":"AKey"},{"Ref":"AWS::NoValue"}]}]')
|
192
|
+
end
|
41
193
|
end
|
42
194
|
end
|
data/spec/aws/serverless_spec.rb
CHANGED
@@ -30,7 +30,6 @@ describe CfnDsl::CloudFormationTemplate do
|
|
30
30
|
}
|
31
31
|
)
|
32
32
|
end
|
33
|
-
# File.open('/tmp/dump', 'w') { |f| f.write(template.to_json) }
|
34
33
|
expect(JSON.parse(template.to_json)).to eq(read_json_fixture('serverless-function.json'))
|
35
34
|
end
|
36
35
|
|
@@ -42,7 +41,6 @@ describe CfnDsl::CloudFormationTemplate do
|
|
42
41
|
CacheClusterSize '512M'
|
43
42
|
Variables(Var1: 'value1')
|
44
43
|
end
|
45
|
-
File.open('/tmp/dump', 'w') { |f| f.write(template.to_json) }
|
46
44
|
expect(JSON.parse(template.to_json)).to eq(read_json_fixture('serverless-api.json'))
|
47
45
|
end
|
48
46
|
end
|
data/spec/cfndsl_spec.rb
CHANGED
@@ -4,44 +4,12 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe CfnDsl do
|
6
6
|
let(:test_template_file_name) { "#{File.dirname(__FILE__)}/fixtures/test.rb" }
|
7
|
-
let(:heat_test_template_file_name) { "#{File.dirname(__FILE__)}/fixtures/heattest.rb" }
|
8
7
|
|
9
8
|
after(:example) { CfnDsl::ExternalParameters.refresh! }
|
10
9
|
|
11
10
|
it 'evaluates a cloud formation' do
|
12
11
|
subject.eval_file_with_extras(test_template_file_name, [[:raw, 'test=123']])
|
13
12
|
end
|
14
|
-
|
15
|
-
it 'evaluates a heat' do
|
16
|
-
subject.eval_file_with_extras(heat_test_template_file_name)
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'when binding is disabed' do
|
20
|
-
let(:param_value) { 'www.google.com?a=1&b=2' }
|
21
|
-
before do
|
22
|
-
CfnDsl.disable_binding
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'evaluates parameters correctly when its value contains "="' do
|
26
|
-
template = subject.eval_file_with_extras(test_template_file_name, [[:raw, "three=#{param_value}"]]).to_json
|
27
|
-
parsed_template = JSON.parse(template)
|
28
|
-
expect(parsed_template['Parameters']['Three']['Default']).to eq param_value
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
describe CfnDsl::HeatTemplate do
|
34
|
-
it 'honors last-set value for non-array properties' do
|
35
|
-
spec = self
|
36
|
-
subject.declare do
|
37
|
-
Server('myserver') do
|
38
|
-
flavor 'foo'
|
39
|
-
flavor 'bar'
|
40
|
-
f = @Properties['flavor'].value
|
41
|
-
spec.expect(f).to spec.eq('bar')
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
13
|
end
|
46
14
|
|
47
15
|
describe CfnDsl::CloudFormationTemplate do
|
@@ -69,16 +37,97 @@ describe CfnDsl::CloudFormationTemplate do
|
|
69
37
|
end
|
70
38
|
end
|
71
39
|
|
72
|
-
it '
|
40
|
+
it 'detects cyclic Resource references' do
|
73
41
|
q = subject.Resource('q') { DependsOn ['r'] }
|
74
42
|
r = subject.Resource('r') { Property('z', Ref('q')) }
|
75
|
-
q_refs = q.build_references
|
76
|
-
r_refs = r.build_references
|
77
|
-
expect(q_refs).to
|
78
|
-
expect(q_refs).to_not
|
79
|
-
expect(r_refs).to
|
80
|
-
expect(r_refs).to_not
|
81
|
-
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/)
|
82
131
|
end
|
83
132
|
|
84
133
|
it 'is a data-driven language' do
|
@@ -96,7 +145,7 @@ describe CfnDsl::CloudFormationTemplate do
|
|
96
145
|
end
|
97
146
|
|
98
147
|
it 'singularizes indirectly' do
|
99
|
-
user = subject.
|
148
|
+
user = subject.IAM_User 'TestUser'
|
100
149
|
policy = user.Policy 'stuff'
|
101
150
|
expect(policy).to eq('stuff')
|
102
151
|
|
@@ -120,8 +169,8 @@ describe CfnDsl::CloudFormationTemplate do
|
|
120
169
|
].each do |param|
|
121
170
|
ref = subject.Ref param
|
122
171
|
expect(ref.to_json).to eq("{\"Ref\":\"#{param}\"}")
|
123
|
-
refs = ref.build_references
|
124
|
-
expect(refs).to
|
172
|
+
refs = ref.build_references
|
173
|
+
expect(refs).to include(param)
|
125
174
|
end
|
126
175
|
end
|
127
176
|
|
@@ -156,8 +205,8 @@ describe CfnDsl::CloudFormationTemplate do
|
|
156
205
|
it 'Ref' do
|
157
206
|
ref = subject.Ref 'X'
|
158
207
|
expect(ref.to_json).to eq('{"Ref":"X"}')
|
159
|
-
refs = ref.build_references
|
160
|
-
expect(refs).to
|
208
|
+
refs = ref.build_references
|
209
|
+
expect(refs).to include('X')
|
161
210
|
end
|
162
211
|
|
163
212
|
it 'FnBase64' do
|
@@ -218,39 +267,6 @@ describe CfnDsl::CloudFormationTemplate do
|
|
218
267
|
end
|
219
268
|
end
|
220
269
|
|
221
|
-
context 'FnFormat', 'String' do
|
222
|
-
it 'formats correctly' do
|
223
|
-
func = subject.FnFormat('abc%0def%1ghi%%x', 'A', 'B')
|
224
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["abc","A","def","B","ghi","%","x"]]}')
|
225
|
-
end
|
226
|
-
end
|
227
|
-
|
228
|
-
context 'FnFormat', 'Hash' do
|
229
|
-
it 'formats correctly' do
|
230
|
-
func = subject.FnFormat('abc%{first}def%{second}ghi%%x', first: 'A', second: 'B')
|
231
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["abc","A","def","B","ghi","%","x"]]}')
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
context 'FnFormat', 'Multiline' do
|
236
|
-
it 'formats correctly' do
|
237
|
-
multiline = <<-TEXT.gsub(/^ {10}/, '')
|
238
|
-
This is the first line
|
239
|
-
This is the %0 line
|
240
|
-
This is a %% sign
|
241
|
-
TEXT
|
242
|
-
func = subject.FnFormat(multiline, 'second')
|
243
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["This is the first line\nThis is the ","second"," line\nThis is a ","%"," sign\n"]]}')
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
context 'FnFormat', 'Ref' do
|
248
|
-
it 'formats correctly' do
|
249
|
-
func = subject.FnFormat '123%{Test}456'
|
250
|
-
expect(func.to_json).to eq('{"Fn::Join":["",["123",{"Ref":"Test"},"456"]]}')
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
270
|
context 'FnCidr', 'Array' do
|
255
271
|
it 'formats correctly' do
|
256
272
|
func = subject.FnCidr('10.0.0.0', '256', '8')
|