cfndsl 1.2.0 → 1.3.3

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.
@@ -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