rspec-puppet-facts 2.0.5 → 4.0.0

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.
@@ -2,6 +2,6 @@ module RspecPuppetFacts
2
2
  # This module contains the current version constant
3
3
  module Version
4
4
  # The current version of this gem
5
- STRING = '2.0.5'
5
+ STRING = '4.0.0'
6
6
  end
7
7
  end
@@ -2,6 +2,7 @@ require 'puppet'
2
2
  require 'facter'
3
3
  require 'facterdb'
4
4
  require 'json'
5
+ require 'deep_merge'
5
6
 
6
7
  # The purpose of this module is to simplify the Puppet
7
8
  # module's RSpec tests by looping through all supported
@@ -56,9 +57,8 @@ module RspecPuppetFacts
56
57
  #
57
58
  # @api private
58
59
  def on_supported_os_implementation(opts = {})
59
- unless (facterversion = opts[:facterversion]) =~ /\A\d+\.\d+(?:\.\d+)*\z/
60
- raise ArgumentError, ":facterversion must be in the format 'n.n' or " \
61
- "'n.n.n' (n is numeric), not '#{facterversion}'"
60
+ unless /\A\d+\.\d+(?:\.\d+)*\z/.match?((facterversion = opts[:facterversion]))
61
+ raise ArgumentError, ":facterversion must be in the format 'n.n' or 'n.n.n' (n is numeric), not '#{facterversion}'"
62
62
  end
63
63
 
64
64
  filter = []
@@ -66,81 +66,75 @@ module RspecPuppetFacts
66
66
  if os_sup['operatingsystemrelease']
67
67
  Array(os_sup['operatingsystemrelease']).map do |operatingsystemmajrelease|
68
68
  opts[:hardwaremodels].each do |hardwaremodel|
69
-
70
69
  os_release_filter = "/^#{Regexp.escape(operatingsystemmajrelease.split(' ')[0])}/"
71
- if os_sup['operatingsystem'] =~ /BSD/i
70
+ case os_sup['operatingsystem']
71
+ when /BSD/i
72
72
  hardwaremodel = 'amd64'
73
- elsif os_sup['operatingsystem'] =~ /Solaris/i
73
+ when /Solaris/i
74
74
  hardwaremodel = 'i86pc'
75
- elsif os_sup['operatingsystem'] =~ /AIX/i
75
+ when /AIX/i
76
76
  hardwaremodel = '/^IBM,.*/'
77
77
  os_release_filter = if operatingsystemmajrelease =~ /\A(\d+)\.(\d+)\Z/
78
78
  "/^#{$~[1]}#{$~[2]}00-/"
79
79
  else
80
80
  "/^#{operatingsystemmajrelease}-/"
81
81
  end
82
- elsif os_sup['operatingsystem'] =~ /Windows/i
83
- hardwaremodel = facterversion =~ /^[12]\./ ? 'x64' : 'x86_64'
82
+ when /Windows/i
83
+ hardwaremodel = 'x86_64'
84
84
  os_sup['operatingsystem'] = os_sup['operatingsystem'].downcase
85
85
  operatingsystemmajrelease = operatingsystemmajrelease[/\A(?:Server )?(.+)/i, 1]
86
86
 
87
87
  # force quoting because windows releases can contain spaces
88
88
  os_release_filter = "\"#{operatingsystemmajrelease}\""
89
-
90
- if operatingsystemmajrelease == '2016' && Puppet::Util::Package.versioncmp(facterversion, '3.4') < 0
91
- os_release_filter = '/^10\\.0\\./'
92
- end
93
- elsif os_sup['operatingsystem'] =~ /Amazon/i
89
+ when /Amazon/i
94
90
  # Tighten the regex for Amazon Linux 2 so that we don't pick up Amazon Linux 2016 or 2017 facts
95
- os_release_filter = "/^2$/" if operatingsystemmajrelease == '2'
91
+ os_release_filter = '/^2$/' if operatingsystemmajrelease == '2'
96
92
  end
97
93
 
98
94
  filter << {
99
- :operatingsystem => os_sup['operatingsystem'],
100
- :operatingsystemrelease => os_release_filter,
101
- :hardwaremodel => hardwaremodel,
95
+ 'os.name' => os_sup['operatingsystem'],
96
+ 'os.release.full' => os_release_filter,
97
+ 'os.hardware' => hardwaremodel,
102
98
  }
103
99
  end
104
100
  end
105
101
  else
106
102
  opts[:hardwaremodels].each do |hardwaremodel|
107
103
  filter << {
108
- :operatingsystem => os_sup['operatingsystem'],
109
- :hardwaremodel => hardwaremodel,
104
+ 'os.name' => os_sup['operatingsystem'],
105
+ 'os.hardware' => hardwaremodel,
110
106
  }
111
107
  end
112
108
  end
113
109
  end
114
110
 
115
- facterversion_obj = Gem::Version.new(facterversion)
111
+ strict_requirement = RspecPuppetFacts.facter_version_to_strict_requirement(facterversion)
112
+
113
+ loose_requirement = RspecPuppetFacts.facter_version_to_loose_requirement(facterversion)
114
+ received_facts = []
116
115
 
117
116
  # FacterDB may have newer versions of facter data for which it contains a subset of all possible
118
117
  # facter data (see FacterDB 0.5.2 for Facter releases 3.8 and 3.9). In this situation we need to
119
118
  # cycle through and downgrade Facter versions per platform type until we find matching Facter data.
120
119
  filter.each do |filter_spec|
121
- facter_version_filter = RspecPuppetFacts.facter_version_to_filter(facterversion)
122
- db = FacterDB.get_facts(filter_spec.merge({ :facterversion => facter_version_filter }))
123
-
124
- if db.empty?
125
- if RspecPuppetFacts.spec_facts_strict?
126
- raise ArgumentError, "No facts were found in the FacterDB for Facter v#{facterversion} on #{filter_spec}, aborting"
127
- end
120
+ versions = FacterDB.get_facts(filter_spec, symbolize_keys: !RSpec.configuration.facterdb_string_keys).to_h do |facts|
121
+ [Gem::Version.new(facts[:facterversion]), facts]
122
+ end
128
123
 
129
- version = FacterDB.get_facts(filter_spec).map { |facts| Gem::Version.new(facts[:facterversion]) }.sort.reverse.detect { |v| v <= facterversion_obj }
124
+ version, facts = versions.select { |v, _f| strict_requirement =~ v }.max_by { |v, _f| v }
130
125
 
126
+ unless version
127
+ version, facts = versions.select { |v, _f| loose_requirement =~ v }.max_by { |v, _f| v } if loose_requirement
131
128
  next unless version
132
- version = version.to_s
133
- facter_version_filter = "/\\A#{Regexp.escape(version)}/"
134
129
 
135
- unless version == facterversion
136
- RspecPuppetFacts.warning "No facts were found in the FacterDB for Facter v#{facterversion} on #{filter_spec}, using v#{version} instead"
137
- end
130
+ raise ArgumentError, "No facts were found in the FacterDB for Facter v#{facterversion} on #{filter_spec}, aborting" if RspecPuppetFacts.spec_facts_strict?
131
+
132
+ RspecPuppetFacts.warning "No facts were found in the FacterDB for Facter v#{facterversion} on #{filter_spec}, using v#{version} instead"
138
133
  end
139
134
 
140
- filter_spec[:facterversion] = facter_version_filter
135
+ received_facts << facts
141
136
  end
142
137
 
143
- received_facts = FacterDB::get_facts(filter)
144
138
  unless received_facts.any?
145
139
  RspecPuppetFacts.warning "No facts were found in the FacterDB for: #{filter.inspect}"
146
140
  return {}
@@ -148,38 +142,25 @@ module RspecPuppetFacts
148
142
 
149
143
  os_facts_hash = {}
150
144
  received_facts.map do |facts|
151
- # Fix facter bug
152
- # Todo: refactor the whole block to rely on structured facts and use legacy facts as fallback
153
- if facts[:operatingsystem] == 'Ubuntu'
154
- operatingsystemmajrelease = facts[:operatingsystemrelease].split('.')[0..1].join('.')
155
- elsif facts[:operatingsystem] == 'OpenBSD'
156
- operatingsystemmajrelease = facts[:operatingsystemrelease]
157
- elsif facts[:operatingsystem] == 'windows' && facts[:operatingsystemrelease].start_with?('10.0.')
158
- operatingsystemmajrelease = '2016'
159
- elsif facts.dig(:os, 'release', 'major')
160
- operatingsystemmajrelease = facts[:os]['release']['major']
161
- elsif facts.dig(:os, 'distro', 'release', 'major')
162
- operatingsystemmajrelease = facts[:os]['distro']['release']['major']
163
- else
164
- if facts[:operatingsystemmajrelease].nil?
165
- operatingsystemmajrelease = facts[:operatingsystemrelease].split('.')[0]
166
- else
167
- operatingsystemmajrelease = facts[:operatingsystemmajrelease]
168
- end
145
+ os_fact = RSpec.configuration.facterdb_string_keys ? facts['os'] : facts[:os]
146
+ unless os_fact
147
+ RspecPuppetFacts.warning "No os fact was found in FacterDB for: #{facts}"
148
+ next
169
149
  end
170
- os = "#{facts[:operatingsystem].downcase}-#{operatingsystemmajrelease}-#{facts[:hardwaremodel]}"
171
- next unless os.start_with? RspecPuppetFacts.spec_facts_os_filter if RspecPuppetFacts.spec_facts_os_filter
150
+
151
+ os = "#{os_fact['name'].downcase}-#{os_fact['release']['major']}-#{os_fact['hardware']}"
152
+ next if RspecPuppetFacts.spec_facts_os_filter && !os.start_with?(RspecPuppetFacts.spec_facts_os_filter)
153
+
172
154
  facts.merge! RspecPuppetFacts.common_facts
173
155
  os_facts_hash[os] = RspecPuppetFacts.with_custom_facts(os, facts)
174
156
  end
175
157
 
176
- return stringify_keys(os_facts_hash) if RSpec.configuration.facterdb_string_keys
177
-
178
158
  os_facts_hash
179
159
  end
180
160
 
161
+ # @api private
181
162
  def stringify_keys(hash)
182
- Hash[hash.collect { |k,v| [k.to_s, v.is_a?(Hash) ? stringify_keys(v) : v] }]
163
+ hash.to_h { |k, v| [k.to_s, v.is_a?(Hash) ? stringify_keys(v) : v] }
183
164
  end
184
165
 
185
166
  # Register a custom fact that will be included in the facts hash.
@@ -210,7 +191,8 @@ module RspecPuppetFacts
210
191
  # @api private
211
192
  def self.register_custom_fact(name, value, options)
212
193
  @custom_facts ||= {}
213
- @custom_facts[name.to_s] = {:options => options, :value => value}
194
+ name = RSpec.configuration.facterdb_string_keys ? name.to_s : name.to_sym
195
+ @custom_facts[name] = { options: options, value: value }
214
196
  end
215
197
 
216
198
  # Adds any custom facts according to the rules defined for the operating
@@ -226,7 +208,13 @@ module RspecPuppetFacts
226
208
  next if fact[:options][:confine] && !fact[:options][:confine].include?(os)
227
209
  next if fact[:options][:exclude] && fact[:options][:exclude].include?(os)
228
210
 
229
- facts[name] = fact[:value].respond_to?(:call) ? fact[:value].call(os, facts) : fact[:value]
211
+ value = fact[:value].respond_to?(:call) ? fact[:value].call(os, facts) : fact[:value]
212
+ # if merge_facts passed, merge supplied facts into facts hash
213
+ if fact[:options][:merge_facts]
214
+ facts.deep_merge!({ name => value })
215
+ else
216
+ facts[name] = value
217
+ end
230
218
  end
231
219
 
232
220
  facts
@@ -246,7 +234,7 @@ module RspecPuppetFacts
246
234
  # @return [nil,String]
247
235
  # @api private
248
236
  def self.spec_facts_os_filter
249
- ENV['SPEC_FACTS_OS']
237
+ ENV.fetch('SPEC_FACTS_OS', nil)
250
238
  end
251
239
 
252
240
  # If SPEC_FACTS_STRICT is set to `yes`, RspecPuppetFacts will error on missing FacterDB entries, instead of warning & skipping the tests, or using an older facter version.
@@ -262,10 +250,11 @@ module RspecPuppetFacts
262
250
  # @return [Hash <Symbol => String>]
263
251
  def self.common_facts
264
252
  return @common_facts if @common_facts
253
+
265
254
  @common_facts = {
266
- :puppetversion => Puppet.version,
267
- :rubysitedir => RbConfig::CONFIG['sitelibdir'],
268
- :rubyversion => RUBY_VERSION,
255
+ puppetversion: Puppet.version,
256
+ rubysitedir: RbConfig::CONFIG['sitelibdir'],
257
+ rubyversion: RUBY_VERSION,
269
258
  }
270
259
 
271
260
  @common_facts[:mco_version] = MCollective::VERSION if mcollective?
@@ -273,6 +262,7 @@ module RspecPuppetFacts
273
262
  if augeas?
274
263
  @common_facts[:augeasversion] = Augeas.open(nil, nil, Augeas::NO_MODL_AUTOLOAD).get('/augeas/version')
275
264
  end
265
+ @common_facts = stringify_keys(@common_facts) if RSpec.configuration.facterdb_string_keys
276
266
 
277
267
  @common_facts
278
268
  end
@@ -309,8 +299,9 @@ module RspecPuppetFacts
309
299
  # @api private
310
300
  def self.meta_supported_os
311
301
  unless metadata['operatingsystem_support'].is_a? Array
312
- fail StandardError, 'Unknown operatingsystem support in the metadata file!'
302
+ raise StandardError, 'Unknown operatingsystem support in the metadata file!'
313
303
  end
304
+
314
305
  metadata['operatingsystem_support']
315
306
  end
316
307
 
@@ -322,8 +313,9 @@ module RspecPuppetFacts
322
313
  def self.metadata
323
314
  return @metadata if @metadata
324
315
  unless File.file? metadata_file
325
- fail StandardError, "Can't find metadata.json... dunno why"
316
+ raise StandardError, "Can't find metadata.json... dunno why"
326
317
  end
318
+
327
319
  content = File.read metadata_file
328
320
  @metadata = JSON.parse content
329
321
  end
@@ -339,7 +331,7 @@ module RspecPuppetFacts
339
331
  # @param message [String]
340
332
  # @api private
341
333
  def self.warning(message)
342
- STDERR.puts message
334
+ warn message
343
335
  end
344
336
 
345
337
  # Reset the memoization
@@ -352,13 +344,47 @@ module RspecPuppetFacts
352
344
  @metadata = nil
353
345
  end
354
346
 
355
- # Generates a JGrep statement expression for a specific facter version
356
- # @return [String] JGrep statement expression
357
- # @param version [String] the Facter version
347
+ # Construct the strict facter version requirement
348
+ # @return [Gem::Requirement] The version requirement to match
358
349
  # @api private
359
- def self.facter_version_to_filter(version)
360
- major, minor = version.split('.')
361
- "/\\A#{major}\\.#{minor}\\./"
350
+ def self.facter_version_to_strict_requirement(version)
351
+ Gem::Requirement.new(facter_version_to_strict_requirement_string(version))
352
+ end
353
+
354
+ # Construct the strict facter version requirement string
355
+ # @return [String] The version requirement to match
356
+ # @api private
357
+ def self.facter_version_to_strict_requirement_string(version)
358
+ if /\A[0-9]+(\.[0-9]+)*\Z/.match?(version)
359
+ # Interpret 3 as ~> 3.0
360
+ "~> #{version}.0"
361
+ else
362
+ version
363
+ end
364
+ end
365
+
366
+ # Construct the loose facter version requirement
367
+ # @return [Optional[Gem::Requirement]] The version requirement to match
368
+ # @api private
369
+ def self.facter_version_to_loose_requirement(version)
370
+ string = facter_version_to_loose_requirement_string(version)
371
+ Gem::Requirement.new(string) if string
372
+ end
373
+
374
+ # Construct the facter version requirement string
375
+ # @return [String] The version requirement to match
376
+ # @api private
377
+ def self.facter_version_to_loose_requirement_string(version)
378
+ if (m = /\A(?<major>[0-9]+)\.(?<minor>[0-9]+)(?:\.(?<patch>[0-9]+))?\Z/.match(version))
379
+ # Interpret 3.1 as < 3.2 and 3.2.1 as < 3.3
380
+ "< #{m[:major]}.#{m[:minor].to_i + 1}"
381
+ elsif /\A[0-9]+\Z/.match?(version)
382
+ # Interpret 3 as < 4
383
+ "< #{version.to_i + 1}"
384
+ else # rubocop:disable Style/EmptyElse
385
+ # This would be the same as the strict requirement
386
+ nil
387
+ end
362
388
  end
363
389
 
364
390
  def self.facter_version_for_puppet_version(puppet_version)
@@ -373,13 +399,13 @@ module RspecPuppetFacts
373
399
  fd = File.open(json_path, 'rb:UTF-8')
374
400
  data = JSON.parse(fd.read)
375
401
 
376
- version_map = data.map { |_, versions|
402
+ version_map = data.map do |_, versions|
377
403
  if versions['puppet'].nil? || versions['facter'].nil?
378
404
  nil
379
405
  else
380
406
  [Gem::Version.new(versions['puppet']), versions['facter']]
381
407
  end
382
- }.compact
408
+ end.compact
383
409
 
384
410
  puppet_gem_version = Gem::Version.new(puppet_version)
385
411
  applicable_versions = version_map.select { |p, _| puppet_gem_version >= p }
@@ -388,7 +414,7 @@ module RspecPuppetFacts
388
414
  return Facter.version
389
415
  end
390
416
 
391
- applicable_versions.sort { |a, b| b.first <=> a.first }.first.last
417
+ applicable_versions.max_by { |p, _| p }.last
392
418
  rescue JSON::ParserError
393
419
  warning "#{json_path} contains invalid JSON, defaulting to Facter #{Facter.version}"
394
420
  Facter.version
@@ -398,7 +424,6 @@ module RspecPuppetFacts
398
424
  end
399
425
 
400
426
  RSpec.configure do |c|
401
- c.add_setting :default_facter_version,
402
- :default => RspecPuppetFacts.facter_version_for_puppet_version(Puppet.version)
403
- c.add_setting :facterdb_string_keys, :default => false
427
+ c.add_setting :default_facter_version, default: RspecPuppetFacts.facter_version_for_puppet_version(Puppet.version)
428
+ c.add_setting :facterdb_string_keys, default: false
404
429
  end
@@ -13,18 +13,20 @@ Gem::Specification.new do |s|
13
13
  s.description = 'Contains facts from many Facter version on many Operating Systems'
14
14
  s.licenses = 'Apache-2.0'
15
15
 
16
- # see .travis.yml for the supported ruby versions
17
- s.required_ruby_version = '>= 2.4.0'
16
+ s.required_ruby_version = '>= 2.7.0'
18
17
 
19
18
  s.files = `git ls-files`.split("\n")
20
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
19
  s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
22
20
 
23
- s.add_development_dependency 'mime-types'
24
- s.add_development_dependency 'rake'
25
- s.add_development_dependency 'rspec'
26
- s.add_development_dependency 'yard'
27
- s.add_runtime_dependency 'puppet'
28
- s.add_runtime_dependency 'facter'
29
- s.add_runtime_dependency 'facterdb', '>= 0.5.0'
21
+ s.add_development_dependency 'mime-types', '~> 3.5', '>= 3.5.2'
22
+ s.add_development_dependency 'rake', '~> 13.1'
23
+ s.add_development_dependency 'rspec', '~> 3.12'
24
+ s.add_development_dependency 'yard', '~> 0.9.34'
25
+
26
+ s.add_development_dependency 'voxpupuli-rubocop', '~> 2.7.0'
27
+
28
+ s.add_runtime_dependency 'deep_merge', '~> 1.2'
29
+ s.add_runtime_dependency 'facter', '< 5'
30
+ s.add_runtime_dependency 'facterdb', '~> 2.1'
31
+ s.add_runtime_dependency 'puppet', '>= 7', '< 9'
30
32
  end
@@ -15,16 +15,16 @@
15
15
  {
16
16
  "operatingsystem": "Debian",
17
17
  "operatingsystemrelease": [
18
- "7",
19
- "8"
18
+ "11",
19
+ "12"
20
20
  ]
21
21
  },
22
22
  {
23
23
  "operatingsystem": "RedHat",
24
24
  "operatingsystemrelease": [
25
- "5",
26
- "6",
27
- "7"
25
+ "7",
26
+ "8",
27
+ "9"
28
28
  ]
29
29
  }
30
30
  ],