class Puppet::Util::Feature

Attributes

path[R]

Public Class Methods

new(path) click to toggle source

Create a new feature collection.

   # File lib/puppet/util/feature.rb
65 def initialize(path)
66   @path = path
67   @results = {}
68   @loader = Puppet::Util::Autoload.new(self, @path)
69 end

Public Instance Methods

add(name, options = {}, &block) click to toggle source

Create a new feature test. You have to pass the feature name, and it must be unique. You can pass a block to determine if the feature is present:

Puppet.features.add(:myfeature) do
  # return true or false if feature is available
  # return nil if feature may become available later
end

The block should return true if the feature is available, false if it is not, or nil if the state is unknown. True and false values will be cached. A nil value will not be cached, and should be used if the feature may become true in the future.

Features are often used to detect if a ruby library is installed. To support that common case, you can pass one or more ruby libraries, and the feature will be true if all of the libraries load successfully:

Puppet.features.add(:myfeature, libs: 'mylib')
Puppet.features.add(:myfeature, libs: ['mylib', 'myotherlib'])

If the ruby library is not installed, then the failure is not cached, as it's assumed puppet may install the gem during catalog application.

If a feature is defined using `:libs` and a block, then the block is used and the `:libs` are ignored.

Puppet evaluates the feature test when the `Puppet.features.myfeature?` method is called. If the feature test was defined using a block and the block returns nil, then the feature test will be re-evaluated the next time `Puppet.features.myfeature?` is called.

@param [Symbol] name The unique feature name @param [Hash<Symbol,Array<String>>] options The libraries to load

   # File lib/puppet/util/feature.rb
43 def add(name, options = {}, &block)
44   method = name.to_s + "?"
45   @results.delete(name)
46 
47   meta_def(method) do
48     # we return a cached result if:
49     #  * if we've tested this feature before
50     #  AND
51     #    * the result was true/false
52     #    OR
53     #    * we're configured to never retry
54     if @results.has_key?(name) &&
55        (!@results[name].nil? || !Puppet[:always_retry_plugins])
56       !!@results[name]
57     else
58       @results[name] = test(name, options, &block)
59       !!@results[name]
60     end
61   end
62 end
load() click to toggle source
   # File lib/puppet/util/feature.rb
71 def load
72   @loader.loadall(Puppet.lookup(:current_environment))
73 end
method_missing(method, *args) click to toggle source
Calls superclass method
   # File lib/puppet/util/feature.rb
75 def method_missing(method, *args)
76   return super unless method.to_s =~ /\?$/
77 
78   feature = method.to_s.sub(/\?$/, '')
79   @loader.load(feature, Puppet.lookup(:current_environment))
80 
81   respond_to?(method) && self.send(method)
82 end
test(name, options) { || ... } click to toggle source

Actually test whether the feature is present. We only want to test when someone asks for the feature, so we don't unnecessarily load files.

    # File lib/puppet/util/feature.rb
 87 def test(name, options, &block)
 88   if block_given?
 89     begin
 90       result = yield
 91     rescue StandardError,ScriptError => detail
 92       warn _("Failed to load feature test for %{name}: %{detail}") % { name: name, detail: detail }
 93       result = nil
 94     end
 95     @results[name] = result
 96     result
 97   else
 98     libs = options[:libs]
 99     if libs
100       libs = [libs] unless libs.is_a?(Array)
101       libs.all? { |lib| load_library(lib, name) } ? true : nil
102     else
103       true
104     end
105   end
106 end

Private Instance Methods

load_library(lib, name) click to toggle source
    # File lib/puppet/util/feature.rb
110 def load_library(lib, name)
111   raise ArgumentError, _("Libraries must be passed as strings not %{klass}") % { klass: lib.class } unless lib.is_a?(String)
112 
113   @rubygems ||= Puppet::Util::RubyGems::Source.new
114   @rubygems.clear_paths
115 
116   begin
117     require lib
118     true
119   rescue LoadError
120     # Expected case. Required library insn't installed.
121     debug_once(_("Could not find library '%{lib}' required to enable feature '%{name}'") %
122       {lib: lib, name: name})
123     false
124   rescue StandardError, ScriptError => detail
125     debug_once(_("Exception occurred while loading library '%{lib}' required to enable feature '%{name}': %{detail}") %
126       {lib: lib, name: name, detail: detail})
127     false
128   end
129 end