cfn-model 0.4.29 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/cfn-model/model/references.rb +136 -2
- data/lib/cfn-model/parser/cfn_parser.rb +7 -4
- data/lib/cfn-model/parser/iam_group_parser.rb +3 -2
- data/lib/cfn-model/parser/iam_role_parser.rb +4 -3
- data/lib/cfn-model/parser/iam_user_parser.rb +5 -4
- data/lib/cfn-model/parser/kms_key_parser.rb +1 -1
- data/lib/cfn-model/parser/parameter_substitution.rb +2 -1
- data/lib/cfn-model/parser/policy_document_parser.rb +15 -13
- data/lib/cfn-model/parser/security_group_parser.rb +2 -2
- data/lib/cfn-model/parser/with_policy_document_parser.rb +1 -1
- data/lib/cfn-model/transforms/serverless.rb +8 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0912463527f6c2239a20d71bd81ce4d7bd8915d44ffb4dd975d684bc57f36555'
|
4
|
+
data.tar.gz: e0943893a06458b5b49b3cc92a73c5570ebae24a1e5b5458b52e456043f401d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e56dff8cbc449c62012d268e470500ae4bd946cfb45b8292e86a6ff50375659f76caafe5c9043ccb3727a5d59ca51c90efb80407c3bc065aeb922ed5909b8900
|
7
|
+
data.tar.gz: 3547645ee5d45b27a7679c805f9f57492174245f15782a6d6abf59d7f67dec8f8ba22305ec31c3412bda6605cac728f6ea3bb75cc80affb0e52ac998acdcd259
|
@@ -9,16 +9,39 @@ require 'cfn-model/parser/parser_error'
|
|
9
9
|
# references yet... in the meantime pile things up here and hope a pattern becomes
|
10
10
|
# clear
|
11
11
|
module References
|
12
|
-
def self.
|
12
|
+
def self.unsupported_passthru?(value)
|
13
|
+
value.has_key?('Fn::GetAtt') || value.has_key?('Fn::ImportValue') || value.has_key?('Fn::Transform') || value.has_key?('Fn::Cidr')
|
14
|
+
end
|
13
15
|
|
16
|
+
def self.resolve_value(cfn_model, value)
|
14
17
|
if value.is_a? Hash
|
15
18
|
if value.has_key?('Ref')
|
16
19
|
resolve_reference(cfn_model, value)
|
17
20
|
elsif value.has_key?('Fn::FindInMap')
|
18
21
|
resolve_map(cfn_model, value)
|
19
|
-
|
22
|
+
elsif value.has_key?('Fn::If')
|
23
|
+
resolve_if(cfn_model, value)
|
24
|
+
elsif value.has_key?('Fn::Sub')
|
25
|
+
resolve_sub(cfn_model, value)
|
26
|
+
elsif value.has_key?('Fn::GetAZs')
|
27
|
+
resolve_getazs(cfn_model, value)
|
28
|
+
elsif value.has_key?('Fn::Split')
|
29
|
+
resolve_split(cfn_model, value)
|
30
|
+
elsif value.has_key?('Fn::Join')
|
31
|
+
resolve_join(cfn_model, value)
|
32
|
+
elsif value.has_key?('Fn::Base64')
|
33
|
+
resolve_base64(cfn_model, value)
|
34
|
+
elsif value.has_key?('Fn::Select')
|
35
|
+
resolve_select(cfn_model, value)
|
36
|
+
elsif unsupported_passthru?(value)
|
20
37
|
value
|
38
|
+
else # another mapping
|
39
|
+
value.map do |k,v|
|
40
|
+
[k, resolve_value(cfn_model, v)]
|
41
|
+
end.to_h
|
21
42
|
end
|
43
|
+
elsif value.is_a? Array
|
44
|
+
value.map { |item| resolve_value(cfn_model, item) }
|
22
45
|
else
|
23
46
|
value
|
24
47
|
end
|
@@ -96,6 +119,117 @@ module References
|
|
96
119
|
|
97
120
|
private
|
98
121
|
|
122
|
+
def self.resolve_sub(cfn_model, expression)
|
123
|
+
if expression['Fn::Sub'].is_a? String
|
124
|
+
resolve_shorthand_sub(cfn_model, expression)
|
125
|
+
elsif expression['Fn::Sub'].is_a? Array
|
126
|
+
resolve_longform_sub(cfn_model, expression)
|
127
|
+
else
|
128
|
+
expression
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.resolve_select(cfn_model, reference)
|
133
|
+
index = reference['Fn::Select'][0]
|
134
|
+
collection = References.resolve_value(cfn_model, reference['Fn::Select'][1])
|
135
|
+
if collection.is_a? Array
|
136
|
+
collection[index]
|
137
|
+
else
|
138
|
+
reference
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.resolve_base64(cfn_model, reference)
|
143
|
+
References.resolve_value(cfn_model, reference['Fn::Base64'])
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.resolve_join(cfn_model, reference)
|
147
|
+
delimiter = reference['Fn::Join'][0]
|
148
|
+
items = References.resolve_value(cfn_model, reference['Fn::Join'][1])
|
149
|
+
return reference unless items.is_a?(Array)
|
150
|
+
items.join(delimiter)
|
151
|
+
end
|
152
|
+
|
153
|
+
def self.resolve_split(cfn_model, reference)
|
154
|
+
delimiter = reference['Fn::Split'][0]
|
155
|
+
target_string = References.resolve_value(cfn_model, reference['Fn::Split'][1])
|
156
|
+
return reference unless target_string.is_a?(String)
|
157
|
+
target_string.split(delimiter)
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.resolve_getazs(cfn_model, reference)
|
161
|
+
number_azs = References.resolve_value(cfn_model, { 'Ref' => 'AWS::NumberAZs' })
|
162
|
+
region = reference['Fn::GetAZs']
|
163
|
+
if region == '' || region == { 'Ref' => 'AWS::Region' }
|
164
|
+
region = References.resolve_value(cfn_model, { 'Ref' => 'AWS::Region' })
|
165
|
+
end
|
166
|
+
(('a'.ord)..('a'.ord+number_azs)).map do |az_number|
|
167
|
+
"#{region}#{az_number.chr}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.strip_cfn_interpolation(reference)
|
172
|
+
reference[2..-2]
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.references_in_sub(string_value)
|
176
|
+
# ignore ${!foo} as cfn interprets that as the literal ${foo}
|
177
|
+
references = string_value.scan /\${[^!].*?}/
|
178
|
+
references.map { |reference| strip_cfn_interpolation(reference) }
|
179
|
+
end
|
180
|
+
|
181
|
+
def self.resolvable_reference?(cfn_model, reference)
|
182
|
+
resolved_value = References.resolve_value(cfn_model, {'Ref'=>reference})
|
183
|
+
resolved_value != {'Ref'=>reference}
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.resolve_shorthand_sub(cfn_model, expression)
|
187
|
+
string_value = expression['Fn::Sub']
|
188
|
+
subbed_string_value = string_value
|
189
|
+
has_unresolved_references = false
|
190
|
+
references_in_sub(string_value).each do |reference|
|
191
|
+
if resolvable_reference?(cfn_model, reference)
|
192
|
+
subbed_string_value = subbed_string_value.gsub(
|
193
|
+
"${#{reference}}",
|
194
|
+
References.resolve_value(cfn_model, {'Ref'=>reference})
|
195
|
+
)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
subbed_string_value
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.resolve_longform_sub(cfn_model, expression)
|
202
|
+
array_value = expression['Fn::Sub']
|
203
|
+
subbed_string_value = array_value[0]
|
204
|
+
substitution_mapping = array_value[1]
|
205
|
+
references_in_sub(subbed_string_value).each do |reference|
|
206
|
+
if substitution_mapping.has_key? reference
|
207
|
+
if References.resolve_value(cfn_model, substitution_mapping[reference]).is_a?(String)
|
208
|
+
subbed_string_value = subbed_string_value.gsub(
|
209
|
+
"${#{reference}}",
|
210
|
+
References.resolve_value(cfn_model, substitution_mapping[reference])
|
211
|
+
)
|
212
|
+
end
|
213
|
+
elsif resolvable_reference?(cfn_model, reference)
|
214
|
+
subbed_string_value = subbed_string_value.gsub(
|
215
|
+
"${#{reference}}",
|
216
|
+
References.resolve_value(cfn_model, {'Ref'=>reference})
|
217
|
+
)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
subbed_string_value
|
221
|
+
end
|
222
|
+
|
223
|
+
def self.resolve_if(cfn_model, expression)
|
224
|
+
if_expression = expression['Fn::If']
|
225
|
+
condition_name = if_expression[0]
|
226
|
+
if cfn_model.conditions[condition_name]
|
227
|
+
resolve_value(cfn_model, if_expression[1])
|
228
|
+
else
|
229
|
+
resolve_value(cfn_model, if_expression[2])
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
99
233
|
def self.logical_resource_id_from_get_att(attribute_spec, attr_to_retrieve=nil)
|
100
234
|
if attribute_spec.is_a? Array
|
101
235
|
if !attr_to_retrieve || attribute_spec[1] == attr_to_retrieve
|
@@ -44,6 +44,9 @@ class CfnParser
|
|
44
44
|
|
45
45
|
apply_parameter_values(cfn_model, parameter_values_json)
|
46
46
|
|
47
|
+
# pass 2: tie together separate resources only where necessary to make life easier for rule logic
|
48
|
+
post_process_resource_model_elements cfn_model
|
49
|
+
|
47
50
|
cfn_model
|
48
51
|
end
|
49
52
|
|
@@ -87,8 +90,7 @@ class CfnParser
|
|
87
90
|
transform_hash_into_parameters cfn_hash, cfn_model
|
88
91
|
transform_hash_into_globals cfn_hash, cfn_model
|
89
92
|
|
90
|
-
|
91
|
-
post_process_resource_model_elements cfn_model
|
93
|
+
|
92
94
|
|
93
95
|
cfn_model
|
94
96
|
end
|
@@ -279,7 +281,7 @@ class CfnParser
|
|
279
281
|
|
280
282
|
def map_non_aws_resource_name_to_class_name(module_names)
|
281
283
|
# this is a little hacky. we've been ignoring Custom so more for
|
282
|
-
# backward compat.
|
284
|
+
# backward compat. for Alexa and other transformed resources just jam the whole
|
283
285
|
# thing together
|
284
286
|
if module_names.first == 'Custom'
|
285
287
|
first_module_index = 1
|
@@ -306,7 +308,8 @@ class CfnParser
|
|
306
308
|
else
|
307
309
|
custom_resource_class_name = map_non_aws_resource_name_to_class_name(module_names)
|
308
310
|
begin
|
309
|
-
|
311
|
+
custom_class = Object.const_get custom_resource_class_name
|
312
|
+
resource_class = custom_class if custom_class.is_a?(ModelElement)
|
310
313
|
rescue NameError
|
311
314
|
Object.const_set(custom_resource_class_name, resource_class)
|
312
315
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'cfn-model/model/iam_role'
|
4
4
|
require 'cfn-model/model/policy'
|
5
|
+
require 'cfn-model/model/references'
|
5
6
|
require_relative 'policy_document_parser'
|
6
7
|
|
7
8
|
class IamGroupParser
|
@@ -12,8 +13,8 @@ class IamGroupParser
|
|
12
13
|
next unless policy.has_key? 'PolicyName'
|
13
14
|
|
14
15
|
new_policy = Policy.new
|
15
|
-
new_policy.policy_name = policy['PolicyName']
|
16
|
-
new_policy.policy_document = PolicyDocumentParser.new.parse(policy['PolicyDocument'])
|
16
|
+
new_policy.policy_name = References.resolve_value(cfn_model, policy['PolicyName'])
|
17
|
+
new_policy.policy_document = PolicyDocumentParser.new.parse(cfn_model, policy['PolicyDocument'])
|
17
18
|
new_policy
|
18
19
|
end.reject { |policy| policy.nil? }
|
19
20
|
iam_group
|
@@ -2,20 +2,21 @@
|
|
2
2
|
|
3
3
|
require 'cfn-model/model/iam_role'
|
4
4
|
require 'cfn-model/model/policy'
|
5
|
+
require 'cfn-model/model/references'
|
5
6
|
require_relative 'policy_document_parser'
|
6
7
|
|
7
8
|
class IamRoleParser
|
8
9
|
def parse(cfn_model:, resource:)
|
9
10
|
iam_role = resource
|
10
11
|
|
11
|
-
iam_role.assume_role_policy_document = PolicyDocumentParser.new.parse(iam_role.assumeRolePolicyDocument)
|
12
|
+
iam_role.assume_role_policy_document = PolicyDocumentParser.new.parse(cfn_model, iam_role.assumeRolePolicyDocument)
|
12
13
|
|
13
14
|
iam_role.policy_objects = iam_role.policies.map do |policy|
|
14
15
|
next unless policy.has_key? 'PolicyName'
|
15
16
|
|
16
17
|
new_policy = Policy.new
|
17
|
-
new_policy.policy_name = policy['PolicyName']
|
18
|
-
new_policy.policy_document = PolicyDocumentParser.new.parse(policy['PolicyDocument'])
|
18
|
+
new_policy.policy_name = References.resolve_value(cfn_model, policy['PolicyName'])
|
19
|
+
new_policy.policy_document = PolicyDocumentParser.new.parse(cfn_model, policy['PolicyDocument'])
|
19
20
|
new_policy
|
20
21
|
end.reject { |policy| policy.nil? }
|
21
22
|
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'cfn-model/model/policy_document'
|
4
4
|
require 'cfn-model/model/policy'
|
5
|
+
require 'cfn-model/model/references'
|
5
6
|
require_relative 'policy_document_parser'
|
6
7
|
|
7
8
|
class IamUserParser
|
@@ -12,8 +13,8 @@ class IamUserParser
|
|
12
13
|
next unless policy.has_key? 'PolicyName'
|
13
14
|
|
14
15
|
new_policy = Policy.new
|
15
|
-
new_policy.policy_name = policy['PolicyName']
|
16
|
-
new_policy.policy_document = PolicyDocumentParser.new.parse(policy['PolicyDocument'])
|
16
|
+
new_policy.policy_name = References.resolve_value(cfn_model, policy['PolicyName'])
|
17
|
+
new_policy.policy_document = PolicyDocumentParser.new.parse(cfn_model, policy['PolicyDocument'])
|
17
18
|
new_policy
|
18
19
|
end.reject { |policy| policy.nil? }
|
19
20
|
|
@@ -22,8 +23,8 @@ class IamUserParser
|
|
22
23
|
user_to_group_additions = cfn_model.resources_by_type 'AWS::IAM::UserToGroupAddition'
|
23
24
|
user_to_group_additions.each do |user_to_group_addition|
|
24
25
|
|
25
|
-
if user_to_group_addition_has_username(user_to_group_addition.users,iam_user)
|
26
|
-
iam_user.group_names << user_to_group_addition.groupName
|
26
|
+
if user_to_group_addition_has_username(user_to_group_addition.users, iam_user)
|
27
|
+
iam_user.group_names << References.resolve_value(cfn_model, user_to_group_addition.groupName)
|
27
28
|
|
28
29
|
# we need to figure out the story on resolving Refs i think for this to be real
|
29
30
|
end
|
@@ -9,7 +9,7 @@ class KmsKeyParser
|
|
9
9
|
kms_key = resource
|
10
10
|
|
11
11
|
new_policy = Policy.new
|
12
|
-
new_policy.policy_document = PolicyDocumentParser.new.parse(kms_key.keyPolicy)
|
12
|
+
new_policy.policy_document = PolicyDocumentParser.new.parse(cfn_model, kms_key.keyPolicy)
|
13
13
|
kms_key.key_policy = new_policy
|
14
14
|
|
15
15
|
kms_key
|
@@ -43,7 +43,8 @@ class ParameterSubstitution
|
|
43
43
|
'AWS::AccountId' => '111111111111',
|
44
44
|
'AWS::Region' => 'us-east-1',
|
45
45
|
'AWS::StackId' => 'arn:aws:cloudformation:us-east-1:111111111111:stack/stackname/51af3dc0-da77-11e4-872e-1234567db123',
|
46
|
-
'AWS::StackName' => 'stackname'
|
46
|
+
'AWS::StackName' => 'stackname',
|
47
|
+
'AWS::NumberAZs' => 2
|
47
48
|
}
|
48
49
|
pseudo_function_defaults.each do |function_name, default_value|
|
49
50
|
parameter = Parameter.new
|
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'cfn-model/model/iam_policy'
|
4
|
+
require 'cfn-model/model/references'
|
5
|
+
|
4
6
|
require 'cfn-model/model/policy_document'
|
5
7
|
|
6
8
|
class PolicyDocumentParser
|
7
|
-
def parse(raw_policy_document)
|
9
|
+
def parse(cfn_model, raw_policy_document)
|
8
10
|
policy_document = PolicyDocument.new
|
9
11
|
|
10
|
-
policy_document.version = raw_policy_document['Version']
|
12
|
+
policy_document.version = References.resolve_value(cfn_model, raw_policy_document['Version'])
|
11
13
|
|
12
14
|
policy_document.statements = streamline_array(raw_policy_document['Statement']) do |statement|
|
13
|
-
parse_statement statement
|
15
|
+
parse_statement cfn_model, statement
|
14
16
|
end
|
15
17
|
|
16
18
|
policy_document
|
@@ -18,17 +20,17 @@ class PolicyDocumentParser
|
|
18
20
|
|
19
21
|
private
|
20
22
|
|
21
|
-
def parse_statement(raw_statement)
|
23
|
+
def parse_statement(cfn_model, raw_statement)
|
22
24
|
statement = Statement.new
|
23
|
-
statement.effect = raw_statement['Effect']
|
24
|
-
statement.sid = raw_statement['Sid']
|
25
|
-
statement.condition = raw_statement['Condition']
|
26
|
-
statement.actions = streamline_array(raw_statement['Action'])
|
27
|
-
statement.not_actions = streamline_array(raw_statement['NotAction'])
|
28
|
-
statement.resources = streamline_array(raw_statement['Resource'])
|
29
|
-
statement.not_resources = streamline_array(raw_statement['NotResource'])
|
30
|
-
statement.principal = raw_statement['Principal']
|
31
|
-
statement.not_principal = raw_statement['NotPrincipal']
|
25
|
+
statement.effect = References.resolve_value(cfn_model, raw_statement['Effect'])
|
26
|
+
statement.sid = References.resolve_value(cfn_model, raw_statement['Sid'])
|
27
|
+
statement.condition = References.resolve_value(cfn_model, raw_statement['Condition'])
|
28
|
+
statement.actions = References.resolve_value(cfn_model, streamline_array(raw_statement['Action']))
|
29
|
+
statement.not_actions = References.resolve_value(cfn_model, streamline_array(raw_statement['NotAction']))
|
30
|
+
statement.resources = References.resolve_value(cfn_model, streamline_array(raw_statement['Resource']))
|
31
|
+
statement.not_resources = References.resolve_value(cfn_model, streamline_array(raw_statement['NotResource']))
|
32
|
+
statement.principal = References.resolve_value(cfn_model, raw_statement['Principal'])
|
33
|
+
statement.not_principal = References.resolve_value(cfn_model, raw_statement['NotPrincipal'])
|
32
34
|
statement
|
33
35
|
end
|
34
36
|
|
@@ -38,7 +38,7 @@ class SecurityGroupParser
|
|
38
38
|
ingress_object = AWS::EC2::SecurityGroupIngress.new cfn_model
|
39
39
|
ingress.each do |k, v|
|
40
40
|
silently_fail do
|
41
|
-
ingress_object.send("#{initialLower(k)}=", v)
|
41
|
+
ingress_object.send("#{initialLower(k)}=", References.resolve_value(cfn_model, v))
|
42
42
|
mapped_at_least_one_attribute = true
|
43
43
|
end
|
44
44
|
end
|
@@ -59,7 +59,7 @@ class SecurityGroupParser
|
|
59
59
|
egress.each do |k, v|
|
60
60
|
next if k.match /::/
|
61
61
|
silently_fail do
|
62
|
-
egress_object.send("#{initialLower(k)}=", v)
|
62
|
+
egress_object.send("#{initialLower(k)}=", References.resolve_value(cfn_model, v))
|
63
63
|
mapped_at_least_one_attribute = true
|
64
64
|
end
|
65
65
|
|
@@ -6,7 +6,7 @@ require_relative 'policy_document_parser'
|
|
6
6
|
|
7
7
|
class WithPolicyDocumentParser
|
8
8
|
def parse(cfn_model:, resource:)
|
9
|
-
resource.policy_document = PolicyDocumentParser.new.parse(resource.policyDocument)
|
9
|
+
resource.policy_document = PolicyDocumentParser.new.parse(cfn_model, resource.policyDocument)
|
10
10
|
resource
|
11
11
|
end
|
12
12
|
end
|
@@ -112,8 +112,14 @@ class CfnModel
|
|
112
112
|
resource_name,
|
113
113
|
with_line_numbers)
|
114
114
|
|
115
|
-
cfn_hash['Resources'][resource_name] = lambda_function
|
116
|
-
|
115
|
+
cfn_hash['Resources'][resource_name] = lambda_function(
|
116
|
+
handler: lambda_fn_params[:handler],
|
117
|
+
code_bucket: lambda_fn_params[:code_bucket],
|
118
|
+
code_key: lambda_fn_params[:code_key],
|
119
|
+
role: lambda_fn_params[:role],
|
120
|
+
runtime: lambda_fn_params[:runtime],
|
121
|
+
with_line_numbers: lambda_fn_params[:with_line_numbers]
|
122
|
+
)
|
117
123
|
unless serverless_function['Properties']['Role']
|
118
124
|
cfn_hash['Resources'][resource_name + 'Role'] = function_role(serverless_function,
|
119
125
|
resource_name,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfn-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Kascic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -165,7 +165,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
167
|
requirements: []
|
168
|
-
rubygems_version: 3.1.
|
168
|
+
rubygems_version: 3.1.4
|
169
169
|
signing_key:
|
170
170
|
specification_version: 4
|
171
171
|
summary: cfn-model
|