hiera 2.0.0-x86-mingw32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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