rspec-puppet 2.7.10 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
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