diff options
author | Luke Kanies <luke@madstop.com> | 2008-05-15 14:18:26 -0500 |
---|---|---|
committer | Luke Kanies <luke@madstop.com> | 2008-05-15 14:18:26 -0500 |
commit | be0a8031fbf8e4a2d608ab600c37c1b01dec16a1 (patch) | |
tree | 7f6ba30967b1b18fcdecd45c4de0b89c9c895249 | |
parent | cc9e2217fd62f02665da84489dc88be6559d2909 (diff) | |
download | facter-be0a8031fbf8e4a2d608ab600c37c1b01dec16a1.tar.gz facter-be0a8031fbf8e4a2d608ab600c37c1b01dec16a1.tar.xz facter-be0a8031fbf8e4a2d608ab600c37c1b01dec16a1.zip |
Creating a 'loader' class to handle loading facts for the collection.
-rw-r--r-- | lib/facter/loader.rb | 127 | ||||
-rwxr-xr-x | spec/unit/loader.rb | 190 |
2 files changed, 317 insertions, 0 deletions
diff --git a/lib/facter/loader.rb b/lib/facter/loader.rb new file mode 100644 index 0000000..3284cc4 --- /dev/null +++ b/lib/facter/loader.rb @@ -0,0 +1,127 @@ +require 'facter' + +# Load facts on demand. +class Facter::Loader + # Load all resolutions for a single fact. + def load(fact) + # Now load from the search path + shortname = fact.to_s.downcase + load_env(shortname) + + filename = shortname + ".rb" + search_path.each do |dir| + # Load individual files + file = File.join(dir, filename) + + # We have to specify Kernel.load, because we have a load method. + Kernel.load(file) if FileTest.exist?(file) + + # And load any directories matching the name + factdir = File.join(dir, shortname) + load_dir(factdir) if FileTest.directory?(factdir) + end + end + + # Load all facts from all directories. + def load_all + load_env + + search_path.each do |dir| + Dir.entries(dir).each do |file| + path = File.join(dir, file) + if File.directory?(path) + load_dir(path) + elsif file =~ /\.rb$/ + Kernel.load(File.join(dir, file)) + end + end + end + end + + # The list of directories we're going to search through for facts. + def search_path + result = [] + result += $LOAD_PATH.collect { |d| File.join(d, "facter") } + if ENV.include?("FACTERLIB") + result += ENV["FACTERLIB"].split(":") + end + + if defined?(Puppet) + result << Puppet.settings.value(:factdest) + result << File.join(Puppet.settings.value(:libdir), "facter") + end + result + end + + def old_stuff + # See if we can find any other facts in the regular Ruby lib + # paths + $:.each do |dir| + fdir = File.join(dir, "facter") + if FileTest.exists?(fdir) and FileTest.directory?(fdir) + factdirs.push(fdir) + end + end + # Also check anything in 'FACTERLIB' + if ENV['FACTERLIB'] + ENV['FACTERLIB'].split(":").each do |fdir| + factdirs.push(fdir) + end + end + factdirs.each do |fdir| + Dir.glob("#{fdir}/*.rb").each do |file| + # Load here, rather than require, because otherwise + # the facts won't get reloaded if someone calls + # "loadfacts". Really only important in testing, but, + # well, it's important in testing. + begin + load file + rescue => detail + warn "Could not load %s: %s" % + [file, detail] + end + end + end + + + # Now try to get facts from the environment + ENV.each do |name, value| + if name =~ /^facter_?(\w+)$/i + Facter.add($1) do + setcode { value } + end + end + end + end + + private + + def load_dir(dir) + return if dir =~ /\/util$/ + Dir.entries(dir).find_all { |f| f =~ /\.rb$/ }.each do |file| + Kernel.load(File.join(dir, file)) + end + end + + # Load facts from the environment. If no name is provided, + # all will be loaded. + def load_env(fact = nil) + # Load from the environment, if possible + ENV.each do |name, value| + # Skip anything that doesn't match our regex. + next unless name =~ /^facter_?(\w+)$/i + env_name = $1 + + # If a fact name was specified, skip anything that doesn't + # match it. + next if fact and env_name != fact + + Facter.add($1) do + setcode { value } + end + + # Short-cut, if we are only looking for one value. + break if fact + end + end +end diff --git a/spec/unit/loader.rb b/spec/unit/loader.rb new file mode 100755 index 0000000..75146e1 --- /dev/null +++ b/spec/unit/loader.rb @@ -0,0 +1,190 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../spec_helper' + +require 'facter/loader' + +# Make sure we have a Puppet constant, so we can test +# loading Puppet facts. +unless defined?(Puppet) + class Puppet; end +end + +describe Facter::Loader do + def with_env(values) + old = {} + values.each do |var, value| + if old_val = ENV[var] + old[var] = old_val + end + ENV[var] = value + end + yield + values.each do |var, value| + if old.include?(var) + ENV[var] = old[var] + else + ENV.delete(var) + end + end + end + + it "should have a method for loading individual facts by name" do + Facter::Loader.new.should respond_to(:load) + end + + it "should have a method for loading all facts" do + Facter::Loader.new.should respond_to(:load_all) + end + + it "should have a method for returning directories containing facts" do + Facter::Loader.new.should respond_to(:search_path) + end + + describe "when determining the search path" do + before do + @loader = Facter::Loader.new + @settings = mock 'settings' + @settings.stubs(:value).returns "/eh" + Puppet.stubs(:settings).returns @settings + end + + it "should include the facter subdirectory of all paths in ruby LOAD_PATH" do + dirs = $LOAD_PATH.collect { |d| File.join(d, "facter") } + paths = @loader.search_path + + dirs.each do |dir| + paths.should be_include(dir) + end + end + + describe "and the FACTERLIB environment variable is set" do + it "should include all paths in FACTERLIB" do + with_env "FACTERLIB" => "/one/path:/two/path" do + paths = @loader.search_path + %w{/one/path /two/path}.each do |dir| + paths.should be_include(dir) + end + end + end + end + + describe "and the Puppet libraries are loaded" do + it "should include the factdest setting" do + @settings.expects(:value).with(:factdest).returns "/my/facts" + @loader.search_path.should be_include("/my/facts") + end + + it "should include the facter subdirectory of the libdir setting" do + @settings.expects(:value).with(:libdir).returns "/lib/dir" + @loader.search_path.should be_include("/lib/dir/facter") + end + end + end + + describe "when loading facts" do + before do + @loader = Facter::Loader.new + @loader.stubs(:search_path).returns [] + end + + it "should load values from the matching environment variable if one is present" do + Facter.expects(:add).with("testing") + + with_env "facter_testing" => "yayness" do + @loader.load(:testing) + end + end + + it "should load any files in the search path with names matching the fact name" do + @loader.expects(:search_path).returns %w{/one/dir /two/dir} + FileTest.stubs(:exist?).returns false + FileTest.expects(:exist?).with("/one/dir/testing.rb").returns true + FileTest.expects(:exist?).with("/two/dir/testing.rb").returns true + + Kernel.expects(:load).with("/one/dir/testing.rb") + Kernel.expects(:load).with("/two/dir/testing.rb") + + @loader.load(:testing) + end + + it "should load any ruby files in directories matching the fact name in the search path" do + @loader.expects(:search_path).returns %w{/one/dir} + FileTest.stubs(:exist?).returns false + FileTest.expects(:directory?).with("/one/dir/testing").returns true + + Dir.expects(:entries).with("/one/dir/testing").returns %w{two.rb} + + Kernel.expects(:load).with("/one/dir/testing/two.rb") + + @loader.load(:testing) + end + + it "should not load files that don't end in '.rb'" do + @loader.expects(:search_path).returns %w{/one/dir} + FileTest.stubs(:exist?).returns false + FileTest.expects(:directory?).with("/one/dir/testing").returns true + + Dir.expects(:entries).with("/one/dir/testing").returns %w{one} + + Kernel.expects(:load).never + + @loader.load(:testing) + end + end + + describe "when loading all facts" do + before do + @loader = Facter::Loader.new + @loader.stubs(:search_path).returns [] + end + + it "should load all files in all search paths" do + @loader.expects(:search_path).returns %w{/one/dir /two/dir} + + Dir.expects(:entries).with("/one/dir").returns %w{a.rb b.rb} + Dir.expects(:entries).with("/two/dir").returns %w{c.rb d.rb} + + %w{/one/dir/a.rb /one/dir/b.rb /two/dir/c.rb /two/dir/d.rb}.each { |f| Kernel.expects(:load).with(f) } + + @loader.load_all + end + + it "should load all files in all subdirectories in all search paths" do + @loader.expects(:search_path).returns %w{/one/dir /two/dir} + + Dir.expects(:entries).with("/one/dir").returns %w{a} + Dir.expects(:entries).with("/two/dir").returns %w{b} + + %w{/one/dir/a /two/dir/b}.each { |f| File.expects(:directory?).with(f).returns true } + + Dir.expects(:entries).with("/one/dir/a").returns %w{c.rb} + Dir.expects(:entries).with("/two/dir/b").returns %w{d.rb} + + %w{/one/dir/a/c.rb /two/dir/b/d.rb}.each { |f| Kernel.expects(:load).with(f) } + + @loader.load_all + end + + it "should not load files in the util subdirectory" do + @loader.expects(:search_path).returns %w{/one/dir} + + Dir.expects(:entries).with("/one/dir").returns %w{util} + + File.expects(:directory?).with("/one/dir/util").returns true + + Dir.expects(:entries).with("/one/dir/util").never + + @loader.load_all + end + + it "should load all facts from the environment" do + Facter.expects(:add).with('one') + Facter.expects(:add).with('two') + + with_env "facter_one" => "yayness", "facter_two" => "boo" do + @loader.load_all + end + end + end +end |