class Puppet::Util::Feature
Attributes
Public Class Methods
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
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
# File lib/puppet/util/feature.rb 71 def load 72 @loader.loadall(Puppet.lookup(:current_environment)) 73 end
# 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
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
# 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