diff --git a/lib/puppet/indirector/hiera.rb b/lib/puppet/indirector/hiera.rb new file mode 100644 index 000000000..b11c01cd6 --- /dev/null +++ b/lib/puppet/indirector/hiera.rb @@ -0,0 +1,48 @@ +require 'puppet/indirector/terminus' +require 'hiera/scope' + +class Puppet::Indirector::Hiera < Puppet::Indirector::Terminus + def initialize(*args) + if ! Puppet.features.hiera? + raise "Hiera terminus not supported without hiera library" + end + super + end + + if defined?(::Psych::SyntaxError) + DataBindingExceptions = [::StandardError, ::Psych::SyntaxError] + else + DataBindingExceptions = [::StandardError] + end + + def find(request) + hiera.lookup(request.key, nil, Hiera::Scope.new(request.options[:variables]), nil, nil) + rescue *DataBindingExceptions => detail + raise Puppet::DataBinding::LookupError.new(detail.message, detail) + end + + private + + def self.hiera_config + hiera_config = Puppet.settings[:hiera_config] + config = {} + + if File.exist?(hiera_config) + config = Hiera::Config.load(hiera_config) + else + Puppet.warning "Config file #{hiera_config} not found, using Hiera defaults" + end + + config[:logger] = 'puppet' + config + end + + def self.hiera + @hiera ||= Hiera.new(:config => hiera_config) + end + + def hiera + self.class.hiera + end +end + diff --git a/spec/fixtures/unit/indirector/hiera/global.yaml b/spec/fixtures/unit/indirector/hiera/global.yaml new file mode 100644 index 000000000..0853e0ec1 --- /dev/null +++ b/spec/fixtures/unit/indirector/hiera/global.yaml @@ -0,0 +1,10 @@ +--- +integer: 3000 +string: 'apache' +hash: + user: 'Hightower' + group: 'admin' + mode: '0644' +array: + - '0.ntp.puppetlabs.com' + - '1.ntp.puppetlabs.com' diff --git a/spec/fixtures/unit/indirector/hiera/invalid.yaml b/spec/fixtures/unit/indirector/hiera/invalid.yaml new file mode 100644 index 000000000..9a84fa87c --- /dev/null +++ b/spec/fixtures/unit/indirector/hiera/invalid.yaml @@ -0,0 +1 @@ +{ invalid: diff --git a/spec/unit/indirector/hiera_spec.rb b/spec/unit/indirector/hiera_spec.rb new file mode 100644 index 000000000..832c2da79 --- /dev/null +++ b/spec/unit/indirector/hiera_spec.rb @@ -0,0 +1,108 @@ +require 'spec_helper' +require 'puppet/data_binding' +require 'puppet/indirector/hiera' +require 'hiera/backend' + +describe Puppet::Indirector::Hiera do + include PuppetSpec::Files + + module Testing + module DataBinding + class Hiera < Puppet::Indirector::Hiera + end + end + end + + def write_hiera_config(config_file, datadir) + File.open(config_file, 'w') do |f| + f.write("--- + :yaml: + :datadir: #{datadir} + :hierarchy: ['global', 'invalid'] + :logger: 'noop' + :backends: ['yaml'] + ") + end + end + + before do + hiera_config_file = tmpfile("hiera.yaml") + Puppet.settings[:hiera_config] = hiera_config_file + write_hiera_config(hiera_config_file, my_fixture_dir) + end + + it "should be the default data_binding terminus" do + Puppet.settings[:data_binding_terminus].should == :hiera + end + + it "should raise an error if we don't have the hiera feature" do + Puppet.features.expects(:hiera?).returns(false) + lambda { Testing::DataBinding::Hiera.new }.should raise_error RuntimeError, + "Hiera terminus not supported without hiera library" + end + + describe "the behavior of the hiera_config method", :if => Puppet.features.hiera? do + it "should override the logger and set it to puppet" do + Testing::DataBinding::Hiera.hiera_config[:logger].should == "puppet" + end + + context "when the Hiera configuration file does not exist" do + let(:path) { File.expand_path('/doesnotexist') } + + before do + Puppet.settings[:hiera_config] = path + end + + it "should log a warning" do + Puppet.expects(:warning).with( + "Config file #{path} not found, using Hiera defaults") + Testing::DataBinding::Hiera.hiera_config + end + + it "should only configure the logger and set it to puppet" do + Puppet.expects(:warning).with( + "Config file #{path} not found, using Hiera defaults") + Testing::DataBinding::Hiera.hiera_config.should == { :logger => 'puppet' } + end + end + end + + describe "the behavior of the find method", :if => Puppet.features.hiera? do + + let(:data_binder) { Testing::DataBinding::Hiera.new } + + it "should support looking up an integer" do + data_binder.find(request("integer")).should == 3000 + end + + it "should support looking up a string" do + data_binder.find(request("string")).should == 'apache' + end + + it "should support looking up an array" do + data_binder.find(request("array")).should == [ + '0.ntp.puppetlabs.com', + '1.ntp.puppetlabs.com', + ] + end + + it "should support looking up a hash" do + data_binder.find(request("hash")).should == { + 'user' => 'Hightower', + 'group' => 'admin', + 'mode' => '0644' + } + end + + it "raises a data binding error if hiera cannot parse the yaml data" do + expect do + data_binder.find(request('invalid')) + end.to raise_error(Puppet::DataBinding::LookupError) + end + end + + def request(key) + Puppet::Indirector::Request.new(:hiera, :find, key, nil) + end +end +