rspec-puppet 2.7.10 → 2.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38af21b6f0c4e9fcfeb90c063d299c725fd80d63e6d74b44d0a6e6f937f6d955
4
- data.tar.gz: 977523988ad9525e31c461040c33894e1cbd79c9180252f9521f8b8172ac111b
3
+ metadata.gz: 375cf91b75c36ee1bc0bbade72608205a7b85fce94a49d0fcfafb02fa6f8eddc
4
+ data.tar.gz: 732ce59e27a5fd9d15052262d21b2de6df85b090fb56152aca88da4480133ac1
5
5
  SHA512:
6
- metadata.gz: e8145ad0c55a4c52f7032f67ea2f73dc7fb72f162c32214e8b3e5aa5bd69efe1bd0ded6c2e36cff165e2e8f51734e0f94017c74f07ad0ce3b1bbeca90463ad34
7
- data.tar.gz: 149c839eb092309b0e3bb1be2c0887aed4dc0ebe8fb9ea2b4b5b562a797df8b39800486e24e181dc7d44abdddbe94bc9938978e0d5862e35fa73581203475545
6
+ metadata.gz: 3f1b0bb5c052df4ff70981384ad81bdc2e75ec1f23f9a890f101a933a4f7f50d569acef001524c7c3a9f37f0cfffd2eeaf0a26a7d67f8a3abd80b7147005e758
7
+ data.tar.gz: 65f0fbe718948c13b1866bb8d71c6b03fe741927ed86e97d6eeefb39526e98671e3ac5ae98e14e939654988fa8c272b3e72f66c5b61e9c6321fac11d5b66e276
@@ -2,6 +2,43 @@
2
2
  All notable changes to this project will be documented in this file. This
3
3
  project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
+ ## [2.8.0]
6
+
7
+ ### Breaking Changes
8
+ * As of the 2.8.0 release, the `rspec-puppet` project no longer guarantees compatibility
9
+ with Puppet 2.x or 3.x (running under Ruby 1.8.7) or Puppet 4.x (running under Ruby 1.9.3).
10
+ * This release adds support for the [`Sensitive`](https://puppet.com/docs/puppet/latest/lang_data_sensitive.html)
11
+ data type, however existing tests that were expecting `String` content may need to be updated
12
+ to wrap the expected value in the new `sensitive` helper:
13
+
14
+ ```ruby
15
+ # Old
16
+ it { is_expected.to contain_file('/etc/mysecret.conf').with_content("top secret\n") }
17
+
18
+ # New
19
+ it { is_expected.to contain_file('/etc/mysecret.conf').with_content(sensitive("top secret\n")) }
20
+ ```
21
+
22
+ ### Added
23
+ * Added support for [trusted external fact data](https://github.com/rodjek/rspec-puppet#specifying-trusted-external-data).
24
+ * Added the ability to exclude resources from the coverage report calculations using a regular expression.
25
+ (See [documentation](https://rspec-puppet.com/documentation/coverage/#excluded-resources) for an example.
26
+ * Added `have_unique_values_for_all` matcher to assert a specific resource parameter value is unique across
27
+ the entire catalogue.
28
+ (See [documentation](https://rspec-puppet.com/documentation/classes/#test-resource-parameter-values-for-uniqueness).)
29
+ * Added ability to customize module-layer Hiera configuration via new settings. See
30
+ [documentation](https://rspec-puppet.com/documentation/configuration/#disable_module_hiera) for details.
31
+
32
+ ### Changed
33
+ * `RSpec::Puppet::Cache` now evicts least recently used entries when it reaches max size.
34
+ * `rspec-puppet`'s implementation of `match_manifests` will no longer look in `init.pp` for class
35
+ declarations if a manifest file exactly matching the class name exists.
36
+
37
+ ### Fixed
38
+ * Resolved compatibility issues with Ruby 2.7.x and added Ruby 2.7.x to the test matrix.
39
+ * Resolved issues calculating coverage and reporting results when there are 0 tested resources.
40
+ * Resolved compatibility issue with `rspec-expectations` 3.10.0.
41
+
5
42
  ## [2.7.10]
6
43
 
7
44
  ### Fixed
data/README.md CHANGED
@@ -2,6 +2,20 @@
2
2
  [![Build Status](https://travis-ci.org/rodjek/rspec-puppet.svg?branch=master)](https://travis-ci.org/rodjek/rspec-puppet)
3
3
  [![Coverage Status](https://coveralls.io/repos/rodjek/rspec-puppet/badge.svg?branch=master)](https://coveralls.io/r/rodjek/rspec-puppet?branch=master)
4
4
 
5
+ #### Table of Contents
6
+
7
+ * [Installation](#installation)
8
+ * [Starting out with a new module](#starting-out-with-a-new-module)
9
+ * [Configure manifests for Puppet 4](#configure-manifests-for-puppet-4)
10
+ * [Configuration](#configuration)
11
+ * [Naming conventions](#naming-conventions)
12
+ * [Example groups](#example-groups)
13
+ * [Defined Types, Classes & Applications](#defined-types-classes--applications)
14
+ * [Functions](#functions)
15
+ * [Hiera integration](#hiera-integration)
16
+ * [Producing coverage reports](#producing-coverage-reports)
17
+ * [Related projects](#related-projects)
18
+
5
19
  ## Installation
6
20
 
7
21
  gem install rspec-puppet
@@ -759,6 +773,30 @@ RSpec.configure do |c|
759
773
  end
760
774
  ```
761
775
 
776
+ #### Specifying trusted external data
777
+
778
+ When testing with Puppet >= 6.14, the trusted facts hash will have an additional `external`
779
+ key for trusted external data.
780
+
781
+ By default, the test environment contains no trusted external data (as usually obtained from
782
+ trusted external commands and found in the `external` key). If you need to test against specific
783
+ trusted external data you can set those with a hash. The hash will then be available in
784
+ `$trusted['external']`
785
+
786
+ ```ruby
787
+ let(:trusted_external_data) { {'foo' => 'bar'} }
788
+ ```
789
+
790
+ You can also create a set of default trusted external data provided to all specs in your spec_helper:
791
+
792
+ ```ruby
793
+ RSpec.configure do |c|
794
+ c.default_trusted_external_data = {
795
+ 'foo' => 'bar'
796
+ }
797
+ end
798
+ ```
799
+
762
800
  #### Testing Exported Resources
763
801
 
764
802
  You can test if a resource was exported from the catalogue by using the
@@ -42,6 +42,7 @@ RSpec.configure do |c|
42
42
  c.add_setting :default_facts, :default => {}
43
43
  c.add_setting :default_node_params, :default => {}
44
44
  c.add_setting :default_trusted_facts, :default => {}
45
+ c.add_setting :default_trusted_external_data, :default => {}
45
46
  c.add_setting :hiera_config, :default => Puppet::Util::Platform.actually_windows? ? 'c:/nul/' : '/dev/null'
46
47
  c.add_setting :parser, :default => 'current'
47
48
  c.add_setting :trusted_node_data, :default => false
@@ -54,6 +55,10 @@ RSpec.configure do |c|
54
55
  c.add_setting :platform, :default => Puppet::Util::Platform.actual_platform
55
56
  c.add_setting :vendormoduledir, :default => Puppet::Util::Platform.actually_windows? ? 'c:/nul/' : '/dev/null'
56
57
  c.add_setting :basemodulepath, :default => Puppet::Util::Platform.actually_windows? ? 'c:/nul/' : '/dev/null'
58
+ c.add_setting :disable_module_hiera, :default => false
59
+ c.add_setting :fixture_hiera_configs, :default => {}
60
+ c.add_setting :use_fixture_spec_hiera, :default => false
61
+ c.add_setting :fallback_to_default_hiera, :default => true
57
62
 
58
63
  c.instance_eval do
59
64
  def trusted_server_facts
@@ -11,12 +11,17 @@ module RSpec::Puppet
11
11
  end
12
12
 
13
13
  def get(*args, &blk)
14
- # decouple the hash key from whatever the blk might do to it
15
14
  key = Marshal.load(Marshal.dump(args))
16
- if !@cache.has_key? key
17
- @cache[key] = (blk || @default_proc).call(*args)
18
- @lra << key
15
+ if @cache.has_key?(key)
16
+ # Cache hit
17
+ # move that entry last to make it "most recenty used"
18
+ @lra.insert(-1, @lra.delete_at(@lra.index(args)))
19
+ else
20
+ # Cache miss
21
+ # Ensure room by evicting least recently used if no space left
19
22
  expire!
23
+ @cache[args] = (blk || @default_proc).call(*args)
24
+ @lra << args
20
25
  end
21
26
 
22
27
  @cache[key]
@@ -25,8 +30,8 @@ module RSpec::Puppet
25
30
  private
26
31
 
27
32
  def expire!
28
- expired = @lra.slice!(0, @lra.size - MAX_ENTRIES)
29
- expired.each { |key| @cache.delete(key) } if expired
33
+ # delete one entry (the oldest) when there is no room in cache
34
+ @cache.delete(@lra.shift) if @cache.size == MAX_ENTRIES
30
35
  end
31
36
  end
32
37
  end
@@ -17,13 +17,25 @@ end
17
17
  module RSpec::Puppet
18
18
  class Coverage
19
19
 
20
- attr_accessor :filters
20
+ attr_accessor :filters, :filters_regex
21
21
 
22
22
  class << self
23
23
  extend Forwardable
24
- def_delegators(:instance, :add, :cover!, :report!,
25
- :filters, :add_filter, :add_from_catalog,
26
- :results)
24
+
25
+ delegated_methods = [
26
+ :instance,
27
+ :add,
28
+ :cover!,
29
+ :report!,
30
+ :filters,
31
+ :filters_regex,
32
+ :add_filter,
33
+ :add_filter_regex,
34
+ :add_from_catalog,
35
+ :results,
36
+ ]
37
+
38
+ def_delegators(*delegated_methods)
27
39
 
28
40
  attr_writer :instance
29
41
 
@@ -35,6 +47,7 @@ module RSpec::Puppet
35
47
  def initialize
36
48
  @collection = {}
37
49
  @filters = ['Stage[main]', 'Class[Settings]', 'Class[main]', 'Node[default]']
50
+ @filters_regex = []
38
51
  end
39
52
 
40
53
  def save_results
@@ -42,6 +55,9 @@ module RSpec::Puppet
42
55
  File.open(File.join(Dir.tmpdir, "rspec-puppet-filter-#{slug}"), 'w+') do |f|
43
56
  f.puts @filters.to_json
44
57
  end
58
+ File.open(File.join(Dir.tmpdir, "rspec-puppet-filter_regex-#{slug}"), 'w+') do |f|
59
+ f.puts @filters_regex.to_json
60
+ end
45
61
  File.open(File.join(Dir.tmpdir, "rspec-puppet-coverage-#{slug}"), 'w+') do |f|
46
62
  f.puts @collection.to_json
47
63
  end
@@ -57,10 +73,17 @@ module RSpec::Puppet
57
73
 
58
74
  def merge_filters
59
75
  pattern = File.join(Dir.tmpdir, "rspec-puppet-filter-#{Digest::MD5.hexdigest(Dir.pwd)}-*")
76
+ regex_filter_pattern = File.join(Dir.tmpdir, "rspec-puppet-filter_regex-#{Digest::MD5.hexdigest(Dir.pwd)}-*")
77
+
60
78
  Dir[pattern].each do |result_file|
61
79
  load_filters(result_file)
62
80
  FileUtils.rm(result_file)
63
81
  end
82
+
83
+ Dir[regex_filter_pattern].each do |result_file|
84
+ load_filters_regex(result_file)
85
+ FileUtils.rm(result_file)
86
+ end
64
87
  end
65
88
 
66
89
  def load_results(path)
@@ -79,6 +102,15 @@ module RSpec::Puppet
79
102
  end
80
103
  end
81
104
 
105
+ def load_filters_regex(path)
106
+ saved_regex_filters = JSON.parse(File.read(path))
107
+ saved_regex_filters.each do |pattern|
108
+ regex = Regexp.new(pattern)
109
+ @filters_regex << regex
110
+ @collection.delete_if { |resource, _| resource =~ regex }
111
+ end
112
+ end
113
+
82
114
  def add(resource)
83
115
  if !exists?(resource) && !filtered?(resource)
84
116
  @collection[resource.to_s] = ResourceWrapper.new(resource)
@@ -86,11 +118,8 @@ module RSpec::Puppet
86
118
  end
87
119
 
88
120
  def add_filter(type, title)
89
- def capitalize_name(name)
90
- name.split('::').map { |subtitle| subtitle.capitalize }.join('::')
91
- end
92
-
93
121
  type = capitalize_name(type)
122
+
94
123
  if type == 'Class'
95
124
  title = capitalize_name(title)
96
125
  end
@@ -98,6 +127,34 @@ module RSpec::Puppet
98
127
  @filters << "#{type}[#{title}]"
99
128
  end
100
129
 
130
+ def add_filter_regex(type, pattern)
131
+ raise ArgumentError.new('pattern argument must be a Regexp') unless pattern.is_a?(Regexp)
132
+
133
+ type = capitalize_name(type)
134
+
135
+ # avoid recompiling the regular expression during processing
136
+ src = pattern.source
137
+
138
+ # switch from anchors to wildcards since it is embedded into a larger pattern
139
+ src = if src.start_with?('\\A', '^')
140
+ src.gsub(/\A(?:\\A|\^)/, '')
141
+ else
142
+ # no anchor at the start
143
+ ".*#{src}"
144
+ end
145
+
146
+ # match an even number of backslashes before the anchor - this indicates that the anchor was not escaped
147
+ # note the necessity for the negative lookbehind `(?<!)` to assert that there is no backslash before this
148
+ src = if src.match(/(?<!\\)(\\\\)*(?:\\[zZ]|\$)\z/)
149
+ src.gsub(/(?:\\[zZ]|\$)\z/, '')
150
+ else
151
+ # no anchor at the end
152
+ "#{src}.*"
153
+ end
154
+
155
+ @filters_regex << /\A#{Regexp.escape(type)}\[#{src}\]\z/
156
+ end
157
+
101
158
  # add all resources from catalog declared in module test_module
102
159
  def add_from_catalog(catalog, test_module)
103
160
  coverable_resources = catalog.to_a.reject { |resource| !test_module.nil? && filter_resource?(resource, test_module) }
@@ -107,7 +164,10 @@ module RSpec::Puppet
107
164
  end
108
165
 
109
166
  def filtered?(resource)
110
- filters.include?(resource.to_s)
167
+ return true if filters.include?(resource.to_s)
168
+ return true if filters_regex.any? { |f| resource.to_s =~ f }
169
+
170
+ false
111
171
  end
112
172
 
113
173
  def cover!(resource)
@@ -136,7 +196,7 @@ module RSpec::Puppet
136
196
  end
137
197
 
138
198
  def run_report(coverage_desired = nil)
139
- if ENV['TEST_ENV_NUMBER']
199
+ if parallel_tests?
140
200
  merge_filters
141
201
  merge_results
142
202
  end
@@ -145,7 +205,7 @@ module RSpec::Puppet
145
205
 
146
206
  coverage_test(coverage_desired, report)
147
207
 
148
- puts report[:text]
208
+ puts "\n\nCoverage Report:\n\n#{report[:text]}"
149
209
  end
150
210
 
151
211
  def coverage_test(coverage_desired, report)
@@ -187,7 +247,9 @@ module RSpec::Puppet
187
247
  report[:total] = @collection.size
188
248
  report[:touched] = @collection.count { |_, resource| resource.touched? }
189
249
  report[:untouched] = report[:total] - report[:touched]
190
- report[:coverage] = "%5.2f" % ((report[:touched].to_f / report[:total].to_f) * 100)
250
+
251
+ coverage = report[:total].to_f > 0 ? ((report[:touched].to_f / report[:total].to_f) * 100) : 100.0
252
+ report[:coverage] = "%5.2f" % coverage
191
253
 
192
254
  report[:resources] = Hash[*@collection.map do |name, wrapper|
193
255
  [name, wrapper.to_hash]
@@ -226,7 +288,7 @@ module RSpec::Puppet
226
288
  # @param test_module [String] The name of the module under test
227
289
  # @return [true, false]
228
290
  def filter_resource?(resource, test_module)
229
- if @filters.include?(resource.to_s)
291
+ if filtered?(resource)
230
292
  return true
231
293
  end
232
294
 
@@ -267,6 +329,10 @@ module RSpec::Puppet
267
329
  !find(resource).nil?
268
330
  end
269
331
 
332
+ def capitalize_name(name)
333
+ name.split('::').map { |subtitle| subtitle.capitalize }.join('::')
334
+ end
335
+
270
336
  class ResourceWrapper
271
337
  attr_reader :resource
272
338
 
@@ -7,3 +7,4 @@ require 'rspec-puppet/matchers/dynamic_matchers'
7
7
  require 'rspec-puppet/matchers/type_matchers'
8
8
  require 'rspec-puppet/matchers/allow_value'
9
9
  require 'rspec-puppet/matchers/raise_error'
10
+ require 'rspec-puppet/matchers/unique_values'
@@ -89,6 +89,11 @@ module RSpec::Puppet
89
89
  else
90
90
  RSpec::Puppet::Coverage.cover!(resource)
91
91
  rsrc_hsh = resource.to_hash
92
+ if resource.respond_to?(:sensitive_parameters)
93
+ resource.sensitive_parameters.each do |s_param|
94
+ rsrc_hsh[s_param] = ::Puppet::Pops::Types::PSensitiveType::Sensitive.new(rsrc_hsh[s_param])
95
+ end
96
+ end
92
97
 
93
98
  if resource.builtin_type?
94
99
  namevar = resource.resource_type.key_attributes.first.to_s
@@ -21,10 +21,7 @@ module RSpec::Puppet
21
21
  #
22
22
  # @return [true, false]
23
23
  def matches?(resource)
24
-
25
- @resource = resource
26
-
27
- actual = @resource[@parameter]
24
+ actual = resource[@parameter]
28
25
  expected = @value
29
26
 
30
27
  # Puppet flattens an array with a single value into just the value and
@@ -65,6 +62,8 @@ module RSpec::Puppet
65
62
  check_hash(expected, actual)
66
63
  when Array
67
64
  check_array(expected, actual)
65
+ when RSpec::Puppet::Sensitive
66
+ expected == actual
68
67
  else
69
68
  check_string(expected, actual)
70
69
  end
@@ -16,8 +16,8 @@ module RSpec::Puppet
16
16
  end
17
17
  end
18
18
 
19
- def raise_error(*args, &block)
20
- RaiseError.new(*args, &block)
19
+ def raise_error(error=defined?(RSpec::Matchers::BuiltIn::RaiseError::UndefinedValue) ? RSpec::Matchers::BuiltIn::RaiseError::UndefinedValue : nil, message=nil, &block)
20
+ RaiseError.new(error, message, &block)
21
21
  end
22
22
  end
23
23
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module Puppet
5
+ module ManifestMatchers
6
+ extend RSpec::Matchers::DSL
7
+
8
+ matcher :have_unique_values_for_all do |type, attribute|
9
+ match do |catalogue|
10
+ catalogue.call.resources.select { |rsrc| rsrc.type == type.capitalize }
11
+ .group_by { |rsrc| rsrc[attribute.to_sym] }
12
+ .all? { |_, group| group.size == 1 }
13
+ end
14
+
15
+ description do
16
+ "have unique attribute values for #{type.capitalize}[#{attribute.to_sym}]"
17
+ end
18
+
19
+ if RSpec::Version::STRING < '3'
20
+ failure_message_for_should do |_actual|
21
+ "expected that the catalogue would have no duplicate values for #{type.capitalize}[#{attribute.to_sym}]"
22
+ end
23
+ else
24
+ failure_message do |_actual|
25
+ "expected that the catalogue would have no duplicate values for #{type.capitalize}[#{attribute.to_sym}]"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -268,6 +268,40 @@ module Puppet
268
268
  end
269
269
  end
270
270
  end
271
+
272
+ if Puppet::Util::Package.versioncmp(Puppet.version, '4.9.0') >= 0
273
+ class Module
274
+ old_hiera_conf_file = instance_method(:hiera_conf_file)
275
+ define_method(:hiera_conf_file) do
276
+ if RSpec::Puppet.rspec_puppet_example?
277
+ if RSpec.configuration.disable_module_hiera
278
+ return nil
279
+ elsif RSpec.configuration.fixture_hiera_configs.key?(name)
280
+ config = RSpec.configuration.fixture_hiera_configs[name]
281
+ config = File.absolute_path(config, path) unless config.nil?
282
+ return config
283
+ elsif RSpec.configuration.use_fixture_spec_hiera
284
+ config = RSpec::Puppet.current_example.fixture_spec_hiera_conf(self)
285
+ return config unless config.nil? && RSpec.configuration.fallback_to_default_hiera
286
+ end
287
+ end
288
+ old_hiera_conf_file.bind(self).call
289
+ end
290
+ end
291
+
292
+ class Pops::Lookup::ModuleDataProvider
293
+ old_configuration_path = instance_method(:configuration_path)
294
+ define_method(:configuration_path) do |lookup_invocation|
295
+ if RSpec::Puppet.rspec_puppet_example?
296
+ env = lookup_invocation.scope.environment
297
+ mod = env.module(module_name)
298
+ raise Puppet::DataBinding::LookupError, _("Environment '%{env}', cannot find module '%{module_name}'") % { :env => env.name, :module_name => module_name } unless mod
299
+ return Pathname.new(mod.hiera_conf_file)
300
+ end
301
+ old_configuration_path.bind(self).call(lookup_invocation)
302
+ end
303
+ end
304
+ end
271
305
  end
272
306
 
273
307
  class Pathname
@@ -302,6 +336,24 @@ class Pathname
302
336
  end
303
337
  end
304
338
 
339
+ # Puppet loads init.pp, then foo.pp, to find class "mod::foo". If
340
+ # class "mod" has been mocked using pre_condition when testing
341
+ # "mod::foo", this causes duplicate declaration for "mod".
342
+ # This monkey patch only loads "init.pp" if "foo.pp" does not exist.
343
+ class Puppet::Module
344
+ if [:match_manifests, 'match_manifests'].any? { |r| instance_methods.include?(r) }
345
+ old_match_manifests = instance_method(:match_manifests)
346
+
347
+ define_method(:match_manifests) do |rest|
348
+ result = old_match_manifests.bind(self).call(rest)
349
+ if result.length > 1 && File.basename(result[0]) == 'init.pp'
350
+ result.shift
351
+ end
352
+ result
353
+ end
354
+ end
355
+ end
356
+
305
357
  # Prevent the File type from munging paths (which uses File.expand_path to
306
358
  # normalise paths, which does very bad things to *nix paths on Windows.
307
359
  file_path_munge = Puppet::Type.type(:file).paramclass(:path).instance_method(:unsafe_munge)
@@ -0,0 +1,47 @@
1
+ module RSpec::Puppet
2
+ if defined?(::Puppet::Pops::Types::PSensitiveType::Sensitive)
3
+ # A wrapper representing Sensitive data type, eg. in class params.
4
+ class Sensitive < ::Puppet::Pops::Types::PSensitiveType::Sensitive
5
+ # Create a new Sensitive object
6
+ # @param [Object] value to wrap
7
+ def initialize(value)
8
+ @value = value
9
+ end
10
+
11
+ # @return the wrapped value
12
+ def unwrap
13
+ @value
14
+ end
15
+
16
+ # @return true
17
+ def sensitive?
18
+ true
19
+ end
20
+
21
+ # @return inspect of the wrapped value, inside Sensitive()
22
+ def inspect
23
+ "Sensitive(#{@value.inspect})"
24
+ end
25
+
26
+ # Check for equality with another value.
27
+ # If compared to Puppet Sensitive type, it compares the wrapped values.
28
+
29
+ # @param other [#unwrap, Object] value to compare to
30
+ def == other
31
+ if other.respond_to? :unwrap
32
+ return unwrap == other.unwrap
33
+ else
34
+ super
35
+ end
36
+ end
37
+ end
38
+ else
39
+ #:nocov:
40
+ class Sensitive
41
+ def initialize(value)
42
+ raise 'The use of the Sensitive data type is not supported by this Puppet version'
43
+ end
44
+ end
45
+ #:nocov:
46
+ end
47
+ end
@@ -1,12 +1,14 @@
1
1
  require 'rspec-puppet/cache'
2
2
  require 'rspec-puppet/adapters'
3
3
  require 'rspec-puppet/raw_string'
4
+ require 'rspec-puppet/sensitive'
4
5
 
5
6
  module RSpec::Puppet
6
7
  module Support
7
8
  include GenericMatchers
8
9
 
9
10
  @@cache = RSpec::Puppet::Cache.new
11
+ @@fixture_hiera_configs = Hash.new { |h, k| h[k] = nil }
10
12
 
11
13
  def subject
12
14
  lambda { catalogue }
@@ -86,10 +88,29 @@ module RSpec::Puppet
86
88
  hiera_config_value = self.respond_to?(:hiera_config) ? hiera_config : nil
87
89
  hiera_data_value = self.respond_to?(:hiera_data) ? hiera_data : nil
88
90
 
91
+ rspec_config_values = [
92
+ :trusted_server_facts,
93
+ :disable_module_hiera,
94
+ :use_fixture_spec_hiera,
95
+ :fixture_hiera_configs,
96
+ :fallback_to_default_hiera,
97
+ ].map { |setting| RSpec.configuration.send(setting) }
98
+
89
99
  build_facts = facts_hash(node_name)
90
- catalogue = build_catalog(node_name, build_facts, trusted_facts_hash(node_name), hiera_config_value,
91
- build_code(type, manifest_opts), exported, node_params_hash, hiera_data_value,
92
- RSpec.configuration.trusted_server_facts)
100
+ catalogue = build_catalog(
101
+ nodename: node_name,
102
+ facts_val: build_facts,
103
+ trusted_facts_val: trusted_facts_hash(node_name),
104
+ hiera_config_val: hiera_config_value,
105
+ code: build_code(type, manifest_opts),
106
+ exported: exported,
107
+ node_params: node_params_hash,
108
+ trusted_external_data: trusted_external_data_hash,
109
+ ignored_cache_params: {
110
+ hiera_data_value: hiera_data_value,
111
+ rspec_config_values: rspec_config_values,
112
+ }
113
+ )
93
114
 
94
115
  test_module = type == :host ? nil : class_name.split('::').first
95
116
  if type == :define
@@ -293,6 +314,19 @@ module RSpec::Puppet
293
314
  extensions
294
315
  end
295
316
 
317
+ def trusted_external_data_hash
318
+ return {} unless Puppet::Util::Package.versioncmp(Puppet.version, '6.14.0') >= 0
319
+
320
+ external_data = {}
321
+
322
+ if RSpec.configuration.default_trusted_external_data.any?
323
+ external_data.merge!(munge_facts(RSpec.configuration.default_trusted_external_data))
324
+ end
325
+
326
+ external_data.merge!(munge_facts(trusted_external_data)) if self.respond_to?(:trusted_external_data)
327
+ external_data
328
+ end
329
+
296
330
  def server_facts_hash
297
331
  server_facts = {}
298
332
 
@@ -380,6 +414,29 @@ module RSpec::Puppet
380
414
  end
381
415
 
382
416
  def build_catalog_without_cache(nodename, facts_val, trusted_facts_val, hiera_config_val, code, exported, node_params, *_)
417
+ build_catalog_without_cache_v2({
418
+ nodename: nodename,
419
+ facts_val: facts_val,
420
+ trusted_facts_val: trusted_facts_val,
421
+ hiera_config_val: hiera_config_val,
422
+ code: code,
423
+ exported: exported,
424
+ node_params: node_params,
425
+ trusted_external: {},
426
+ })
427
+ end
428
+
429
+ def build_catalog_without_cache_v2(
430
+ nodename: nil,
431
+ facts_val: nil,
432
+ trusted_facts_val: nil,
433
+ hiera_config_val: nil,
434
+ code: nil,
435
+ exported: nil,
436
+ node_params: nil,
437
+ trusted_external_data: nil,
438
+ ignored_cache_params: {}
439
+ )
383
440
 
384
441
  # If we're going to rebuild the catalog, we should clear the cached instance
385
442
  # of Hiera that Puppet is using. This opens the possibility of the catalog
@@ -401,10 +458,14 @@ module RSpec::Puppet
401
458
 
402
459
  node_obj = Puppet::Node.new(nodename, { :parameters => node_params, :facts => node_facts })
403
460
 
461
+ trusted_info = ['remote', nodename, trusted_facts_val]
462
+ if Puppet::Util::Package.versioncmp(Puppet.version, '6.14.0') >= 0
463
+ trusted_info.push(trusted_external_data)
464
+ end
404
465
  if Puppet::Util::Package.versioncmp(Puppet.version, '4.3.0') >= 0
405
466
  Puppet.push_context(
406
467
  {
407
- :trusted_information => Puppet::Context::TrustedInformation.new('remote', nodename, trusted_facts_val)
468
+ :trusted_information => Puppet::Context::TrustedInformation.new(*trusted_info)
408
469
  },
409
470
  "Context for spec trusted hash"
410
471
  )
@@ -423,7 +484,11 @@ module RSpec::Puppet
423
484
 
424
485
  def build_catalog(*args)
425
486
  @@cache.get(*args) do |*args|
426
- build_catalog_without_cache(*args)
487
+ if args.length == 1 && args.first.is_a?(Hash)
488
+ build_catalog_without_cache_v2(args.first)
489
+ else
490
+ build_catalog_without_cache(*args)
491
+ end
427
492
  end
428
493
  end
429
494
 
@@ -440,8 +505,7 @@ module RSpec::Puppet
440
505
  end
441
506
 
442
507
  def escape_special_chars(string)
443
- string.gsub!(/\$/, "\\$")
444
- string
508
+ string.gsub(/\$/, "\\$")
445
509
  end
446
510
 
447
511
  def rspec_compatibility
@@ -452,6 +516,23 @@ module RSpec::Puppet
452
516
  end
453
517
  end
454
518
 
519
+ def fixture_spec_hiera_conf(mod)
520
+ return @@fixture_hiera_configs[mod.name] if @@fixture_hiera_configs.key?(mod.name)
521
+
522
+ path = Pathname.new(mod.path)
523
+ if path.join('spec').exist?
524
+ path.join('spec').find do |file|
525
+ Find.prune if %w[modules work-dir].any? do |dir|
526
+ file.relative_path_from(path).to_s.start_with?("spec/fixtures/#{dir}")
527
+ end
528
+ if file.basename.to_s.eql?(Puppet::Pops::Lookup::HieraConfig::CONFIG_FILE_NAME)
529
+ return @@fixture_hiera_configs[mod.name] = file.to_s
530
+ end
531
+ end
532
+ end
533
+ @@fixture_hiera_configs[mod.name]
534
+ end
535
+
455
536
  # Helper to return a resource/node reference, so it gets translated in params to a raw string
456
537
  # without quotes.
457
538
  #
@@ -462,6 +543,14 @@ module RSpec::Puppet
462
543
  return RSpec::Puppet::RawString.new("#{type}['#{title}']")
463
544
  end
464
545
 
546
+ # Helper to return value wrapped in Sensitive type.
547
+ #
548
+ # @param [Object] value to wrap
549
+ # @return [RSpec::Puppet::Sensitive] a new Sensitive wrapper with the new value
550
+ def sensitive(value)
551
+ return RSpec::Puppet::Sensitive.new(value)
552
+ end
553
+
465
554
  # @!attribute [r] adapter
466
555
  # @api private
467
556
  # @return [Class < RSpec::Puppet::Adapters::Base]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-puppet
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.10
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Sharpe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-15 00:00:00.000000000 Z
11
+ date: 2020-11-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -60,12 +60,14 @@ files:
60
60
  - lib/rspec-puppet/matchers/raise_error.rb
61
61
  - lib/rspec-puppet/matchers/run.rb
62
62
  - lib/rspec-puppet/matchers/type_matchers.rb
63
+ - lib/rspec-puppet/matchers/unique_values.rb
63
64
  - lib/rspec-puppet/monkey_patches.rb
64
65
  - lib/rspec-puppet/monkey_patches/win32/registry.rb
65
66
  - lib/rspec-puppet/monkey_patches/win32/taskscheduler.rb
66
67
  - lib/rspec-puppet/monkey_patches/windows/taskschedulerconstants.rb
67
68
  - lib/rspec-puppet/rake_task.rb
68
69
  - lib/rspec-puppet/raw_string.rb
70
+ - lib/rspec-puppet/sensitive.rb
69
71
  - lib/rspec-puppet/setup.rb
70
72
  - lib/rspec-puppet/spec_helper.rb
71
73
  - lib/rspec-puppet/support.rb
@@ -89,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
91
  - !ruby/object:Gem::Version
90
92
  version: '0'
91
93
  requirements: []
92
- rubygems_version: 3.0.3
94
+ rubygems_version: 3.0.8
93
95
  signing_key:
94
96
  specification_version: 4
95
97
  summary: RSpec tests for your Puppet manifests