cfn-model 0.4.12 → 0.4.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1706430755f12f30280c2a2baebb5566d50898ede81ec20e503ef91642e08b6
4
- data.tar.gz: 30075abf14b456b2ebed049d9af2b68cfee7be938f8bcf9b8936170e651f8eeb
3
+ metadata.gz: 82992d31bca207f0fdccac49ce35cd26042703ea19c9cccd6c84166c534fcb30
4
+ data.tar.gz: c73dfff8dc47c2112b3d7c31ed9ff9c88d00cb7e9519495383b54d924d4110e9
5
5
  SHA512:
6
- metadata.gz: a7223fce1ad56a488c37cfc27c0b35a2b5f0df76c4d3dc7b55ca2d46139b8f2f479e5ae981549ac91844d710cabf2519a73fbc1507426e0ee254976cf0a328d6
7
- data.tar.gz: a1e9d39a1f17aa3f0ed0238cb8abc098a2880f3873ff3270ff9f2e6dc6c8dbdc017c96e3c0e6ca30531de5be8adf7a3494cd97b843476a8300ebda657fea977a
6
+ metadata.gz: 0de79a70b411c0655e60e0fbc61454b4fd8b662a132a66ec56512c5ce9dce7a338c3994dd8df1fa988bc281defed281e0300197b23383034bb147b05e8dec399
7
+ data.tar.gz: 02a845a5e38a29d9d097ec1a31fdd6be34ce28760150c381e7070776fb864864a70659b58ed409eacb1b02185ec17d8debdfe00d18348c0f9da2b20e9c1f8d47
@@ -3,7 +3,7 @@
3
3
  require_relative 'references'
4
4
 
5
5
  class CfnModel
6
- attr_reader :resources, :parameters, :line_numbers
6
+ attr_reader :resources, :parameters, :line_numbers, :conditions
7
7
 
8
8
  ##
9
9
  # if you really want it, here it is - the raw Hash from YAML.load. you'll have to mess with structural nits of
@@ -14,6 +14,7 @@ class CfnModel
14
14
  def initialize
15
15
  @parameters = {}
16
16
  @resources = {}
17
+ @conditions = {}
17
18
  @raw_model = nil
18
19
  @line_numbers = {}
19
20
  end
@@ -24,6 +25,9 @@ class CfnModel
24
25
  # the Hash is a clone
25
26
  def copy
26
27
  new_cfn_model = CfnModel.new
28
+ @conditions.each do |k,v|
29
+ new_cfn_model.conditions[k] = v
30
+ end
27
31
  @parameters.each do |k,v|
28
32
  new_cfn_model.parameters[k] = v
29
33
  end
@@ -38,8 +38,8 @@ class CfnParser
38
38
  ##
39
39
  # Given raw json/yml CloudFormation template, returns a CfnModel object
40
40
  # or raise ParserErrors if something is amiss with the format
41
- def parse(cloudformation_yml, parameter_values_json=nil, with_line_numbers=false)
42
- cfn_model = parse_without_parameters(cloudformation_yml, with_line_numbers)
41
+ def parse(cloudformation_yml, parameter_values_json=nil, with_line_numbers=false, condition_values_json=nil)
42
+ cfn_model = parse_without_parameters(cloudformation_yml, with_line_numbers, condition_values_json)
43
43
 
44
44
  apply_parameter_values(cfn_model, parameter_values_json)
45
45
 
@@ -54,7 +54,7 @@ class CfnParser
54
54
  ToRubyWithLineNumbers.create.accept(handler.root).first
55
55
  end
56
56
 
57
- def parse_without_parameters(cloudformation_yml, with_line_numbers=false)
57
+ def parse_without_parameters(cloudformation_yml, with_line_numbers=false, condition_values_json=nil)
58
58
  pre_validate_model cloudformation_yml
59
59
 
60
60
  cfn_hash =
@@ -73,6 +73,8 @@ class CfnParser
73
73
  cfn_model = CfnModel.new
74
74
  cfn_model.raw_model = cfn_hash
75
75
 
76
+ process_conditions cfn_hash, cfn_model, condition_values_json
77
+
76
78
  # pass 1: wire properties into ModelElement objects
77
79
  if with_line_numbers
78
80
  transform_hash_into_model_elements_with_numbers cfn_hash, cfn_model
@@ -89,6 +91,24 @@ class CfnParser
89
91
 
90
92
  private
91
93
 
94
+ def process_conditions(cfn_hash, cfn_model, condition_values_json)
95
+ if cfn_hash.key?('Conditions')
96
+ if condition_values_json.nil?
97
+ condition_values = {}
98
+ else
99
+ condition_values = JSON.load condition_values_json
100
+ end
101
+
102
+ cfn_hash['Conditions'].each do |condition_key, _|
103
+ if condition_values.key?(condition_key) && [true, false].include?(condition_values[condition_key])
104
+ cfn_model.conditions[condition_key] = condition_values[condition_key]
105
+ else
106
+ cfn_model.conditions[condition_key] = true
107
+ end
108
+ end
109
+ end
110
+ end
111
+
92
112
  def apply_parameter_values(cfn_model, parameter_values_json)
93
113
  ParameterSubstitution.new.apply_parameter_values(
94
114
  cfn_model,
@@ -119,7 +139,7 @@ class CfnParser
119
139
  resource_object.resource_type = resource['Type']
120
140
  resource_object.metadata = resource['Metadata']
121
141
 
122
- assign_fields_based_upon_properties resource_object, resource
142
+ assign_fields_based_upon_properties resource_object, resource, cfn_model
123
143
 
124
144
  cfn_model.resources[resource_name] = resource_object
125
145
  end
@@ -135,7 +155,7 @@ class CfnParser
135
155
  resource_object.resource_type = resource['Type']['value']
136
156
  resource_object.metadata = resource['Metadata']
137
157
 
138
- assign_fields_based_upon_properties resource_object, resource
158
+ assign_fields_based_upon_properties resource_object, resource, cfn_model
139
159
 
140
160
  cfn_model.resources[resource_name] = resource_object
141
161
  cfn_model.line_numbers[resource_name] = resource['Type']['line']
@@ -175,11 +195,31 @@ class CfnParser
175
195
  end
176
196
  end
177
197
 
178
- def assign_fields_based_upon_properties(resource_object, resource)
198
+ def deal_with_conditional_property_definitions(resource, cfn_model)
199
+ all_extra_concrete_properties = []
200
+ resource['Properties'].each do |property_name, property_value|
201
+ next if %w(Fn::Transform).include? property_name
202
+ if property_name == 'Fn::If'
203
+ concrete_properties = ExpressionEvaluator.new.evaluate(
204
+ {'Fn::If'=>property_value},
205
+ cfn_model.conditions
206
+ )
207
+ all_extra_concrete_properties << concrete_properties
208
+ end
209
+ end
210
+ all_extra_concrete_properties.each do |extra_concrete_properties|
211
+ resource['Properties'].merge!(extra_concrete_properties)
212
+ end
213
+ resource['Properties'].delete('Fn::If')
214
+ end
215
+
216
+ def assign_fields_based_upon_properties(resource_object, resource, cfn_model)
179
217
  unless resource['Properties'].nil?
218
+ deal_with_conditional_property_definitions(resource, cfn_model)
219
+
180
220
  resource['Properties'].each do |property_name, property_value|
181
- next if %w(Fn::Transform Fn::If).include? property_name
182
- resource_object.send("#{map_property_name_to_attribute(property_name)}=", property_value)
221
+ next if %w(Fn::Transform).include? property_name
222
+ resource_object.send("#{map_property_name_to_attribute(property_name)}=", map_property_value(property_value, cfn_model))
183
223
  end
184
224
  end
185
225
  end
@@ -194,6 +234,10 @@ class CfnParser
194
234
  resource_class
195
235
  end
196
236
 
237
+ def map_property_value(property_value, cfn_model)
238
+ ExpressionEvaluator.new.evaluate(property_value, cfn_model.conditions)
239
+ end
240
+
197
241
  def map_property_name_to_attribute(str)
198
242
  (str.slice(0).downcase + str[1..(str.length)]).gsub /-/, '_'
199
243
  end
@@ -0,0 +1,39 @@
1
+ class ExpressionEvaluator
2
+ FN_IF = 'Fn::If'
3
+
4
+ ##
5
+ # {'Fn::If'=>[Condition,X,Y]} returns X if conditions doesn't include Condition, otherwise it return X or Y depending
6
+ #
7
+ # Other than Fn::If, it just returns the value itself
8
+ def evaluate(expression, conditions)
9
+ if if_condition?(expression)
10
+ outcome(expression, conditions)
11
+ else
12
+ expression
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def outcome(expression, conditions)
19
+ if if_condition?(expression)
20
+ if_expression = expression[FN_IF]
21
+ condition_name = if_expression[0]
22
+ if conditions[condition_name]
23
+ outcome(if_expression[1], conditions)
24
+ else
25
+ outcome(if_expression[2], conditions)
26
+ end
27
+ elsif expression.is_a?(Hash) # plain dict
28
+ expression.each do |k,v|
29
+ expression[k] = outcome(v, conditions)
30
+ end
31
+ else
32
+ expression
33
+ end
34
+ end
35
+
36
+ def if_condition?(property_value)
37
+ property_value.is_a?(Hash) && property_value.key?(FN_IF) && property_value.size == 1
38
+ end
39
+ end
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.12
4
+ version: 0.4.13
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-01-13 00:00:00.000000000 Z
11
+ date: 2020-01-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -89,6 +89,7 @@ files:
89
89
  - lib/cfn-model/parser/cfn_parser.rb
90
90
  - lib/cfn-model/parser/ec2_instance_parser.rb
91
91
  - lib/cfn-model/parser/ec2_network_interface_parser.rb
92
+ - lib/cfn-model/parser/expression_evaluator.rb
92
93
  - lib/cfn-model/parser/iam_group_parser.rb
93
94
  - lib/cfn-model/parser/iam_role_parser.rb
94
95
  - lib/cfn-model/parser/iam_user_parser.rb