class Puppet::Util::Feature
Attributes
Public Class Methods
Create a new feature collection.
# File lib/puppet/util/feature.rb 64 def initialize(path) 65 @path = path 66 @results = {} 67 @loader = Puppet::Util::Autoload.new(self, @path) 68 end
Public Instance Methods
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 42 def add(name, options = {}, &block) 43 method = name.to_s + "?" 44 @results.delete(name) 45 46 meta_def(method) do 47 # we return a cached result if: 48 # * if we've tested this feature before 49 # AND 50 # * the result was true/false 51 # OR 52 # * we're configured to never retry 53 if @results.has_key?(name) && 54 (!@results[name].nil? || !Puppet[:always_retry_plugins]) 55 !!@results[name] 56 else 57 @results[name] = test(name, options, &block) 58 !!@results[name] 59 end 60 end 61 end
# File lib/puppet/util/feature.rb 70 def load 71 @loader.loadall(Puppet.lookup(:current_environment)) 72 end
# File lib/puppet/util/feature.rb 74 def method_missing(method, *args) 75 return super unless method.to_s =~ /\?$/ 76 77 feature = method.to_s.sub(/\?$/, '') 78 @loader.load(feature, Puppet.lookup(:current_environment)) 79 80 respond_to?(method) && self.send(method) 81 end
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 86 def test(name, options, &block) 87 if block_given? 88 begin 89 result = yield 90 rescue StandardError,ScriptError => detail 91 warn _("Failed to load feature test for %{name}: %{detail}") % { name: name, detail: detail } 92 result = nil 93 end 94 @results[name] = result 95 result 96 else 97 libs = options[:libs] 98 if libs 99 libs = [libs] unless libs.is_a?(Array) 100 libs.all? { |lib| load_library(lib, name) } ? true : nil 101 else 102 true 103 end 104 end 105 end
Private Instance Methods
# File lib/puppet/util/feature.rb 109 def load_library(lib, name) 110 raise ArgumentError, _("Libraries must be passed as strings not %{klass}") % { klass: lib.class } unless lib.is_a?(String) 111 112 @rubygems ||= Puppet::Util::RubyGems::Source.new 113 @rubygems.clear_paths 114 115 begin 116 require lib 117 true 118 rescue LoadError 119 # Expected case. Required library insn't installed. 120 debug_once(_("Could not find library '%{lib}' required to enable feature '%{name}'") % 121 {lib: lib, name: name}) 122 false 123 rescue StandardError, ScriptError => detail 124 debug_once(_("Exception occurred while loading library '%{lib}' required to enable feature '%{name}': %{detail}") % 125 {lib: lib, name: name, detail: detail}) 126 false 127 end 128 end