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/lib/cfndsl/mappings.rb
CHANGED
data/lib/cfndsl/module.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'plurals'
|
4
|
+
require_relative 'names'
|
3
5
|
|
4
6
|
# Adds some dsl module helpers
|
5
7
|
class Module
|
@@ -51,6 +53,7 @@ class Module
|
|
51
53
|
# on the main object, and the block is then evaluated in the context
|
52
54
|
# of the new object.
|
53
55
|
#
|
56
|
+
# rubocop:disable Metrics/MethodLength
|
54
57
|
def dsl_content_object(*symbols)
|
55
58
|
symbols.each do |symbol|
|
56
59
|
plural = CfnDsl::Plurals.pluralize(symbol) # @@plurals[symbol] || "#{symbol}s"
|
@@ -65,12 +68,22 @@ class Module
|
|
65
68
|
hash = {}
|
66
69
|
instance_variable_set(pluralvar, hash)
|
67
70
|
end
|
68
|
-
hash[name]
|
69
|
-
|
70
|
-
|
71
|
+
instance = hash[name]
|
72
|
+
|
73
|
+
if !instance
|
74
|
+
instance = definition_class.new(*values)
|
75
|
+
hash[name] = instance
|
76
|
+
elsif !instance.is_a?(definition_class)
|
77
|
+
raise ArgumentError, "#{method}(#{name}) already exists and is not a #{definition_class}"
|
78
|
+
elsif !values.empty?
|
79
|
+
raise ArgumentError, "wrong number of arguments (given #{values.size + 1}, expected 1) as #{method}(#{name}) already exists"
|
80
|
+
end
|
81
|
+
instance.instance_eval(&block) if block
|
82
|
+
return instance
|
71
83
|
end
|
72
84
|
end
|
73
85
|
end
|
74
86
|
end
|
87
|
+
# rubocop:enable Metrics/MethodLength
|
75
88
|
end
|
76
89
|
end
|
data/lib/cfndsl/names.rb
CHANGED
@@ -1,17 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'globals'
|
4
|
+
require_relative 'module'
|
5
|
+
require_relative 'jsonable'
|
6
|
+
require_relative 'names'
|
7
|
+
require_relative 'plurals'
|
8
|
+
require_relative 'ref_check'
|
9
|
+
require_relative 'properties'
|
10
|
+
require_relative 'update_policy'
|
11
|
+
require_relative 'creation_policy'
|
12
|
+
require_relative 'conditions'
|
13
|
+
require_relative 'mappings'
|
14
|
+
require_relative 'resources'
|
15
|
+
require_relative 'rules'
|
16
|
+
require_relative 'parameters'
|
17
|
+
require_relative 'outputs'
|
18
|
+
|
19
|
+
require 'tsort'
|
6
20
|
|
7
21
|
module CfnDsl
|
8
22
|
# Handles the overall template object
|
9
23
|
# rubocop:disable Metrics/ClassLength
|
10
24
|
class OrchestrationTemplate < JSONable
|
11
25
|
dsl_attr_setter :AWSTemplateFormatVersion, :Description, :Metadata, :Transform
|
12
|
-
dsl_content_object :Condition, :Parameter, :Output, :Resource, :Mapping
|
26
|
+
dsl_content_object :Condition, :Parameter, :Output, :Resource, :Mapping, :Rule
|
13
27
|
|
14
|
-
|
28
|
+
GLOBAL_REFS = {
|
15
29
|
'AWS::NotificationARNs' => 1,
|
16
30
|
'AWS::Region' => 1,
|
17
31
|
'AWS::StackId' => 1,
|
@@ -29,9 +43,10 @@ module CfnDsl
|
|
29
43
|
parts = resource.split('::')
|
30
44
|
until parts.empty?
|
31
45
|
break if CfnDsl.reserved_items.include? parts.first
|
46
|
+
|
32
47
|
abreve_name = parts.join('_')
|
33
48
|
if accessors.key? abreve_name
|
34
|
-
accessors
|
49
|
+
accessors[abreve_name] = :duplicate # Delete potentially ambiguous names
|
35
50
|
else
|
36
51
|
accessors[abreve_name] = type_module.const_get resource_name
|
37
52
|
types_mapping[abreve_name] = resource
|
@@ -39,17 +54,25 @@ module CfnDsl
|
|
39
54
|
parts.shift
|
40
55
|
end
|
41
56
|
end
|
42
|
-
accessors.each_pair { |acc, res| create_resource_accessor(acc, res, types_mapping[acc]) }
|
57
|
+
accessors.each_pair { |acc, res| create_resource_accessor(acc, res, types_mapping[acc]) unless res == :duplicate }
|
43
58
|
end
|
44
59
|
|
45
60
|
def create_resource_def(name, info)
|
46
|
-
resource = Class.new ResourceDefinition
|
61
|
+
resource = Class.new ResourceDefinition do
|
62
|
+
# do not allow Type to be respecified
|
63
|
+
def Type(type = nil)
|
64
|
+
return @Type unless type
|
65
|
+
raise CfnDsl::Error, "Cannot override previously defined Type #{@Type} with #{type}" unless type == @Type
|
66
|
+
|
67
|
+
super
|
68
|
+
end
|
69
|
+
end
|
47
70
|
resource_name = name.gsub(/::/, '_')
|
48
71
|
type_module.const_set(resource_name, resource)
|
49
72
|
info['Properties'].each_pair do |pname, ptype|
|
50
73
|
if ptype.is_a? Array
|
51
74
|
pclass = type_module.const_get ptype.first
|
52
|
-
create_array_property_def(resource, pname, pclass)
|
75
|
+
create_array_property_def(resource, pname, pclass, info)
|
53
76
|
else
|
54
77
|
pclass = type_module.const_get ptype
|
55
78
|
create_property_def(resource, pname, pclass)
|
@@ -58,9 +81,66 @@ module CfnDsl
|
|
58
81
|
resource_name
|
59
82
|
end
|
60
83
|
|
61
|
-
|
84
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
85
|
+
def create_array_property_def(resource, pname, pclass, info)
|
86
|
+
singular_name = CfnDsl::Plurals.singularize pname
|
87
|
+
plural_name = singular_name == pname ? CfnDsl::Plurals.pluralize(pname) : pname
|
88
|
+
|
89
|
+
if singular_name == plural_name
|
90
|
+
# Generate the extended list concat method
|
91
|
+
plural_name = nil
|
92
|
+
elsif pname == plural_name && info['Properties'].include?(singular_name)
|
93
|
+
# The singlular name is a different property, do not redefine it here but rather use the extended form
|
94
|
+
# with the plural name. This allows construction of deep types, but no mechanism to overwrite a previous value
|
95
|
+
# (eg CodePipeline::Pipeline ArtifactStores vs ArtifactStore)
|
96
|
+
# Note is is also possible (but unlikely) for the spec to change in a way that triggers this condition where it did not
|
97
|
+
# before which will result in breaking behaviour for existing apps.
|
98
|
+
singular_name = plural_name
|
99
|
+
plural_name = nil
|
100
|
+
elsif pname == singular_name && info['Properties'].include?(plural_name)
|
101
|
+
# The plural name is a different property, do not redefine it here
|
102
|
+
# Note it is unlikely that a singular form is going to be a List property if the plural form also exists.
|
103
|
+
plural_name = singular_name
|
104
|
+
end
|
105
|
+
|
106
|
+
# Plural form just a normal property definition expecting an Array type
|
107
|
+
create_property_def(resource, pname, Array, plural_name) if plural_name
|
108
|
+
|
109
|
+
# Singular form understands concatenation and Fn::If property
|
110
|
+
create_singular_property_def(resource, pname, pclass, singular_name) if singular_name
|
111
|
+
end
|
112
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
113
|
+
|
114
|
+
def create_resource_accessor(accessor, resource, type)
|
115
|
+
class_eval do
|
116
|
+
CfnDsl.method_names(accessor) do |method|
|
117
|
+
define_method(method) do |name, *values, &block|
|
118
|
+
name = name.to_s
|
119
|
+
@Resources ||= {}
|
120
|
+
instance = @Resources[name]
|
121
|
+
if !instance
|
122
|
+
instance = resource.new(*values)
|
123
|
+
# Previously the type was set after the block was evaled
|
124
|
+
# But now trying to reset Type on a specific subtype will raise exception
|
125
|
+
instance.instance_variable_set('@Type', type)
|
126
|
+
@Resources[name] = instance
|
127
|
+
elsif type != (other_type = instance.instance_variable_get('@Type'))
|
128
|
+
raise ArgumentError, "Resource #{name}<#{other_type}> exists, and is not a <#{type}>"
|
129
|
+
elsif !values.empty?
|
130
|
+
raise ArgumentError, "wrong number of arguments (given #{values.size + 1}, expected 1) as Resource #{name} already exists"
|
131
|
+
end
|
132
|
+
@Resources[name].instance_eval(&block) if block
|
133
|
+
instance
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def create_property_def(resource, pname, pclass, method_name = pname)
|
62
142
|
resource.class_eval do
|
63
|
-
CfnDsl.method_names(
|
143
|
+
CfnDsl.method_names(method_name) do |method|
|
64
144
|
define_method(method) do |*values, &block|
|
65
145
|
values.push pclass.new if values.empty?
|
66
146
|
@Properties ||= {}
|
@@ -72,99 +152,139 @@ module CfnDsl
|
|
72
152
|
end
|
73
153
|
end
|
74
154
|
|
75
|
-
def
|
76
|
-
create_property_def(resource, pname, Array)
|
77
|
-
|
78
|
-
sname = CfnDsl::Plurals.singularize pname
|
79
|
-
|
80
|
-
return if sname == pname
|
155
|
+
def create_singular_property_def(resource, pname, pclass, singular_name)
|
81
156
|
resource.class_eval do
|
82
|
-
CfnDsl.method_names(
|
83
|
-
define_method(method) do |value = nil, &block|
|
157
|
+
CfnDsl.method_names(singular_name) do |method|
|
158
|
+
define_method(method) do |value = nil, fn_if: nil, **hash_value, &block|
|
159
|
+
value = hash_value unless value || hash_value.empty?
|
84
160
|
@Properties ||= {}
|
85
161
|
@Properties[pname] ||= PropertyDefinition.new([])
|
86
|
-
value
|
87
|
-
|
88
|
-
|
162
|
+
if value.is_a?(Array)
|
163
|
+
@Properties[pname].value.concat(value)
|
164
|
+
else
|
165
|
+
value ||= pclass.new
|
166
|
+
@Properties[pname].value.push fn_if ? FnIf(fn_if, value, Ref('AWS::NoValue')) : value
|
167
|
+
value.instance_eval(&block) if block
|
168
|
+
end
|
89
169
|
value
|
90
170
|
end
|
91
171
|
end
|
92
172
|
end
|
93
173
|
end
|
174
|
+
end
|
94
175
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
176
|
+
def initialize(description = nil, &block)
|
177
|
+
@AWSTemplateFormatVersion = '2010-09-09'
|
178
|
+
@Description = description if description
|
179
|
+
declare(&block) if block_given?
|
180
|
+
end
|
181
|
+
|
182
|
+
alias _Condition Condition
|
183
|
+
|
184
|
+
# Condition has two usages at this level
|
185
|
+
# @overload Condition(name,expression)
|
186
|
+
# @overload Condition(name) - referencing a condition in a condition expression
|
187
|
+
def Condition(name, expression = nil)
|
188
|
+
if expression
|
189
|
+
_Condition(name, expression)
|
190
|
+
else
|
191
|
+
{ Condition: ConditionDefinition.new(name) }
|
108
192
|
end
|
109
193
|
end
|
110
194
|
|
111
|
-
def
|
112
|
-
|
195
|
+
def check_refs
|
196
|
+
invalids = check_condition_refs + check_resource_refs + check_output_refs + check_rule_refs
|
197
|
+
invalids unless invalids.empty?
|
113
198
|
end
|
114
199
|
|
115
|
-
|
116
|
-
def valid_ref?(ref, origin = nil)
|
200
|
+
def valid_ref?(ref, ref_containers = [GLOBAL_REFS, @Resources, @Parameters])
|
117
201
|
ref = ref.to_s
|
118
|
-
|
202
|
+
ref_containers.any? { |c| c && c.key?(ref) }
|
203
|
+
end
|
119
204
|
|
120
|
-
|
205
|
+
def check_condition_refs
|
206
|
+
invalids = []
|
121
207
|
|
122
|
-
|
208
|
+
# Conditions can refer to other conditions in Fn::And, Fn::Or and Fn::Not
|
209
|
+
invalids.concat(_check_refs(:Condition, :condition_refs, [@Conditions]))
|
123
210
|
|
124
|
-
|
125
|
-
|
126
|
-
|
211
|
+
# They can also Ref Globals and Parameters (but not Resources))
|
212
|
+
invalids.concat(_check_refs(:Condition, :all_refs, [GLOBAL_REFS, @Parameters]))
|
213
|
+
end
|
214
|
+
|
215
|
+
def check_resource_refs
|
216
|
+
invalids = []
|
217
|
+
invalids.concat(_check_refs(:Resource, :all_refs, [@Resources, GLOBAL_REFS, @Parameters]))
|
127
218
|
|
128
|
-
|
219
|
+
# DependsOn and conditions in Fn::If expressions
|
220
|
+
invalids.concat(_check_refs(:Resource, :condition_refs, [@Conditions]))
|
129
221
|
end
|
130
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
131
222
|
|
132
|
-
def
|
133
|
-
invalids =
|
134
|
-
invalids
|
223
|
+
def check_output_refs
|
224
|
+
invalids = []
|
225
|
+
invalids.concat(_check_refs(:Output, :all_refs, [@Resources, GLOBAL_REFS, @Parameters]))
|
226
|
+
invalids.concat(_check_refs(:Output, :condition_refs, [@Conditions]))
|
135
227
|
end
|
136
228
|
|
137
|
-
def
|
229
|
+
def check_rule_refs
|
138
230
|
invalids = []
|
139
|
-
@
|
140
|
-
|
141
|
-
@Resources.keys.each do |resource|
|
142
|
-
@_resource_refs[resource.to_s] = @Resources[resource].build_references({})
|
143
|
-
end
|
144
|
-
@_resource_refs.keys.each do |origin|
|
145
|
-
@_resource_refs[origin].keys.each do |ref|
|
146
|
-
invalids.push "Invalid Reference: Resource #{origin} refers to #{ref}" unless valid_ref?(ref, origin)
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
231
|
+
invalids.concat(_check_refs(:Rule, :all_refs, [@Resources, GLOBAL_REFS, @Parameters]))
|
232
|
+
invalids.concat(_check_refs(:Rule, :condition_refs, [@Conditions]))
|
150
233
|
invalids
|
151
234
|
end
|
152
235
|
|
153
|
-
|
236
|
+
# For testing for cycles
|
237
|
+
class RefHash < Hash
|
238
|
+
include TSort
|
239
|
+
|
240
|
+
alias tsort_each_node each_key
|
241
|
+
def tsort_each_child(node, &block)
|
242
|
+
fetch(node, []).each(&block)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
247
|
+
def _check_refs(container_name, method, source_containers)
|
248
|
+
container = instance_variable_get("@#{container_name}s")
|
249
|
+
return [] unless container
|
250
|
+
|
154
251
|
invalids = []
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
252
|
+
referred_by = RefHash.new { |h, k| h[k] = [] }
|
253
|
+
self_check = source_containers.first.eql?(container)
|
254
|
+
|
255
|
+
container.each_pair do |name, entry|
|
256
|
+
name = name.to_s
|
257
|
+
begin
|
258
|
+
refs = entry.build_references([], self_check && name, method)
|
259
|
+
refs.each { |r| referred_by[r.to_s] << name }
|
260
|
+
rescue RefCheck::SelfReference, RefCheck::NullReference => e
|
261
|
+
# Topological sort will not detect self or null references
|
262
|
+
invalids.push("#{container_name} #{e.message}")
|
159
263
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
264
|
+
end
|
265
|
+
|
266
|
+
referred_by.each_pair do |ref, names|
|
267
|
+
unless valid_ref?(ref, source_containers)
|
268
|
+
invalids.push "Invalid Reference: #{container_name}s #{names} refer to unknown #{method == :condition_refs ? 'Condition' : 'Reference'} #{ref}"
|
164
269
|
end
|
165
270
|
end
|
271
|
+
|
272
|
+
begin
|
273
|
+
referred_by.tsort if self_check && invalids.empty? # Check for cycles
|
274
|
+
rescue TSort::Cyclic => e
|
275
|
+
invalids.push "Cyclic references found in #{container_name}s #{referred_by} - #{e.message}"
|
276
|
+
end
|
277
|
+
|
166
278
|
invalids
|
167
279
|
end
|
280
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
281
|
+
|
282
|
+
def validate
|
283
|
+
errors = check_refs || []
|
284
|
+
raise CfnDsl::Error, "#{errors.size} errors in template\n#{errors.join("\n")}" unless errors.empty?
|
285
|
+
|
286
|
+
self
|
287
|
+
end
|
168
288
|
end
|
169
289
|
# rubocop:enable Metrics/ClassLength
|
170
290
|
end
|
data/lib/cfndsl/outputs.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'jsonable'
|
2
4
|
|
3
5
|
module CfnDsl
|
4
6
|
# Handles Output objects
|
@@ -12,5 +14,9 @@ module CfnDsl
|
|
12
14
|
def initialize(value = nil)
|
13
15
|
@Value = value if value
|
14
16
|
end
|
17
|
+
|
18
|
+
def condition_refs
|
19
|
+
[@Condition].flatten.compact.map(&:to_s)
|
20
|
+
end
|
15
21
|
end
|
16
22
|
end
|
data/lib/cfndsl/parameters.rb
CHANGED
data/lib/cfndsl/plurals.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CfnDsl
|
2
4
|
# Plural names for lists of content objects
|
3
5
|
module Plurals
|
@@ -5,15 +7,15 @@ module CfnDsl
|
|
5
7
|
|
6
8
|
@plurals = {
|
7
9
|
'AssumeRolePolicyDocument' => 'AssumeRolePolicyDocument',
|
8
|
-
'CreationPolicy'
|
9
|
-
'DBSecurityGroupIngress'
|
10
|
-
'Metadata'
|
11
|
-
'Policy'
|
12
|
-
'PolicyDocument'
|
13
|
-
'Property'
|
14
|
-
'SecurityGroupEgress'
|
15
|
-
'SecurityGroupIngress'
|
16
|
-
'UpdatePolicy'
|
10
|
+
'CreationPolicy' => 'CreationPolicy',
|
11
|
+
'DBSecurityGroupIngress' => 'DBSecurityGroupIngress',
|
12
|
+
'Metadata' => 'Metadata',
|
13
|
+
'Policy' => 'Policies',
|
14
|
+
'PolicyDocument' => 'PolicyDocument',
|
15
|
+
'Property' => 'Properties',
|
16
|
+
'SecurityGroupEgress' => 'SecurityGroupEgress',
|
17
|
+
'SecurityGroupIngress' => 'SecurityGroupIngress',
|
18
|
+
'UpdatePolicy' => 'UpdatePolicy'
|
17
19
|
}
|
18
20
|
@singles = @plurals.invert
|
19
21
|
|
@@ -22,7 +24,18 @@ module CfnDsl
|
|
22
24
|
end
|
23
25
|
|
24
26
|
def singularize(name)
|
25
|
-
@singles.fetch(name.to_s)
|
27
|
+
@singles.fetch(name.to_s) do |key|
|
28
|
+
case key
|
29
|
+
when /List$/
|
30
|
+
key[0..-5]
|
31
|
+
when /ies$/
|
32
|
+
key[0..-4] + 'y'
|
33
|
+
when /s$/
|
34
|
+
key[0..-2]
|
35
|
+
else
|
36
|
+
key
|
37
|
+
end
|
38
|
+
end
|
26
39
|
end
|
27
40
|
end
|
28
41
|
end
|