diff options
-rw-r--r-- | CHANGELOG | 10 | ||||
-rw-r--r-- | Modulefile | 2 | ||||
-rw-r--r-- | README.markdown | 9 | ||||
-rw-r--r-- | lib/facter/facter_dot_d.rb | 183 | ||||
-rw-r--r-- | lib/facter/root_home.rb | 17 | ||||
-rw-r--r-- | spec/spec_helper.rb | 1 | ||||
-rw-r--r-- | spec/unit/facter/root_home_spec.rb | 42 |
7 files changed, 263 insertions, 1 deletions
@@ -1,3 +1,13 @@ +2011-08-18 Puppet Labs <support@puppetlabs.com> - 2.1.1 +* Change facts.d paths to match Facter 2.0 paths. +* /etc/facter/facts.d +* /etc/puppetlabs/facter/facts.d + +2011-08-17 Puppet Labs <support@puppetlabs.com> - 2.1.0 +* Add R.I. Pienaar's facts.d custom facter fact +* facts defined in /etc/facts.d and /etc/puppetlabs/facts.d are + automatically loaded now. + 2011-08-04 Puppet Labs <support@puppetlabs.com> - 2.0.0 * Rename whole_line to file_line * This is an API change and as such motivating a 2.0.0 release according to semver.org. @@ -1,5 +1,5 @@ name 'puppetlabs-stdlib' -version '2.0.0' +version '2.1.1' source 'git://github.com/puppetlabs/puppetlabs-stdlib' author 'puppetlabs' license 'Apache 2.0' diff --git a/README.markdown b/README.markdown index 68186c2..4f06651 100644 --- a/README.markdown +++ b/README.markdown @@ -49,3 +49,12 @@ before the function is used. $namespace = 'site::data' include "${namespace}" $myvar = getvar("${namespace}::myvar") + +## Facts ## + +Facts in `/etc/facter/facts.d` and `/etc/puppetlabs/facter/facts.d` are now loaded +automatically. This is a direct copy of R.I. Pienaar's custom facter fact +located at: +[https://github.com/ripienaar/facter-facts/tree/master/facts-dot-d](https://github.com/ripienaar/facter-facts/tree/master/facts-dot-d) + + diff --git a/lib/facter/facter_dot_d.rb b/lib/facter/facter_dot_d.rb new file mode 100644 index 0000000..90586a9 --- /dev/null +++ b/lib/facter/facter_dot_d.rb @@ -0,0 +1,183 @@ +# A Facter plugin that loads facts from /etc/facts.d. +# +# Facts can be in the form of JSON, YAML or Text files +# and any executable that returns key=value pairs. +# +# In the case of scripts you can also create a file that +# contains a cache TTL. For foo.sh store the ttl as just +# a number in foo.sh.ttl +# +# The cache is stored in /tmp/facts_cache.yaml as a mode +# 600 file and will have the end result of not calling your +# fact scripts more often than is needed +class Facter::Util::DotD + require 'yaml' + + def initialize(dir="/etc/facts.d", cache_file="/tmp/facts_cache.yml") + @dir = dir + @cache_file = cache_file + @cache = nil + @types = {".txt" => :txt, ".json" => :json, ".yaml" => :yaml} + end + + def entries + Dir.entries(@dir).reject{|f| f =~ /^\.|\.ttl$/}.sort.map {|f| File.join(@dir, f) } + rescue + [] + end + + def fact_type(file) + extension = File.extname(file) + + type = @types[extension] || :unknown + + type = :script if type == :unknown && File.executable?(file) + + return type + end + + def txt_parser(file) + File.readlines(file).each do |line| + if line =~ /^(.+)=(.+)$/ + var = $1; val = $2 + + Facter.add(var) do + setcode { val } + end + end + end + rescue Exception => e + Facter.warn("Failed to handle #{file} as text facts: #{e.class}: #{e}") + end + + def json_parser(file) + begin + require 'json' + rescue LoadError + require 'rubygems' + retry + end + + JSON.load(File.read(file)).each_pair do |f, v| + Facter.add(f) do + setcode { v } + end + end + rescue Exception => e + Facter.warn("Failed to handle #{file} as json facts: #{e.class}: #{e}") + end + + def yaml_parser(file) + require 'yaml' + + YAML.load_file(file).each_pair do |f, v| + Facter.add(f) do + setcode { v } + end + end + rescue Exception => e + Facter.warn("Failed to handle #{file} as yaml facts: #{e.class}: #{e}") + end + + def script_parser(file) + result = cache_lookup(file) + ttl = cache_time(file) + + unless result + result = Facter::Util::Resolution.exec(file) + + if ttl > 0 + Facter.debug("Updating cache for #{file}") + cache_store(file, result) + cache_save! + end + else + Facter.debug("Using cached data for #{file}") + end + + result.split("\n").each do |line| + if line =~ /^(.+)=(.+)$/ + var = $1; val = $2 + + Facter.add(var) do + setcode { val } + end + end + end + rescue Exception => e + Facter.warn("Failed to handle #{file} as script facts: #{e.class}: #{e}") + Facter.debug(e.backtrace.join("\n\t")) + end + + def cache_save! + cache = load_cache + File.open(@cache_file, "w", 0600) {|f| f.write(YAML.dump(cache)) } + rescue + end + + def cache_store(file, data) + load_cache + + @cache[file] = {:data => data, :stored => Time.now.to_i} + rescue + end + + def cache_lookup(file) + cache = load_cache + + return nil if cache.empty? + + ttl = cache_time(file) + + if cache[file] + now = Time.now.to_i + + return cache[file][:data] if ttl == -1 + return cache[file][:data] if (now - cache[file][:stored]) <= ttl + return nil + else + return nil + end + rescue + return nil + end + + def cache_time(file) + meta = file + ".ttl" + + return File.read(meta).chomp.to_i + rescue + return 0 + end + + def load_cache + unless @cache + if File.exist?(@cache_file) + @cache = YAML.load_file(@cache_file) + else + @cache = {} + end + end + + return @cache + rescue + @cache = {} + return @cache + end + + def create + entries.each do |fact| + type = fact_type(fact) + parser = "#{type}_parser" + + if respond_to?("#{type}_parser") + Facter.debug("Parsing #{fact} using #{parser}") + + send(parser, fact) + end + end + end +end + +Facter::Util::DotD.new("/etc/facter/facts.d").create +Facter::Util::DotD.new("/etc/puppetlabs/facter/facts.d").create diff --git a/lib/facter/root_home.rb b/lib/facter/root_home.rb new file mode 100644 index 0000000..61fcf39 --- /dev/null +++ b/lib/facter/root_home.rb @@ -0,0 +1,17 @@ +# A facter fact to determine the root home directory. +# This varies on PE supported platforms and may be +# reconfigured by the end user. + +module Facter::Util::RootHome + class << self + def get_root_home + root_ent = Facter::Util::Resolution.exec("getent passwd root") + # The home directory is the sixth element in the passwd entry + root_ent.split(":")[5] + end + end +end + +Facter.add(:root_home) do + setcode { Facter::Util::RootHome.get_root_home } +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index fdc04bc..fc19cef 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,6 +7,7 @@ p dir ARGV.clear require 'puppet' +require 'facter' require 'mocha' gem 'rspec', '>=2.0.0' require 'rspec/expectations' diff --git a/spec/unit/facter/root_home_spec.rb b/spec/unit/facter/root_home_spec.rb new file mode 100644 index 0000000..8946d9d --- /dev/null +++ b/spec/unit/facter/root_home_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' +require 'facter/root_home' + +describe Facter::Util::RootHome do + context "solaris" do + let(:root_ent) { "root:x:0:0:Super-User:/:/sbin/sh" } + let(:expected_root_home) { "/" } + + it "should return /" do + Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(root_ent) + Facter::Util::RootHome.get_root_home.should == expected_root_home + end + end + context "linux" do + let(:root_ent) { "root:x:0:0:root:/root:/bin/bash" } + let(:expected_root_home) { "/root" } + + it "should return /root" do + Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(root_ent) + Facter::Util::RootHome.get_root_home.should == expected_root_home + end + end + context "macosx" do + let(:root_ent) { "root:*:0:0:System Administrator:/var/root:/bin/sh" } + let(:expected_root_home) { "/var/root" } + + it "should return /var/root" do + Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(root_ent) + Facter::Util::RootHome.get_root_home.should == expected_root_home + end + end + context "windows" do + let(:root_ent) { "FIXME TBD on Windows" } + let(:expected_root_home) { "FIXME TBD on Windows" } + + it "should return FIXME TBD on windows" do + pending "FIXME: TBD on windows" + Facter::Util::Resolution.expects(:exec).with("getent passwd root").returns(root_ent) + Facter::Util::RootHome.get_root_home.should == expected_root_home + end + end +end |