File: adapters.rb

package info (click to toggle)
ruby-rspec-puppet 4.0.2%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,444 kB
  • sloc: ruby: 6,377; makefile: 6
file content (197 lines) | stat: -rw-r--r-- 7,308 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# frozen_string_literal: true

require 'rspec-puppet/facter_impl'

module RSpec::Puppet
  module Adapters
    class Base
      # Set up all Puppet settings applicable for this Puppet version as
      # application defaults.
      #
      # Puppet setting values can be taken from the global RSpec configuration, or from the currently
      # executing RSpec context. When a setting is specified both in the global configuration and in
      # the example group, the setting in the example group is preferred.
      #
      # @example Configuring a Puppet setting from within an RSpec example group
      #   RSpec.describe 'my_module::my_class', :type => :class do
      #     let(:module_path) { "/Users/luke/modules" }
      #     #=> Puppet[:modulepath] will be "/Users/luke/modules"
      #   end
      #
      # @example Configuring a Puppet setting with both a global RSpec configuration and local context
      #   RSpec.configure do |config|
      #     config.confdir = "/etc/puppet"
      #   end
      #   RSpec.describe 'my_module', :type => :class do
      #     # Puppet[:confdir] will be "/etc/puppet"
      #   end
      #   RSpec.describe 'my_module::my_class', :type => :class do
      #     let(:confdir) { "/etc/puppetlabs/puppet" }
      #     # => Puppet[:confdir] will be "/etc/puppetlabs/puppet" in this example group
      #   end
      #   RSpec.describe 'my_module::my_define', :type => :define do
      #     # Puppet[:confdir] will be "/etc/puppet" again
      #   end
      #
      # @param example_group [RSpec::Core::ExampleGroup] The RSpec context to use for local settings
      # @return [void]
      def setup_puppet(example_group)
        case RSpec.configuration.facter_implementation.to_sym
        when :rspec
          # Lazily instantiate FacterTestImpl here to optimize memory
          # allocation, as the proc will only be called if FacterImpl is unset
          set_facter_impl(proc { RSpec::Puppet::FacterTestImpl.new })
        when :facter
          set_facter_impl(Facter)
        else
          raise "Unsupported facter_implementation '#{RSpec.configuration.facter_implementation}'"
        end

        Puppet.runtime[:facter] = FacterImpl

        settings = settings_map.map do |puppet_setting, rspec_setting|
          [puppet_setting, get_setting(example_group, rspec_setting)]
        end.flatten
        default_hash = { confdir: '/dev/null', vardir: '/dev/null' }
        if defined?(Puppet::Test::TestHelper) && Puppet::Test::TestHelper.respond_to?(:app_defaults_for_tests, true)
          default_hash.merge!(Puppet::Test::TestHelper.send(:app_defaults_for_tests))
        end
        settings_hash = default_hash.merge(Hash[*settings])
        if Gem.win_platform?
          settings_hash.each_with_object(settings_hash) do |(k, v), h|
            h[k] = v == '/dev/null' ? 'c:/nul/' : v
          end
        end

        Puppet.settings.initialize_app_defaults(settings_hash)

        # Forcefully apply the environmentpath setting instead of relying on
        # the application defaults as Puppet::Test::TestHelper automatically
        # sets this value as well, overriding our application default
        Puppet.settings[:environmentpath] = settings_hash[:environmentpath] if settings_hash.key?(:environmentpath)

        @environment_name = example_group.environment

        modulepath = if (rspec_modulepath = RSpec.configuration.module_path)
                       rspec_modulepath.split(File::PATH_SEPARATOR)
                     else
                       Puppet[:environmentpath].split(File::PATH_SEPARATOR).map do |path|
                         File.join(path, 'fixtures', 'modules')
                       end
                     end

        if (rspec_manifest = RSpec.configuration.manifest)
          manifest = rspec_manifest
        else
          manifest_paths = Puppet[:environmentpath].split(File::PATH_SEPARATOR).map do |path|
            File.join(path, 'fixtures', 'manifests')
          end

          manifest = manifest_paths.find do |path|
            File.exist?(path)
          end

          manifest ||= Puppet::Node::Environment::NO_MANIFEST
        end

        env = Puppet::Node::Environment.create(@environment_name, modulepath, manifest)
        loader = Puppet::Environments::Static.new(env)

        Puppet.push_context(
          {
            environments: loader,
            current_environment: env,
            loaders: Puppet::Pops::Loaders.new(env)
          },
          'Setup rspec-puppet environments'
        )
      end

      def get_setting(example_group, rspec_setting)
        if example_group.respond_to?(rspec_setting)
          example_group.send(rspec_setting)
        else
          RSpec.configuration.send(rspec_setting)
        end
      end

      def catalog(node, exported)
        node.environment = current_environment
        # Override $::environment to workaround PUP-5835, where Puppet otherwise
        # stores a symbol for the parameter
        if node.parameters['environment'] != node.parameters['environment'].to_s
          node.parameters['environment'] = current_environment.name.to_s
        end

        catalog = if exported
                    # Use the compiler directly to skip the filtering done by the indirector
                    Puppet::Parser::Compiler.compile(node).filter { |r| !r.exported? }
                  else
                    Puppet::Resource::Catalog.indirection.find(node.name, use_node: node)
                  end

        Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(node.facts, catalog)

        catalog
      end

      def current_environment
        Puppet::Node::Environment.new(@environment_name)
      end

      def settings_map
        [
          %i[modulepath module_path],
          %i[basemodulepath basemodulepath],
          %i[config config],
          %i[confdir confdir],
          %i[environmentpath environmentpath],
          %i[hiera_config hiera_config],
          %i[strict_variables strict_variables],
          %i[vendormoduledir vendormoduledir]
        ]
      end

      def current_environment
        Puppet.lookup(:current_environment)
      end

      def modulepath
        current_environment.modulepath
      end

      # @return [String, nil] The path to the Puppet manifest if it is present and set, nil otherwise.
      def manifest
        m = current_environment.manifest
        if m == Puppet::Node::Environment::NO_MANIFEST
          nil
        else
          m
        end
      end

      # @api private
      #
      # Set the FacterImpl constant to the given Facter implementation or noop
      # if the constant is already set. If a proc is given, it will only be
      # called if FacterImpl is not defined.
      #
      # @param impl [Object, Proc] An object or a proc that implements the Facter API
      def set_facter_impl(impl)
        return if defined?(FacterImpl)

        impl = impl.call if impl.is_a?(Proc)
        Object.send(:const_set, :FacterImpl, impl)
      end
    end

    def self.get
      [
        ['7.11', Base]
      ].each do |(version, klass)|
        return klass.new if Puppet::Util::Package.versioncmp(Puppet.version, version) >= 0
      end
      raise "Puppet version #{Puppet.version} is not supported."
    end
  end
end