summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLuke Kanies <luke@madstop.com>2008-05-15 14:18:26 -0500
committerLuke Kanies <luke@madstop.com>2008-05-15 14:18:26 -0500
commitbe0a8031fbf8e4a2d608ab600c37c1b01dec16a1 (patch)
tree7f6ba30967b1b18fcdecd45c4de0b89c9c895249
parentcc9e2217fd62f02665da84489dc88be6559d2909 (diff)
downloadfacter-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.rb127
-rwxr-xr-xspec/unit/loader.rb190
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