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.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +4 -4
  4. data/.travis.yml +6 -1
  5. data/CHANGELOG.md +32 -34
  6. data/README.md +68 -21
  7. data/Rakefile +25 -3
  8. data/TODO.md +18 -0
  9. data/UPGRADING.md +22 -0
  10. data/cfndsl.gemspec +3 -3
  11. data/exe/cfndsl +5 -0
  12. data/lib/cfndsl.rb +2 -116
  13. data/lib/cfndsl/aws/cloud_formation_template.rb +8 -1
  14. data/lib/cfndsl/aws/patches/000_sam.spec.json +574 -0
  15. data/lib/cfndsl/aws/patches/100_sam.spec_DeploymentPreference_patch.json +64 -0
  16. data/lib/cfndsl/aws/patches/500_Cognito_IdentityPoolRoleAttachment_patches.json +25 -0
  17. data/lib/cfndsl/aws/patches/500_IoT1Click_patch_PlacementTemplate_DeviceTemplates.json +20 -0
  18. data/lib/cfndsl/aws/patches/500_SAM_Serverless_Function_S3Event_Events_patch.json +16 -0
  19. data/lib/cfndsl/aws/patches/500_SAM_Serverless_Function_S3Location_Version_patch.json +16 -0
  20. data/lib/cfndsl/aws/patches/500_SSM_AssociationName_patch.json +16 -0
  21. data/lib/cfndsl/aws/patches/500_VPCEndpoint_patch.json +17 -0
  22. data/lib/cfndsl/aws/patches/510_ElasticSearch_Domain_patches.json +15 -0
  23. data/lib/cfndsl/aws/patches/600_RefKinds_patch.json +3654 -0
  24. data/lib/cfndsl/aws/patches/700_SAM_Serverless_Function_InlineCode_patch.json +20 -0
  25. data/lib/cfndsl/aws/patches/800_List_types_patch.json +115 -0
  26. data/lib/cfndsl/aws/resource_specification.json +35809 -11627
  27. data/lib/cfndsl/aws/types.rb +3 -3
  28. data/lib/cfndsl/cfnlego.rb +34 -0
  29. data/lib/{cfnlego → cfndsl/cfnlego}/cloudformation.erb +0 -0
  30. data/lib/{cfnlego → cfndsl/cfnlego}/cloudformation.rb +0 -0
  31. data/lib/{cfnlego → cfndsl/cfnlego}/resource.rb +3 -8
  32. data/lib/cfndsl/cloudformation.rb +107 -0
  33. data/lib/cfndsl/conditions.rb +11 -1
  34. data/lib/cfndsl/creation_policy.rb +1 -1
  35. data/lib/cfndsl/deep_merge.rb +4 -0
  36. data/lib/cfndsl/external_parameters.rb +4 -13
  37. data/lib/cfndsl/globals.rb +48 -9
  38. data/lib/cfndsl/jsonable.rb +22 -60
  39. data/lib/cfndsl/mappings.rb +1 -1
  40. data/lib/cfndsl/module.rb +16 -5
  41. data/lib/cfndsl/orchestration_template.rb +185 -83
  42. data/lib/cfndsl/outputs.rb +5 -1
  43. data/lib/cfndsl/parameters.rb +1 -1
  44. data/lib/cfndsl/plurals.rb +12 -1
  45. data/lib/cfndsl/properties.rb +1 -1
  46. data/lib/cfndsl/rake_task.rb +206 -12
  47. data/lib/cfndsl/ref_check.rb +19 -11
  48. data/lib/cfndsl/resources.rb +6 -19
  49. data/lib/cfndsl/rules.rb +1 -1
  50. data/lib/cfndsl/runner.rb +143 -0
  51. data/lib/cfndsl/specification.rb +80 -95
  52. data/lib/cfndsl/types.rb +205 -91
  53. data/lib/cfndsl/update_policy.rb +1 -1
  54. data/lib/cfndsl/version.rb +1 -1
  55. data/sample/autoscale.rb +0 -1
  56. data/sample/autoscale2.rb +0 -1
  57. data/sample/config_service.rb +2 -2
  58. data/sample/t1.rb +1 -1
  59. data/sample/vpc_example.rb +1 -1
  60. data/sample/vpc_with_vpn_example.rb +1 -1
  61. data/spec/aws/list_type_patches_spec.rb +35 -0
  62. data/spec/aws/nested_arrays_spec.rb +155 -3
  63. data/spec/aws/serverless_spec.rb +0 -2
  64. data/spec/cfndsl_spec.rb +94 -78
  65. data/spec/cli_spec.rb +16 -54
  66. data/spec/cloud_formation_template_spec.rb +233 -0
  67. data/spec/condition_spec.rb +24 -0
  68. data/spec/direct_ruby_spec.rb +19 -0
  69. data/spec/external_parameters_spec.rb +2 -15
  70. data/spec/fixtures/condition-assertion.json +1 -0
  71. data/spec/fixtures/test.rb +2 -1
  72. data/spec/generate_spec.rb +4 -2
  73. data/spec/resources_spec.rb +0 -7
  74. data/spec/spec_helper.rb +2 -7
  75. data/spec/support/shared_examples/orchestration_template.rb +15 -2
  76. data/spec/types_definition_spec.rb +3 -6
  77. metadata +52 -23
  78. data/bin/cfndsl +0 -160
  79. data/lib/cfndsl/errors.rb +0 -31
  80. data/lib/cfndsl/os/heat_template.rb +0 -18
  81. data/lib/cfndsl/os/types.rb +0 -14
  82. data/lib/cfndsl/os/types.yaml +0 -2423
  83. data/lib/cfndsl/patches.rb +0 -226
  84. data/lib/cfnlego.rb +0 -44
  85. data/spec/fixtures/heattest.rb +0 -24
  86. data/spec/heat_template_spec.rb +0 -7
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cfndsl/jsonable'
3
+ require_relative 'jsonable'
4
4
 
5
5
  module CfnDsl
6
6
  # Handles autoscaling group update policy objects for Resources
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CfnDsl
4
- VERSION = '0.17.5'
4
+ VERSION = '1.0.0.pre.1'
5
5
  end
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.
@@ -12,7 +12,7 @@ CloudFormation do
12
12
  Bucket('ConfigServiceBucket') do
13
13
  end
14
14
 
15
- Policy('ConfigServiceS3BucketAccessPolicy') do
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
- Policy('ConfigServiceSNSTopicAccessPolicy') do
71
+ IAM_Policy('ConfigServiceSNSTopicAccessPolicy') do
72
72
  PolicyName 'ConfigServiceSNSTopicAccessPolicy'
73
73
  PolicyDocument(
74
74
  'Version' => '2012-10-17',
data/sample/t1.rb CHANGED
@@ -10,7 +10,7 @@ CloudFormation do
10
10
  name = "machine#{i}"
11
11
  EC2_Instance(name) do
12
12
  ImageId 'ami-12345678'
13
- Type 't1.micro'
13
+ InstanceType 't1.micro'
14
14
  end
15
15
  end
16
16
  end
@@ -42,7 +42,7 @@ CloudFormation do
42
42
  RouteTableId Ref(route_table)
43
43
  end
44
44
 
45
- Route(subnet + 'GatewayRoute') do
45
+ EC2_Route(subnet + 'GatewayRoute') do
46
46
  DependsOn :GatewayToInternet
47
47
  RouteTableId Ref(route_table)
48
48
  DestinationCidrBlock '0.0.0.0/0'
@@ -60,7 +60,7 @@ CloudFormation do
60
60
  RouteTableId Ref(route_table)
61
61
  end
62
62
 
63
- Route(subnet + 'GatewayRoute') do
63
+ EC2_Route(subnet + 'GatewayRoute') do
64
64
  DependsOn :GatewayToInternet
65
65
  RouteTableId Ref(route_table)
66
66
  DestinationCidrBlock '0.0.0.0/0'
@@ -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 '#Nested_Arrays' do
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 'check multiple invocations work' do
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 'check multiple invocation with arrays works' do
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
@@ -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 'validates references' do
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 have_key('r')
78
- expect(q_refs).to_not have_key('q')
79
- expect(r_refs).to have_key('q')
80
- expect(r_refs).to_not have_key('r')
81
- expect(subject.check_refs.length).to eq(2)
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.User 'TestUser'
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 have_key(param)
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 have_key('X')
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')