hiera 2.0.0-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +202 -0
  3. data/LICENSE +18 -0
  4. data/README.md +276 -0
  5. data/bin/hiera +248 -0
  6. data/lib/hiera.rb +115 -0
  7. data/lib/hiera/backend.rb +325 -0
  8. data/lib/hiera/backend/json_backend.rb +58 -0
  9. data/lib/hiera/backend/yaml_backend.rb +63 -0
  10. data/lib/hiera/config.rb +90 -0
  11. data/lib/hiera/console_logger.rb +13 -0
  12. data/lib/hiera/error.rb +4 -0
  13. data/lib/hiera/fallback_logger.rb +41 -0
  14. data/lib/hiera/filecache.rb +86 -0
  15. data/lib/hiera/interpolate.rb +98 -0
  16. data/lib/hiera/noop_logger.rb +8 -0
  17. data/lib/hiera/puppet_logger.rb +17 -0
  18. data/lib/hiera/recursive_guard.rb +20 -0
  19. data/lib/hiera/util.rb +47 -0
  20. data/lib/hiera/version.rb +89 -0
  21. data/spec/spec_helper.rb +78 -0
  22. data/spec/unit/backend/json_backend_spec.rb +85 -0
  23. data/spec/unit/backend/yaml_backend_spec.rb +138 -0
  24. data/spec/unit/backend_spec.rb +743 -0
  25. data/spec/unit/config_spec.rb +118 -0
  26. data/spec/unit/console_logger_spec.rb +19 -0
  27. data/spec/unit/fallback_logger_spec.rb +80 -0
  28. data/spec/unit/filecache_spec.rb +142 -0
  29. data/spec/unit/fixtures/interpolate/config/hiera.yaml +6 -0
  30. data/spec/unit/fixtures/interpolate/data/niltest.yaml +2 -0
  31. data/spec/unit/fixtures/interpolate/data/recursive.yaml +3 -0
  32. data/spec/unit/fixtures/override/config/hiera.yaml +5 -0
  33. data/spec/unit/fixtures/override/data/alternate.yaml +1 -0
  34. data/spec/unit/fixtures/override/data/common.yaml +2 -0
  35. data/spec/unit/hiera_spec.rb +81 -0
  36. data/spec/unit/interpolate_spec.rb +36 -0
  37. data/spec/unit/puppet_logger_spec.rb +31 -0
  38. data/spec/unit/util_spec.rb +49 -0
  39. data/spec/unit/version_spec.rb +44 -0
  40. metadata +142 -0
@@ -0,0 +1,8 @@
1
+ class Hiera
2
+ module Noop_logger
3
+ class << self
4
+ def warn(msg);end
5
+ def debug(msg);end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ class Hiera
2
+ module Puppet_logger
3
+ class << self
4
+ def suitable?
5
+ defined?(::Puppet) == "constant"
6
+ end
7
+
8
+ def warn(msg)
9
+ Puppet.notice("hiera(): #{msg}")
10
+ end
11
+
12
+ def debug(msg)
13
+ Puppet.debug("hiera(): #{msg}")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ # Allow for safe recursive lookup of values during variable interpolation.
2
+ #
3
+ # @api private
4
+ class Hiera::InterpolationLoop < StandardError; end
5
+
6
+ class Hiera::RecursiveGuard
7
+ def initialize
8
+ @seen = []
9
+ end
10
+
11
+ def check(value, &block)
12
+ if @seen.include?(value)
13
+ raise Hiera::InterpolationLoop, "Detected in [#{@seen.join(', ')}]"
14
+ end
15
+ @seen.push(value)
16
+ ret = yield
17
+ @seen.pop
18
+ ret
19
+ end
20
+ end
@@ -0,0 +1,47 @@
1
+ class Hiera
2
+ module Util
3
+ module_function
4
+
5
+ def posix?
6
+ require 'etc'
7
+ Etc.getpwuid(0) != nil
8
+ end
9
+
10
+ def microsoft_windows?
11
+ return false unless file_alt_separator
12
+
13
+ begin
14
+ require 'win32/dir'
15
+ true
16
+ rescue LoadError => err
17
+ warn "Cannot run on Microsoft Windows without the win32-dir gem: #{err}"
18
+ false
19
+ end
20
+ end
21
+
22
+ def config_dir
23
+ if microsoft_windows?
24
+ File.join(common_appdata, 'PuppetLabs', 'code')
25
+ else
26
+ '/etc/puppetlabs/code'
27
+ end
28
+ end
29
+
30
+ def var_dir
31
+ if microsoft_windows?
32
+ File.join(common_appdata, 'PuppetLabs', 'code', 'hieradata')
33
+ else
34
+ '/etc/puppetlabs/code/hieradata'
35
+ end
36
+ end
37
+
38
+ def file_alt_separator
39
+ File::ALT_SEPARATOR
40
+ end
41
+
42
+ def common_appdata
43
+ Dir::COMMON_APPDATA
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,89 @@
1
+ # The version method and constant are isolated in hiera/version.rb so that a
2
+ # simple `require 'hiera/version'` allows a rubygems gemspec or bundler
3
+ # Gemfile to get the hiera version of the gem install.
4
+ #
5
+ # The version is programatically settable because we want to allow the
6
+ # Raketasks and such to set the version based on the output of `git describe`
7
+
8
+
9
+ class Hiera
10
+ VERSION = "2.0.0"
11
+
12
+ ##
13
+ # version is a public API method intended to always provide a fast and
14
+ # lightweight way to determine the version of hiera.
15
+ #
16
+ # The intent is that software external to hiera be able to determine the
17
+ # hiera version with no side-effects. The expected use is:
18
+ #
19
+ # require 'hiera/version'
20
+ # version = Hiera.version
21
+ #
22
+ # This function has the following ordering precedence. This precedence list
23
+ # is designed to facilitate automated packaging tasks by simply writing to
24
+ # the VERSION file in the same directory as this source file.
25
+ #
26
+ # 1. If a version has been explicitly assigned using the Hiera.version=
27
+ # method, return that version.
28
+ # 2. If there is a VERSION file, read the contents, trim any
29
+ # trailing whitespace, and return that version string.
30
+ # 3. Return the value of the Hiera::VERSION constant hard-coded into
31
+ # the source code.
32
+ #
33
+ # If there is no VERSION file, the method must return the version string of
34
+ # the nearest parent version that is an officially released version. That is
35
+ # to say, if a branch named 3.1.x contains 25 patches on top of the most
36
+ # recent official release of 3.1.1, then the version method must return the
37
+ # string "3.1.1" if no "VERSION" file is present.
38
+ #
39
+ # By design the version identifier is _not_ intended to vary during the life
40
+ # a process. There is no guarantee provided that writing to the VERSION file
41
+ # while a Hiera process is running will cause the version string to be
42
+ # updated. On the contrary, the contents of the VERSION are cached to reduce
43
+ # filesystem accesses.
44
+ #
45
+ # The VERSION file is intended to be used by package maintainers who may be
46
+ # applying patches or otherwise changing the software version in a manner
47
+ # that warrants a different software version identifier. The VERSION file is
48
+ # intended to be managed and owned by the release process and packaging
49
+ # related tasks, and as such should not reside in version control. The
50
+ # VERSION constant is intended to be version controlled in history.
51
+ #
52
+ # Ideally, this behavior will allow package maintainers to precisely specify
53
+ # the version of the software they're packaging as in the following example:
54
+ #
55
+ # $ git describe --match "1.2.*" > lib/hiera/VERSION
56
+ # $ ruby -r hiera/version -e 'puts Hiera.version'
57
+ # 1.2.1-9-g9fda440
58
+ #
59
+ # @api public
60
+ #
61
+ # @return [String] containing the hiera version, e.g. "1.2.1"
62
+ def self.version
63
+ version_file = File.join(File.dirname(__FILE__), 'VERSION')
64
+ return @hiera_version if @hiera_version
65
+ if version = read_version_file(version_file)
66
+ @hiera_version = version
67
+ end
68
+ @hiera_version ||= VERSION
69
+ end
70
+
71
+ def self.version=(version)
72
+ @hiera_version = version
73
+ end
74
+
75
+ ##
76
+ # read_version_file reads the content of the "VERSION" file that lives in the
77
+ # same directory as this source code file.
78
+ #
79
+ # @api private
80
+ #
81
+ # @return [String] for example: "1.6.14-6-gea42046" or nil if the VERSION
82
+ # file does not exist.
83
+ def self.read_version_file(path)
84
+ if File.exists?(path)
85
+ File.read(path).chomp
86
+ end
87
+ end
88
+ private_class_method :read_version_file
89
+ end
@@ -0,0 +1,78 @@
1
+ $:.insert(0, File.join([File.dirname(__FILE__), "..", "lib"]))
2
+
3
+ require 'rubygems'
4
+ require 'rspec'
5
+ require 'mocha'
6
+ require 'hiera'
7
+ require 'tmpdir'
8
+
9
+ RSpec.configure do |config|
10
+ config.mock_with :mocha
11
+
12
+ if Hiera::Util.microsoft_windows? && RUBY_VERSION =~ /^1\./
13
+ require 'win32console'
14
+ config.output_stream = $stdout
15
+ config.error_stream = $stderr
16
+ config.formatters.each { |f| f.instance_variable_set(:@output, $stdout) }
17
+ end
18
+
19
+ config.after :suite do
20
+ # Log the spec order to a file, but only if the LOG_SPEC_ORDER environment variable is
21
+ # set. This should be enabled on Jenkins runs, as it can be used with Nick L.'s bisect
22
+ # script to help identify and debug order-dependent spec failures.
23
+ if ENV['LOG_SPEC_ORDER']
24
+ File.open("./spec_order.txt", "w") do |logfile|
25
+ config.instance_variable_get(:@files_to_run).each { |f| logfile.puts f }
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ # So everyone else doesn't have to include this base constant.
32
+ module HieraSpec
33
+ FIXTURE_DIR = File.join(dir = File.expand_path(File.dirname(__FILE__)), 'unit', 'fixtures') unless defined?(FIXTURE_DIR)
34
+ end
35
+
36
+ # In ruby 1.8.5 Dir does not have mktmpdir defined, so this monkey patches
37
+ # Dir to include the 1.8.7 definition of that method if it isn't already defined.
38
+ # Method definition borrowed from ruby-1.8.7-p357/lib/ruby/1.8/tmpdir.rb
39
+ unless Dir.respond_to?(:mktmpdir)
40
+ def Dir.mktmpdir(prefix_suffix=nil, tmpdir=nil)
41
+ case prefix_suffix
42
+ when nil
43
+ prefix = "d"
44
+ suffix = ""
45
+ when String
46
+ prefix = prefix_suffix
47
+ suffix = ""
48
+ when Array
49
+ prefix = prefix_suffix[0]
50
+ suffix = prefix_suffix[1]
51
+ else
52
+ raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
53
+ end
54
+ tmpdir ||= Dir.tmpdir
55
+ t = Time.now.strftime("%Y%m%d")
56
+ n = nil
57
+ begin
58
+ path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
59
+ path << "-#{n}" if n
60
+ path << suffix
61
+ Dir.mkdir(path, 0700)
62
+ rescue Errno::EEXIST
63
+ n ||= 0
64
+ n += 1
65
+ retry
66
+ end
67
+
68
+ if block_given?
69
+ begin
70
+ yield path
71
+ ensure
72
+ FileUtils.remove_entry_secure path
73
+ end
74
+ else
75
+ path
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,85 @@
1
+ require 'spec_helper'
2
+ require 'hiera/backend/json_backend'
3
+
4
+ class Hiera
5
+ module Backend
6
+ describe Json_backend do
7
+ before do
8
+ Hiera.stubs(:debug)
9
+ Hiera.stubs(:warn)
10
+ Hiera::Backend.stubs(:empty_answer).returns(nil)
11
+ @cache = mock
12
+ @backend = Json_backend.new(@cache)
13
+ end
14
+
15
+ describe "#initialize" do
16
+ it "should announce its creation" do # because other specs checks this
17
+ Hiera.expects(:debug).with("Hiera JSON backend starting")
18
+ Json_backend.new
19
+ end
20
+ end
21
+
22
+ describe "#lookup" do
23
+ it "should look for data in all sources" do
24
+ Backend.expects(:datasources).multiple_yields(["one"], ["two"])
25
+ Backend.expects(:datafile).with(:json, {}, "one", "json").returns(nil)
26
+ Backend.expects(:datafile).with(:json, {}, "two", "json").returns(nil)
27
+
28
+ expect { @backend.lookup("key", {}, nil, :priority, nil) }.to throw_symbol(:no_such_key)
29
+ end
30
+
31
+ it "should retain the data types found in data files" do
32
+ Backend.expects(:datasources).yields("one").times(3)
33
+ Backend.expects(:datafile).with(:json, {}, "one", "json").returns("/nonexisting/one.json").times(3)
34
+ File.stubs(:exist?).with("/nonexisting/one.json").returns(true)
35
+
36
+ @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"stringval" => "string", "boolval" => true, "numericval" => 1}).times(3)
37
+
38
+ @backend.lookup("stringval", {}, nil, :priority, nil).should == "string"
39
+ @backend.lookup("boolval", {}, nil, :priority, nil).should == true
40
+ @backend.lookup("numericval", {}, nil, :priority, nil).should == 1
41
+ end
42
+
43
+ it "should pick data earliest source that has it for priority searches" do
44
+ scope = {"rspec" => "test"}
45
+ Backend.expects(:datasources).multiple_yields(["one"], ["two"])
46
+ Backend.expects(:datafile).with(:json, scope, "one", "json").returns("/nonexisting/one.json")
47
+ Backend.expects(:datafile).with(:json, scope, "two", "json").never
48
+
49
+ File.stubs(:exist?).with("/nonexisting/one.json").returns(true)
50
+ @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "test_%{rspec}"})
51
+
52
+ @backend.lookup("key", scope, nil, :priority, nil).should == "test_test"
53
+ end
54
+
55
+ it "should build an array of all data sources for array searches" do
56
+ Hiera::Backend.stubs(:empty_answer).returns([])
57
+ Backend.stubs(:parse_answer).with('answer', {}, {}, anything).returns("answer")
58
+ Backend.expects(:datafile).with(:json, {}, "one", "json").returns("/nonexisting/one.json")
59
+ Backend.expects(:datafile).with(:json, {}, "two", "json").returns("/nonexisting/two.json")
60
+
61
+ Backend.expects(:datasources).multiple_yields(["one"], ["two"])
62
+
63
+ File.expects(:exist?).with("/nonexisting/one.json").returns(true)
64
+ File.expects(:exist?).with("/nonexisting/two.json").returns(true)
65
+
66
+ @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "answer"})
67
+ @cache.expects(:read_file).with("/nonexisting/two.json", Hash).returns({"key" => "answer"})
68
+
69
+ @backend.lookup("key", {}, nil, :array, nil).should == ["answer", "answer"]
70
+ end
71
+
72
+ it "should parse the answer for scope variables" do
73
+ Backend.stubs(:parse_answer).with('test_%{rspec}', {'rspec' => 'test'}, {}, anything).returns("test_test")
74
+ Backend.expects(:datasources).yields("one")
75
+ Backend.expects(:datafile).with(:json, {"rspec" => "test"}, "one", "json").returns("/nonexisting/one.json")
76
+
77
+ File.expects(:exist?).with("/nonexisting/one.json").returns(true)
78
+ @cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "test_%{rspec}"})
79
+
80
+ @backend.lookup("key", {"rspec" => "test"}, nil, :priority, nil).should == "test_test"
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,138 @@
1
+ require 'spec_helper'
2
+ require 'hiera/backend/yaml_backend'
3
+
4
+ class Hiera
5
+ module Backend
6
+ class FakeCache
7
+ attr_accessor :value
8
+ def read(path, expected_type, default, &block)
9
+ read_file(path, expected_type, &block)
10
+ rescue => e
11
+ default
12
+ end
13
+
14
+ def read_file(path, expected_type, &block)
15
+ output = block.call(@value)
16
+ if !output.is_a? expected_type
17
+ raise TypeError
18
+ end
19
+ output
20
+ end
21
+ end
22
+
23
+ describe Yaml_backend do
24
+ before do
25
+ Config.load({})
26
+ Hiera.stubs(:debug)
27
+ Hiera.stubs(:warn)
28
+ @cache = FakeCache.new
29
+ @backend = Yaml_backend.new(@cache)
30
+ end
31
+
32
+ describe "#initialize" do
33
+ it "should announce its creation" do # because other specs checks this
34
+ Hiera.expects(:debug).with("Hiera YAML backend starting")
35
+ Yaml_backend.new
36
+ end
37
+ end
38
+
39
+ describe "#lookup" do
40
+ it "should pick data earliest source that has it for priority searches" do
41
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).yields(["one", "/nonexisting/one.yaml"])
42
+ @cache.value = "---\nkey: answer"
43
+
44
+ @backend.lookup("key", {}, nil, :priority, nil).should == "answer"
45
+ end
46
+
47
+ describe "handling unexpected YAML values" do
48
+ before do
49
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).yields(["one", "/nonexisting/one.yaml"])
50
+ end
51
+
52
+ it "throws :no_such_key when key is missing in YAML" do
53
+ @cache.value = "---\n"
54
+ expect { @backend.lookup("key", {}, nil, :priority, nil) }.to throw_symbol(:no_such_key)
55
+ end
56
+
57
+ it "returns nil when the YAML value is nil" do
58
+ @cache.value = "key: ~\n"
59
+ @backend.lookup("key", {}, nil, :priority, nil).should be_nil
60
+ end
61
+
62
+ it "throws :no_such_key when the YAML file is false" do
63
+ @cache.value = ""
64
+ expect { @backend.lookup("key", {}, nil, :priority, nil) }.to throw_symbol(:no_such_key)
65
+ end
66
+
67
+ it "raises a TypeError when the YAML value is not a hash" do
68
+ @cache.value = "---\n[one, two, three]"
69
+ expect { @backend.lookup("key", {}, nil, :priority, nil) }.to raise_error(TypeError)
70
+ end
71
+ end
72
+
73
+ it "should build an array of all data sources for array searches" do
74
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"])
75
+ @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"answer"})
76
+ @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>"answer"})
77
+
78
+ @backend.lookup("key", {}, nil, :array, nil).should == ["answer", "answer"]
79
+ end
80
+
81
+ it "should ignore empty hash of data sources for hash searches" do
82
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"])
83
+
84
+ @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({})
85
+ @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
86
+
87
+ @backend.lookup("key", {}, nil, :hash, nil).should == {"a" => "answer"}
88
+ end
89
+
90
+ it "should build a merged hash of data sources for hash searches" do
91
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"])
92
+
93
+ @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
94
+ @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"b"=>"answer", "a"=>"wrong"}})
95
+
96
+ @backend.lookup("key", {}, nil, :hash, nil).should == {"a" => "answer", "b" => "answer"}
97
+ end
98
+
99
+ it "should fail when trying to << a Hash" do
100
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"])
101
+
102
+ @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>["a", "answer"]})
103
+ @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
104
+
105
+ expect {@backend.lookup("key", {}, nil, :array, nil)}.to raise_error(Exception, "Hiera type mismatch for key 'key': expected Array and got Hash")
106
+ end
107
+
108
+ it "should fail when trying to merge an Array" do
109
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"], ["two", "/nonexisting/two.yaml"])
110
+
111
+ @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
112
+ @cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>["a", "wrong"]})
113
+
114
+ expect { @backend.lookup("key", {}, nil, :hash, nil) }.to raise_error(Exception, "Hiera type mismatch for key 'key': expected Hash and got Array")
115
+ end
116
+
117
+ it "should parse the answer for scope variables" do
118
+ Backend.expects(:datasourcefiles).with(:yaml, {"rspec" => "test"}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"])
119
+
120
+ @cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"test_%{rspec}"})
121
+
122
+ @backend.lookup("key", {"rspec" => "test"}, nil, :priority, nil).should == "test_test"
123
+ end
124
+
125
+ it "should retain datatypes found in yaml files" do
126
+ Backend.expects(:datasourcefiles).with(:yaml, {}, "yaml", nil).multiple_yields(["one", "/nonexisting/one.yaml"]).times(3)
127
+
128
+
129
+ @cache.value = "---\nstringval: 'string'\nboolval: true\nnumericval: 1"
130
+
131
+ @backend.lookup("stringval", {}, nil, :priority, nil).should == "string"
132
+ @backend.lookup("boolval", {}, nil, :priority, nil).should == true
133
+ @backend.lookup("numericval", {}, nil, :priority, nil).should == 1
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end