cfndsl 1.2.0 → 1.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'version'
4
+ require 'fileutils'
4
5
 
5
6
  # Global variables to adjust CfnDsl behavior
6
7
  module CfnDsl
@@ -136,12 +136,13 @@ module CfnDsl
136
136
  check_names
137
137
  hash = {}
138
138
  instance_variables.each do |var|
139
- name = var[1..-1]
139
+ name = var[1..]
140
140
 
141
- if name =~ /^__/
141
+ case name
142
+ when /^__/
142
143
  # if a variable starts with double underscore, strip one off
143
- name = name[1..-1]
144
- elsif name =~ /^_/
144
+ name = name[1..]
145
+ when /^_/
145
146
  # Hide variables that start with single underscore
146
147
  name = nil
147
148
  end
@@ -72,18 +72,24 @@ module CfnDsl
72
72
  resource_name = name.gsub(/::/, '_')
73
73
  type_module.const_set(resource_name, resource)
74
74
  info['Properties'].each_pair do |pname, ptype|
75
+ # handle bogus List defined as Type
76
+ unless ptype.is_a?(Array)
77
+ pclass = type_module.const_get ptype
78
+ if pclass.is_a?(Array)
79
+ ptype = pclass
80
+ else
81
+ create_property_def(resource, pname, pclass)
82
+ end
83
+ end
84
+
75
85
  if ptype.is_a? Array
76
86
  pclass = type_module.const_get ptype.first
77
87
  create_array_property_def(resource, pname, pclass, info)
78
- else
79
- pclass = type_module.const_get ptype
80
- create_property_def(resource, pname, pclass)
81
88
  end
82
89
  end
83
90
  resource_name
84
91
  end
85
92
 
86
- # rubocop:disable Metrics/PerceivedComplexity
87
93
  def create_array_property_def(resource, pname, pclass, info)
88
94
  singular_name = CfnDsl::Plurals.singularize pname
89
95
  plural_name = singular_name == pname ? CfnDsl::Plurals.pluralize(pname) : pname
@@ -111,7 +117,6 @@ module CfnDsl
111
117
  # Singular form understands concatenation and Fn::If property
112
118
  create_singular_property_def(resource, pname, pclass, singular_name) if singular_name
113
119
  end
114
- # rubocop:enable Metrics/PerceivedComplexity
115
120
 
116
121
  def create_resource_accessor(accessor, resource, type)
117
122
  class_eval do
@@ -245,7 +250,7 @@ module CfnDsl
245
250
  end
246
251
  end
247
252
 
248
- # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
253
+ # rubocop:disable Metrics/CyclomaticComplexity
249
254
  def _check_refs(container_name, method, source_containers)
250
255
  container = instance_variable_get("@#{container_name}s")
251
256
  return [] unless container
@@ -279,7 +284,7 @@ module CfnDsl
279
284
 
280
285
  invalids
281
286
  end
282
- # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
287
+ # rubocop:enable Metrics/CyclomaticComplexity
283
288
 
284
289
  def validate
285
290
  errors = check_refs || []
@@ -20,7 +20,7 @@ module CfnDsl
20
20
  @singles = @plurals.invert
21
21
 
22
22
  def pluralize(name)
23
- @plurals.fetch(name.to_s) { |key| key + 's' }
23
+ @plurals.fetch(name.to_s) { |key| "#{key}s" }
24
24
  end
25
25
 
26
26
  def singularize(name)
@@ -29,7 +29,7 @@ module CfnDsl
29
29
  when /List$/
30
30
  key[0..-5]
31
31
  when /ies$/
32
- key[0..-4] + 'y'
32
+ "#{key[0..-4]}y"
33
33
  when /s$/
34
34
  key[0..-2]
35
35
  else
@@ -79,7 +79,7 @@ module CfnDsl
79
79
  # then any existing file is considered sufficient, and 'latest' is the version used for downloading
80
80
  #
81
81
  # @todo Add capability to provide a user spec/patches dir
82
- def specification(name: nil, file:, version: nil)
82
+ def specification(file:, name: nil, version: nil)
83
83
  if name
84
84
  desc 'Update Resource Specification' unless ::Rake.application.last_description
85
85
  task name, [:cfn_spec_version] => file
@@ -218,7 +218,7 @@ module CfnDsl
218
218
  end
219
219
 
220
220
  def verbose
221
- (Rake.verbose? || cfndsl_opts&.fetch(:verbose, false)) && STDERR
221
+ (Rake.verbose? || cfndsl_opts&.fetch(:verbose, false)) && $stderr
222
222
  end
223
223
 
224
224
  def generate(opts)
@@ -238,8 +238,8 @@ module CfnDsl
238
238
  verbose&.puts("Writing to #{type}")
239
239
  end
240
240
 
241
- def outputter(opts)
242
- opts[:output].nil? ? yield(STDOUT) : file_output(opts[:output]) { |f| yield f }
241
+ def outputter(opts, &block)
242
+ opts[:output].nil? ? yield($stdout) : file_output(opts[:output], &block)
243
243
  end
244
244
 
245
245
  def model(filename)
@@ -253,8 +253,8 @@ module CfnDsl
253
253
  cfndsl_opts.fetch(:extras, [])
254
254
  end
255
255
 
256
- def file_output(path)
257
- File.open(File.expand_path(path), 'w') { |f| yield f }
256
+ def file_output(path, &block)
257
+ File.open(File.expand_path(path), 'w', &block)
258
258
  end
259
259
  end
260
260
  # rubocop:enable Metrics/ClassLength
@@ -10,7 +10,7 @@ module RefCheck
10
10
  end
11
11
 
12
12
  # Build up a set of references.
13
- # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
13
+ # rubocop:disable Metrics/CyclomaticComplexity
14
14
  def build_references(refs = [], origin = nil, method = :all_refs)
15
15
  if respond_to?(method)
16
16
  send(method).each do |ref|
@@ -30,7 +30,7 @@ module RefCheck
30
30
 
31
31
  refs
32
32
  end
33
- # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
33
+ # rubocop:enable Metrics/CyclomaticComplexity
34
34
 
35
35
  def ref_children
36
36
  []
data/lib/cfndsl/runner.rb CHANGED
@@ -117,14 +117,14 @@ module CfnDsl
117
117
  end
118
118
 
119
119
  filename = File.expand_path(ARGV[0])
120
- verbose = options[:verbose] && STDERR
120
+ verbose = options[:verbose] && $stderr
121
121
 
122
122
  verbose.puts "Using specification file #{CfnDsl.specification_file}" if verbose
123
123
 
124
124
  require_relative 'cloudformation'
125
125
  model = CfnDsl.eval_file_with_extras(filename, options[:extras], verbose)
126
126
 
127
- output = STDOUT
127
+ output = $stdout
128
128
  if options[:output] != '-'
129
129
  verbose.puts("Writing to #{options[:output]}") if verbose
130
130
  output = File.open(File.expand_path(options[:output]), 'w')
@@ -70,20 +70,18 @@ module CfnDsl
70
70
  next unless %w[ResourceTypes PropertyTypes].include?(top_level_type)
71
71
 
72
72
  patches.each_pair do |property_type_name, patch_details|
73
- begin
74
- applies_to = spec[top_level_type]
75
- unless property_type_name == 'patch'
76
- # Patch applies within a specific property type
77
- applies_to = applies_to[property_type_name]
78
- patch_details = patch_details['patch']
79
- end
80
-
81
- Hana::Patch.new(patch_details['operations']).apply(applies_to) if patch_required?(patch_details)
82
- rescue Hana::Patch::MissingTargetException => e
83
- raise "Failed specification patch #{top_level_type} #{property_type_name} from #{from_file}" if fail_patches
84
-
85
- warn "Ignoring failed specification patch #{top_level_type} #{property_type_name} from #{from_file} - #{e.class.name}:#{e.message}"
73
+ applies_to = spec[top_level_type]
74
+ unless property_type_name == 'patch'
75
+ # Patch applies within a specific property type
76
+ applies_to = applies_to[property_type_name]
77
+ patch_details = patch_details['patch']
86
78
  end
79
+
80
+ Hana::Patch.new(patch_details['operations']).apply(applies_to) if patch_required?(patch_details)
81
+ rescue Hana::Patch::MissingTargetException => e
82
+ raise "Failed specification patch #{top_level_type} #{property_type_name} from #{from_file}" if fail_patches
83
+
84
+ warn "Ignoring failed specification patch #{top_level_type} #{property_type_name} from #{from_file} - #{e.class.name}:#{e.message}"
87
85
  end
88
86
  end
89
87
  end
data/lib/cfndsl/types.rb CHANGED
@@ -9,6 +9,18 @@ module CfnDsl
9
9
  # Types helper
10
10
  # rubocop:disable Metrics/ModuleLength
11
11
  module Types
12
+ def self.extract_list_type(root_name, property_info)
13
+ # Tag is a reused type, but not quite primitive
14
+ # and not all resources use the general form
15
+ if property_info['ItemType'] == 'Tag'
16
+ ['Tag']
17
+ elsif (property_info['ItemType'] == 'Json') && (property_info['Type'] == 'List')
18
+ ['Json']
19
+ else
20
+ Array(root_name + property_info['ItemType'])
21
+ end
22
+ end
23
+
12
24
  def self.extract_from_resource_spec(fail_patches: false)
13
25
  spec = Specification.load_file(fail_patches: fail_patches)
14
26
  resources = extract_resources spec.resources
@@ -16,35 +28,31 @@ module CfnDsl
16
28
  { 'Resources' => resources, 'Types' => types, 'Version' => spec.version, 'File' => spec.file }
17
29
  end
18
30
 
19
- # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
31
+ # rubocop:disable Metrics/CyclomaticComplexity
20
32
  def self.extract_resources(spec)
21
33
  spec.each_with_object({}) do |(resource_name, resource_info), resources|
22
34
  properties = resource_info['Properties'].each_with_object({}) do |(property_name, property_info), extracted|
23
35
  # some json incorrectly labelled as Type -> Json instead of PrimitiveType
24
36
  # also, AWS now has the concept of Map which cfndsl had never defined
25
- if property_info['Type'] == 'Map' || property_info['Type'] == 'Json'
26
- property_type = 'Json'
27
- elsif property_info['PrimitiveType']
28
- property_type = property_info['PrimitiveType']
29
- elsif property_info['PrimitiveItemType']
30
- property_type = Array(property_info['PrimitiveItemType'])
31
- elsif property_info['PrimitiveTypes']
32
- property_type = property_info['PrimitiveTypes'][0]
33
- elsif property_info['ItemType']
34
- # Tag is a reused type, but not quite primitive
35
- # and not all resources use the general form
36
- property_type = if property_info['ItemType'] == 'Tag'
37
- ['Tag']
38
- else
39
- Array(resource_name.split('::').join + property_info['ItemType'])
40
- end
41
- elsif property_info['Type']
42
- # Special types (defined below) are joined with their parent
43
- # resource name for uniqueness and connection
44
- property_type = resource_name.split('::').join + property_info['Type']
45
- else
46
- warn "could not extract resource type from #{resource_name}"
47
- end
37
+ property_type =
38
+ if property_info['Type'] == 'Map' || property_info['Type'] == 'Json'
39
+ 'Json'
40
+ elsif property_info['PrimitiveType']
41
+ property_info['PrimitiveType']
42
+ elsif property_info['PrimitiveItemType']
43
+ Array(property_info['PrimitiveItemType'])
44
+ elsif property_info['PrimitiveTypes']
45
+ property_info['PrimitiveTypes'][0]
46
+ elsif property_info['ItemType']
47
+ extract_list_type(resource_name.split('::').join, property_info)
48
+ elsif property_info['Type']
49
+ # Special types (defined below) are joined with their parent
50
+ # resource name for uniqueness and connection
51
+ resource_name.split('::').join + property_info['Type']
52
+ else
53
+ warn "could not extract resource type for property #{property_name} from #{resource_name}, assuming Json"
54
+ 'Json'
55
+ end
48
56
  extracted[property_name] = property_type
49
57
  extracted
50
58
  end
@@ -52,7 +60,6 @@ module CfnDsl
52
60
  resources
53
61
  end
54
62
  end
55
- # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength
56
63
 
57
64
  # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength
58
65
  def self.extract_types(spec)
@@ -76,43 +83,43 @@ module CfnDsl
76
83
  root_resource_name = root_resource ? root_resource[1].gsub(/::/, '') : property_name
77
84
  property_name = property_name.gsub(/::|\./, '')
78
85
 
79
- if property_info.key?('PrimitiveType')
80
- properties = property_info['PrimitiveType']
81
- elsif property_info.key?('Type')
82
- properties = property_info['Type']
83
- elsif property_info.key?('Properties')
84
- properties = property_info['Properties'].each_with_object({}) do |(nested_prop_name, nested_prop_info), extracted|
85
- if nested_prop_info['Type'] == 'Map' || nested_prop_info['Type'] == 'Json'
86
- # The Map type and the incorrectly labelled Json type
87
- nested_prop_type = 'Json'
88
- elsif nested_prop_info['PrimitiveType']
89
- nested_prop_type = nested_prop_info['PrimitiveType']
90
- elsif nested_prop_info['PrimitiveItemType']
91
- nested_prop_type = Array(nested_prop_info['PrimitiveItemType'])
92
- elsif nested_prop_info['PrimitiveItemTypes']
93
- nested_prop_type = Array(nested_prop_info['PrimitiveItemTypes'])
94
- elsif nested_prop_info['Types']
95
- nested_prop_type = Array(nested_prop_info['Types'])
96
- elsif nested_prop_info['ItemType']
97
- # Tag is a reused type, but not quite primitive
98
- # and not all resources use the general form
86
+ properties =
87
+ if property_info.key?('PrimitiveType')
88
+ property_info['PrimitiveType']
89
+ elsif property_info.key?('ItemType')
90
+ extract_list_type(root_resource_name, property_info)
91
+ elsif property_info.key?('Type')
92
+ property_info['Type']
93
+ elsif property_info.key?('Properties')
94
+ property_info['Properties'].each_with_object({}) do |(nested_prop_name, nested_prop_info), extracted|
99
95
  nested_prop_type =
100
- if nested_prop_info['ItemType'] == 'Tag'
101
- ['Tag']
96
+ if nested_prop_info['Type'] == 'Map' || nested_prop_info['Type'] == 'Json'
97
+ # The Map type and the incorrectly labelled Json type
98
+ 'Json'
99
+ elsif nested_prop_info['PrimitiveType']
100
+ nested_prop_info['PrimitiveType']
101
+ elsif nested_prop_info['PrimitiveItemType']
102
+ Array(nested_prop_info['PrimitiveItemType'])
103
+ elsif nested_prop_info['PrimitiveItemTypes']
104
+ Array(nested_prop_info['PrimitiveItemTypes'])
105
+ elsif nested_prop_info['Types']
106
+ Array(nested_prop_info['Types'])
107
+ elsif nested_prop_info['ItemType']
108
+ extract_list_type(root_resource_name, nested_prop_info)
109
+ elsif nested_prop_info['Type']
110
+ root_resource_name + nested_prop_info['Type']
102
111
  else
103
- Array(root_resource_name + nested_prop_info['ItemType'])
112
+ warn "could not extract property type for #{nested_prop_name} from #{property_name}, assuming Json"
113
+ p nested_prop_info
114
+ 'Json'
104
115
  end
105
-
106
- elsif nested_prop_info['Type']
107
- nested_prop_type = root_resource_name + nested_prop_info['Type']
108
- else
109
- warn "could not extract property type from #{property_name}"
110
- p nested_prop_info
116
+ extracted[nested_prop_name] = nested_prop_type
117
+ extracted
111
118
  end
112
- extracted[nested_prop_name] = nested_prop_type
113
- extracted
119
+ else
120
+ # No Type information typically just a plain JSON object
121
+ 'Json'
114
122
  end
115
- end
116
123
  types[property_name] = properties
117
124
  types
118
125
  end
@@ -152,27 +159,33 @@ module CfnDsl
152
159
  classes = {}
153
160
 
154
161
  # Go through and declare all of the types first
155
- types_list['Types'].each_key do |typename|
156
- if !type_def.const_defined?(typename)
157
- klass = type_def.const_set(typename, Class.new(type_def::Type))
158
- classes[typename] = klass
159
- else
160
- classes[typename] = type_def.const_get(typename)
162
+ types_list['Types'].each_pair do |typename, type_val|
163
+ unless type_def.const_defined?(typename)
164
+ if type_val.is_a?(Array)
165
+ type_def.const_set(typename, type_val)
166
+ else
167
+ classes[typename] = type_def.const_set(typename, Class.new(type_def::Type))
168
+ end
161
169
  end
162
170
  end
163
171
 
164
172
  # Now go through them again and define attribute setter methods
165
- classes.each_pair do |typename, type|
173
+ classes.each_pair do |typename, klass|
166
174
  typeval = types_list['Types'][typename]
167
175
  next unless typeval.respond_to?(:each_pair)
168
176
 
169
177
  typeval.each_pair do |attr_name, attr_type|
170
178
  attr_method = attr_name
171
179
  variable = "@#{attr_name}".to_sym
172
- klass = nil
180
+
181
+ # handle bogus List defined as Type
182
+ unless attr_type.is_a?(Array)
183
+ attr_class = type_def.const_get(attr_type)
184
+ attr_type = attr_class if attr_class.is_a?(Array)
185
+ end
173
186
 
174
187
  if attr_type.is_a?(Array)
175
- klass = type_def.const_get(attr_type[0])
188
+ attr_class = type_def.const_get(attr_type[0])
176
189
  singular_method = CfnDsl::Plurals.singularize(attr_name)
177
190
 
178
191
  if singular_method == attr_name
@@ -180,16 +193,14 @@ module CfnDsl
180
193
  attr_method = CfnDsl::Plurals.pluralize(attr_name)
181
194
  end
182
195
 
183
- define_array_method(klass, singular_method, type, variable) if singular_method != attr_method
184
-
185
- else
186
- klass = type_def.const_get(attr_type)
196
+ define_array_method(attr_class, singular_method, klass, variable) if singular_method != attr_method
187
197
  end
188
198
 
189
- type.class_eval do
199
+ klass.class_eval do
190
200
  CfnDsl.method_names(attr_method) do |inner_method|
191
201
  define_method(inner_method) do |value = nil, *_rest, &block|
192
- value ||= klass.new
202
+ # noinspection RubyScope
203
+ value ||= attr_class.new
193
204
  instance_variable_set(variable, value)
194
205
  value.instance_eval(&block) if block
195
206
  value
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CfnDsl
4
- VERSION = '1.2.0'
4
+ VERSION = '1.3.3'
5
5
  end
@@ -120,16 +120,16 @@ module DeepMerge
120
120
  puts "#{di} looping: #{src_key.inspect} => #{src_value.inspect} :: #{dest.inspect}" if merge_debug
121
121
  if dest[src_key]
122
122
  puts "#{di} ==>merging: #{src_key.inspect} => #{src_value.inspect} :: #{dest[src_key].inspect}" if merge_debug
123
- dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(debug_indent: di + ' '))
123
+ dest[src_key] = deep_merge!(src_value, dest[src_key], options.merge(debug_indent: "#{di} "))
124
124
  else # dest[src_key] doesn't exist so we want to create and overwrite it (but we do this via deep_merge!)
125
125
  puts "#{di} ==>merging over: #{src_key.inspect} => #{src_value.inspect}" if merge_debug
126
- # note: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
126
+ # NOTE: we rescue here b/c some classes respond to "dup" but don't implement it (Numeric, TrueClass, FalseClass, NilClass among maybe others)
127
127
  begin
128
128
  src_dup = src_value.dup # we dup src_value if possible because we're going to merge into it (since dest is empty)
129
129
  rescue TypeError
130
130
  src_dup = src_value
131
131
  end
132
- dest[src_key] = deep_merge!(src_value, src_dup, options.merge(debug_indent: di + ' '))
132
+ dest[src_key] = deep_merge!(src_value, src_dup, options.merge(debug_indent: "#{di} "))
133
133
  end
134
134
  elsif dest.is_a?(Array) && extend_existing_arrays
135
135
  dest.push(source)
@@ -181,12 +181,12 @@ module DeepMerge
181
181
  list = []
182
182
  dest.each_index do |i|
183
183
  list[i] = deep_merge!(source[i] || {}, dest[i],
184
- options.merge(debug_indent: di + ' '))
184
+ options.merge(debug_indent: "#{di} "))
185
185
  end
186
- list += source[dest.count..-1] if source.count > dest.count
186
+ list += source[dest.count..] if source.count > dest.count
187
187
  dest = list
188
188
  elsif keep_array_duplicates
189
- dest = dest.concat(source)
189
+ dest.concat(source)
190
190
  else
191
191
  dest |= source
192
192
  end