hiera 1.2.1 → 1.3.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of hiera might be problematic. Click here for more details.
- data/README.md +3 -3
- data/bin/hiera +2 -1
- data/lib/hiera.rb +2 -6
- data/lib/hiera/backend.rb +20 -24
- data/lib/hiera/backend/json_backend.rb +1 -1
- data/lib/hiera/backend/yaml_backend.rb +1 -1
- data/lib/hiera/error.rb +4 -0
- data/lib/hiera/filecache.rb +30 -18
- data/lib/hiera/interpolate.rb +48 -0
- data/lib/hiera/recursive_guard.rb +20 -0
- data/lib/hiera/version.rb +89 -0
- data/spec/unit/backend/json_backend_spec.rb +5 -5
- data/spec/unit/backend/yaml_backend_spec.rb +14 -14
- data/spec/unit/backend_spec.rb +199 -31
- data/spec/unit/filecache_spec.rb +111 -32
- data/spec/unit/version_spec.rb +44 -0
- metadata +60 -70
- data/lib/hiera/recursive_lookup.rb +0 -31
data/README.md
CHANGED
@@ -117,7 +117,7 @@ A sample configuration file can be seen here:
|
|
117
117
|
:logger: console
|
118
118
|
|
119
119
|
:hierarchy:
|
120
|
-
- "
|
120
|
+
- "sites/%{location}"
|
121
121
|
- common
|
122
122
|
|
123
123
|
:yaml:
|
@@ -130,14 +130,14 @@ A sample configuration file can be seen here:
|
|
130
130
|
This configuration will require YAML files in _/etc/puppet/hieradata_ these need to contain
|
131
131
|
Hash data, sample files matching the hierarchy described in the _Why?_ section are below:
|
132
132
|
|
133
|
-
_/etc/puppet/hieradata/dc1.yaml_:
|
133
|
+
_/etc/puppet/hieradata/sites/dc1.yaml_:
|
134
134
|
<pre>
|
135
135
|
---
|
136
136
|
ntpserver: ntp1.dc1.example.com
|
137
137
|
sysadmin: dc1noc@example.com
|
138
138
|
</pre>
|
139
139
|
|
140
|
-
_/etc/puppet/hieradata/dc2.yaml_:
|
140
|
+
_/etc/puppet/hieradata/sites/dc2.yaml_:
|
141
141
|
<pre>
|
142
142
|
---
|
143
143
|
ntpserver: ntp1.dc2.example.com
|
data/bin/hiera
CHANGED
@@ -24,6 +24,7 @@ end
|
|
24
24
|
require 'hiera'
|
25
25
|
require 'hiera/util'
|
26
26
|
require 'optparse'
|
27
|
+
require 'pp'
|
27
28
|
|
28
29
|
options = {
|
29
30
|
:default => nil,
|
@@ -222,5 +223,5 @@ ans = hiera.lookup(options[:key], options[:default], options[:scope], nil, optio
|
|
222
223
|
if ans.is_a?(String)
|
223
224
|
puts ans
|
224
225
|
else
|
225
|
-
|
226
|
+
pp ans
|
226
227
|
end
|
data/lib/hiera.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
|
3
3
|
class Hiera
|
4
|
-
|
5
|
-
|
4
|
+
require "hiera/error"
|
5
|
+
require "hiera/version"
|
6
6
|
require "hiera/config"
|
7
7
|
require "hiera/util"
|
8
8
|
require "hiera/backend"
|
@@ -15,10 +15,6 @@ class Hiera
|
|
15
15
|
class << self
|
16
16
|
attr_reader :logger
|
17
17
|
|
18
|
-
def version
|
19
|
-
VERSION
|
20
|
-
end
|
21
|
-
|
22
18
|
# Loggers are pluggable, just provide a class called
|
23
19
|
# Hiera::Foo_logger and respond to :warn and :debug
|
24
20
|
#
|
data/lib/hiera/backend.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'hiera/util'
|
2
|
-
require 'hiera/
|
2
|
+
require 'hiera/recursive_guard'
|
3
|
+
require 'hiera/interpolate'
|
3
4
|
|
4
5
|
begin
|
5
6
|
require 'deep_merge'
|
@@ -8,21 +9,25 @@ end
|
|
8
9
|
|
9
10
|
class Hiera
|
10
11
|
module Backend
|
11
|
-
INTERPOLATION = /%\{([^\}]*)\}/
|
12
|
-
|
13
12
|
class << self
|
14
13
|
# Data lives in /var/lib/hiera by default. If a backend
|
15
14
|
# supplies a datadir in the config it will be used and
|
16
15
|
# subject to variable expansion based on scope
|
17
16
|
def datadir(backend, scope)
|
18
17
|
backend = backend.to_sym
|
19
|
-
default = Hiera::Util.var_dir
|
20
18
|
|
21
|
-
if Config
|
22
|
-
|
19
|
+
if Config[backend] && Config[backend][:datadir]
|
20
|
+
dir = Config[backend][:datadir]
|
23
21
|
else
|
24
|
-
|
22
|
+
dir = Hiera::Util.var_dir
|
23
|
+
end
|
24
|
+
|
25
|
+
if !dir.is_a?(String)
|
26
|
+
raise(Hiera::InvalidConfigurationError,
|
27
|
+
"datadir for #{backend} cannot be an array")
|
25
28
|
end
|
29
|
+
|
30
|
+
parse_string(dir, scope)
|
26
31
|
end
|
27
32
|
|
28
33
|
# Finds the path to a datafile based on the Backend#datadir
|
@@ -85,22 +90,8 @@ class Hiera
|
|
85
90
|
#
|
86
91
|
# @api public
|
87
92
|
def parse_string(data, scope, extra_data={})
|
88
|
-
interpolate(data, Hiera::
|
89
|
-
end
|
90
|
-
|
91
|
-
def interpolate(data, values)
|
92
|
-
if data.is_a?(String)
|
93
|
-
data.gsub(INTERPOLATION) do
|
94
|
-
name = $1
|
95
|
-
values.lookup(name) do |value|
|
96
|
-
interpolate(value, values)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
else
|
100
|
-
data
|
101
|
-
end
|
93
|
+
Hiera::Interpolate.interpolate(data, Hiera::RecursiveGuard.new, scope, extra_data)
|
102
94
|
end
|
103
|
-
private :interpolate
|
104
95
|
|
105
96
|
# Parses a answer received from data files
|
106
97
|
#
|
@@ -114,7 +105,8 @@ class Hiera
|
|
114
105
|
elsif data.is_a?(Hash)
|
115
106
|
answer = {}
|
116
107
|
data.each_pair do |key, val|
|
117
|
-
|
108
|
+
interpolated_key = parse_string(key, scope, extra_data)
|
109
|
+
answer[interpolated_key] = parse_answer(val, scope, extra_data)
|
118
110
|
end
|
119
111
|
|
120
112
|
return answer
|
@@ -145,7 +137,7 @@ class Hiera
|
|
145
137
|
# Deep merge options use the Hash utility function provided by [deep_merge](https://github.com/peritor/deep_merge)
|
146
138
|
#
|
147
139
|
# :native => Native Hash.merge
|
148
|
-
# :deep => Use Hash.deep_merge
|
140
|
+
# :deep => Use Hash.deep_merge
|
149
141
|
# :deeper => Use Hash.deep_merge!
|
150
142
|
#
|
151
143
|
def merge_answer(left,right)
|
@@ -205,6 +197,10 @@ class Hiera
|
|
205
197
|
return default if answer.nil?
|
206
198
|
return answer
|
207
199
|
end
|
200
|
+
|
201
|
+
def clear!
|
202
|
+
@backends = {}
|
203
|
+
end
|
208
204
|
end
|
209
205
|
end
|
210
206
|
end
|
data/lib/hiera/error.rb
ADDED
data/lib/hiera/filecache.rb
CHANGED
@@ -24,32 +24,44 @@ class Hiera
|
|
24
24
|
# reading/parsing fails it will return {} instead
|
25
25
|
#
|
26
26
|
# Prior to calling this method you should be sure the file exist
|
27
|
-
def read(path, expected_type=
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
@cache[path][:data] = File.read(path)
|
40
|
-
end
|
27
|
+
def read(path, expected_type = Object, default=nil, &block)
|
28
|
+
read_file(path, expected_type, &block)
|
29
|
+
rescue TypeError => detail
|
30
|
+
Hiera.debug("#{detail.message}, setting defaults")
|
31
|
+
@cache[path][:data] = default
|
32
|
+
rescue => detail
|
33
|
+
error = "Reading data from #{path} failed: #{detail.class}: #{detail}"
|
34
|
+
if default.nil?
|
35
|
+
raise detail
|
36
|
+
else
|
37
|
+
Hiera.debug(error)
|
38
|
+
@cache[path][:data] = default
|
41
39
|
end
|
40
|
+
end
|
42
41
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
42
|
+
# Read a file when it changes. If a file is re-read and has not changed since the last time
|
43
|
+
# then the last, processed, contents will be returned.
|
44
|
+
#
|
45
|
+
# The processed data can also be checked against an expected type. If the
|
46
|
+
# type does not match a TypeError is raised.
|
47
|
+
#
|
48
|
+
# No error handling is done inside this method. Any failed reads or errors
|
49
|
+
# in processing will be propagated to the caller
|
50
|
+
def read_file(path, expected_type = Object)
|
51
|
+
if stale?(path)
|
52
|
+
data = File.read(path)
|
53
|
+
@cache[path][:data] = block_given? ? yield(data) : data
|
54
|
+
|
55
|
+
if !@cache[path][:data].is_a?(expected_type)
|
56
|
+
raise TypeError, "Data retrieved from #{path} is #{data.class} not #{expected_type}"
|
47
57
|
end
|
48
58
|
end
|
49
59
|
|
50
60
|
@cache[path][:data]
|
51
61
|
end
|
52
62
|
|
63
|
+
private
|
64
|
+
|
53
65
|
def stale?(path)
|
54
66
|
meta = path_metadata(path)
|
55
67
|
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'hiera/backend'
|
2
|
+
|
3
|
+
class Hiera::Interpolate
|
4
|
+
class << self
|
5
|
+
INTERPOLATION = /%\{([^\}]*)\}/
|
6
|
+
METHOD_INTERPOLATION = /%\{(scope|hiera)\(['"]([^"']*)["']\)\}/
|
7
|
+
|
8
|
+
def interpolate(data, recurse_guard, scope, extra_data)
|
9
|
+
if data.is_a?(String) && (match = data.match(INTERPOLATION))
|
10
|
+
interpolation_variable = match[1]
|
11
|
+
recurse_guard.check(interpolation_variable) do
|
12
|
+
interpolate_method, key = get_interpolation_method_and_key(data)
|
13
|
+
interpolated_data = send(interpolate_method, data, key, scope, extra_data)
|
14
|
+
interpolate(interpolated_data, recurse_guard, scope, extra_data)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
data
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_interpolation_method_and_key(data)
|
22
|
+
if (match = data.match(METHOD_INTERPOLATION))
|
23
|
+
case match[1]
|
24
|
+
when 'hiera' then [:hiera_interpolate, match[2]]
|
25
|
+
when 'scope' then [:scope_interpolate, match[2]]
|
26
|
+
end
|
27
|
+
elsif (match = data.match(INTERPOLATION))
|
28
|
+
[:scope_interpolate, match[1]]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
private :get_interpolation_method_and_key
|
32
|
+
|
33
|
+
def scope_interpolate(data, key, scope, extra_data)
|
34
|
+
value = scope[key]
|
35
|
+
if value.nil? || value == :undefined
|
36
|
+
value = extra_data[key]
|
37
|
+
end
|
38
|
+
data.sub(INTERPOLATION, value.to_s)
|
39
|
+
end
|
40
|
+
private :scope_interpolate
|
41
|
+
|
42
|
+
def hiera_interpolate(data, key, scope, extra_data)
|
43
|
+
value = Hiera::Backend.lookup(key, nil, scope, nil, :priority)
|
44
|
+
data.sub(METHOD_INTERPOLATION, value)
|
45
|
+
end
|
46
|
+
private :hiera_interpolate
|
47
|
+
end
|
48
|
+
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,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 = "1.3.0-rc2"
|
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
|
@@ -33,7 +33,7 @@ class Hiera
|
|
33
33
|
Backend.expects(:datafile).with(:json, {}, "one", "json").returns("/nonexisting/one.json").times(3)
|
34
34
|
File.stubs(:exist?).with("/nonexisting/one.json").returns(true)
|
35
35
|
|
36
|
-
@cache.expects(:
|
36
|
+
@cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"stringval" => "string", "boolval" => true, "numericval" => 1}).times(3)
|
37
37
|
|
38
38
|
@backend.lookup("stringval", {}, nil, :priority).should == "string"
|
39
39
|
@backend.lookup("boolval", {}, nil, :priority).should == true
|
@@ -47,7 +47,7 @@ class Hiera
|
|
47
47
|
Backend.expects(:datafile).with(:json, scope, "two", "json").never
|
48
48
|
|
49
49
|
File.stubs(:exist?).with("/nonexisting/one.json").returns(true)
|
50
|
-
@cache.expects(:
|
50
|
+
@cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "test_%{rspec}"})
|
51
51
|
|
52
52
|
@backend.lookup("key", scope, nil, :priority).should == "test_test"
|
53
53
|
end
|
@@ -63,8 +63,8 @@ class Hiera
|
|
63
63
|
File.expects(:exist?).with("/nonexisting/one.json").returns(true)
|
64
64
|
File.expects(:exist?).with("/nonexisting/two.json").returns(true)
|
65
65
|
|
66
|
-
@cache.expects(:
|
67
|
-
@cache.expects(:
|
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
68
|
|
69
69
|
@backend.lookup("key", {}, nil, :array).should == ["answer", "answer"]
|
70
70
|
end
|
@@ -75,7 +75,7 @@ class Hiera
|
|
75
75
|
Backend.expects(:datafile).with(:json, {"rspec" => "test"}, "one", "json").returns("/nonexisting/one.json")
|
76
76
|
|
77
77
|
File.expects(:exist?).with("/nonexisting/one.json").returns(true)
|
78
|
-
@cache.expects(:
|
78
|
+
@cache.expects(:read_file).with("/nonexisting/one.json", Hash).returns({"key" => "test_%{rspec}"})
|
79
79
|
|
80
80
|
@backend.lookup("key", {"rspec" => "test"}, nil, :priority).should == "test_test"
|
81
81
|
end
|
@@ -32,7 +32,7 @@ class Hiera
|
|
32
32
|
Backend.expects(:datasources).multiple_yields(["one"], ["two"])
|
33
33
|
Backend.expects(:datafile).with(:yaml, {}, "one", "yaml").returns("/nonexisting/one.yaml")
|
34
34
|
Backend.expects(:datafile).with(:yaml, {}, "two", "yaml").returns(nil).never
|
35
|
-
@cache.expects(:
|
35
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"answer"})
|
36
36
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
37
37
|
|
38
38
|
@backend.lookup("key", {}, nil, :priority).should == "answer"
|
@@ -50,7 +50,7 @@ class Hiera
|
|
50
50
|
Backend.expects(:datasources).multiple_yields(["one"])
|
51
51
|
Backend.expects(:datafile).with(:yaml, {}, "one", "yaml").returns("/nonexisting/one.yaml")
|
52
52
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
53
|
-
@cache.expects(:
|
53
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({})
|
54
54
|
|
55
55
|
@backend.lookup("key", {}, nil, :priority).should be_nil
|
56
56
|
end
|
@@ -62,8 +62,8 @@ class Hiera
|
|
62
62
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
63
63
|
File.stubs(:exist?).with("/nonexisting/two.yaml").returns(true)
|
64
64
|
|
65
|
-
@cache.expects(:
|
66
|
-
@cache.expects(:
|
65
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"answer"})
|
66
|
+
@cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>"answer"})
|
67
67
|
|
68
68
|
@backend.lookup("key", {}, nil, :array).should == ["answer", "answer"]
|
69
69
|
end
|
@@ -75,8 +75,8 @@ class Hiera
|
|
75
75
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
76
76
|
File.stubs(:exist?).with("/nonexisting/two.yaml").returns(true)
|
77
77
|
|
78
|
-
@cache.expects(:
|
79
|
-
@cache.expects(:
|
78
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({})
|
79
|
+
@cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
|
80
80
|
|
81
81
|
@backend.lookup("key", {}, nil, :hash).should == {"a" => "answer"}
|
82
82
|
end
|
@@ -88,8 +88,8 @@ class Hiera
|
|
88
88
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
89
89
|
File.stubs(:exist?).with("/nonexisting/two.yaml").returns(true)
|
90
90
|
|
91
|
-
@cache.expects(:
|
92
|
-
@cache.expects(:
|
91
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
|
92
|
+
@cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"b"=>"answer", "a"=>"wrong"}})
|
93
93
|
|
94
94
|
@backend.lookup("key", {}, nil, :hash).should == {"a" => "answer", "b" => "answer"}
|
95
95
|
end
|
@@ -101,8 +101,8 @@ class Hiera
|
|
101
101
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
102
102
|
File.stubs(:exist?).with("/nonexisting/two.yaml").returns(true)
|
103
103
|
|
104
|
-
@cache.expects(:
|
105
|
-
@cache.expects(:
|
104
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>["a", "answer"]})
|
105
|
+
@cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
|
106
106
|
|
107
107
|
expect {@backend.lookup("key", {}, nil, :array)}.to raise_error(Exception, "Hiera type mismatch: expected Array and got Hash")
|
108
108
|
end
|
@@ -114,8 +114,8 @@ class Hiera
|
|
114
114
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
115
115
|
File.stubs(:exist?).with("/nonexisting/two.yaml").returns(true)
|
116
116
|
|
117
|
-
@cache.expects(:
|
118
|
-
@cache.expects(:
|
117
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>{"a"=>"answer"}})
|
118
|
+
@cache.expects(:read_file).with("/nonexisting/two.yaml", Hash).returns({"key"=>["a", "wrong"]})
|
119
119
|
|
120
120
|
expect { @backend.lookup("key", {}, nil, :hash) }.to raise_error(Exception, "Hiera type mismatch: expected Hash and got Array")
|
121
121
|
end
|
@@ -125,7 +125,7 @@ class Hiera
|
|
125
125
|
Backend.expects(:datafile).with(:yaml, {"rspec" => "test"}, "one", "yaml").returns("/nonexisting/one.yaml")
|
126
126
|
File.stubs(:exist?).with("/nonexisting/one.yaml").returns(true)
|
127
127
|
|
128
|
-
@cache.expects(:
|
128
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).returns({"key"=>"test_%{rspec}"})
|
129
129
|
|
130
130
|
@backend.lookup("key", {"rspec" => "test"}, nil, :priority).should == "test_test"
|
131
131
|
end
|
@@ -137,7 +137,7 @@ class Hiera
|
|
137
137
|
|
138
138
|
yaml = "---\nstringval: 'string'\nboolval: true\nnumericval: 1"
|
139
139
|
|
140
|
-
@cache.expects(:
|
140
|
+
@cache.expects(:read_file).with("/nonexisting/one.yaml", Hash).times(3).returns({"boolval"=>true, "numericval"=>1, "stringval"=>"string"})
|
141
141
|
|
142
142
|
@backend.lookup("stringval", {}, nil, :priority).should == "string"
|
143
143
|
@backend.lookup("boolval", {}, nil, :priority).should == true
|
data/spec/unit/backend_spec.rb
CHANGED
@@ -5,15 +5,30 @@ class Hiera
|
|
5
5
|
describe Backend do
|
6
6
|
describe "#datadir" do
|
7
7
|
it "interpolates any values in the configured value" do
|
8
|
-
Config.load({:rspec => {:datadir => "/tmp"}})
|
9
|
-
|
10
|
-
Backend.datadir(:rspec, {})
|
8
|
+
Config.load({:rspec => {:datadir => "/tmp/%{interpolate}"}})
|
9
|
+
|
10
|
+
dir = Backend.datadir(:rspec, { "interpolate" => "my_data" })
|
11
|
+
|
12
|
+
dir.should == "/tmp/my_data"
|
11
13
|
end
|
12
14
|
|
13
15
|
it "defaults to a directory in var" do
|
14
16
|
Config.load({})
|
15
|
-
Backend.
|
16
|
-
|
17
|
+
Backend.datadir(:rspec, {}).should == Hiera::Util.var_dir
|
18
|
+
|
19
|
+
Config.load({:rspec => nil})
|
20
|
+
Backend.datadir(:rspec, {}).should == Hiera::Util.var_dir
|
21
|
+
|
22
|
+
Config.load({:rspec => {}})
|
23
|
+
Backend.datadir(:rspec, {}).should == Hiera::Util.var_dir
|
24
|
+
end
|
25
|
+
|
26
|
+
it "fails when the datadir is an array" do
|
27
|
+
Config.load({:rspec => {:datadir => []}})
|
28
|
+
|
29
|
+
expect do
|
30
|
+
Backend.datadir(:rspec, {})
|
31
|
+
end.to raise_error(Hiera::InvalidConfigurationError, /datadir for rspec cannot be an array/)
|
17
32
|
end
|
18
33
|
end
|
19
34
|
|
@@ -103,11 +118,19 @@ class Hiera
|
|
103
118
|
Backend.parse_string(input, {}).should == input
|
104
119
|
end
|
105
120
|
|
106
|
-
|
107
|
-
|
108
|
-
|
121
|
+
@scope_interpolation_tests = {
|
122
|
+
"replace %{part1} and %{part2}" =>
|
123
|
+
"replace value of part1 and value of part2",
|
124
|
+
"replace %{scope('part1')} and %{scope('part2')}" =>
|
125
|
+
"replace value of part1 and value of part2"
|
126
|
+
}
|
127
|
+
|
128
|
+
@scope_interpolation_tests.each do |input, expected|
|
129
|
+
it "replaces interpolations with data looked up in the scope" do
|
130
|
+
scope = {"part1" => "value of part1", "part2" => "value of part2"}
|
109
131
|
|
110
|
-
|
132
|
+
Backend.parse_string(input, scope).should == expected
|
133
|
+
end
|
111
134
|
end
|
112
135
|
|
113
136
|
it "replaces interpolations with data looked up in extra_data when scope does not contain the value" do
|
@@ -120,14 +143,27 @@ class Hiera
|
|
120
143
|
Backend.parse_string(input, {"rspec" => "test"}, {"rspec" => "fail"}).should == "test_test_test"
|
121
144
|
end
|
122
145
|
|
123
|
-
|
124
|
-
|
125
|
-
|
146
|
+
@interprets_nil_in_scope_tests = {
|
147
|
+
"test_%{rspec}_test" => "test__test",
|
148
|
+
"test_%{scope('rspec')}_test" => "test__test"
|
149
|
+
}
|
150
|
+
|
151
|
+
@interprets_nil_in_scope_tests.each do |input, expected|
|
152
|
+
it "interprets nil in scope as a non-value" do
|
153
|
+
Backend.parse_string(input, {"rspec" => nil}).should == expected
|
154
|
+
end
|
126
155
|
end
|
127
156
|
|
128
|
-
|
129
|
-
|
130
|
-
|
157
|
+
@interprets_false_in_scope_tests = {
|
158
|
+
"test_%{rspec}_test" => "test_false_test",
|
159
|
+
"test_%{scope('rspec')}_test" => "test_false_test"
|
160
|
+
}
|
161
|
+
|
162
|
+
@interprets_false_in_scope_tests.each do |input, expected|
|
163
|
+
it "interprets false in scope as a real value" do
|
164
|
+
input = "test_%{scope('rspec')}_test"
|
165
|
+
Backend.parse_string(input, {"rspec" => false}).should == expected
|
166
|
+
end
|
131
167
|
end
|
132
168
|
|
133
169
|
it "interprets false in extra_data as a real value" do
|
@@ -140,9 +176,15 @@ class Hiera
|
|
140
176
|
Backend.parse_string(input, {}, {"rspec" => nil}).should == "test__test"
|
141
177
|
end
|
142
178
|
|
143
|
-
|
144
|
-
|
145
|
-
|
179
|
+
@interprets_undefined_in_scope_tests = {
|
180
|
+
"test_%{rspec}_test" => "test__test",
|
181
|
+
"test_%{scope('rspec')}_test" => "test__test"
|
182
|
+
}
|
183
|
+
|
184
|
+
@interprets_undefined_in_scope_tests.each do |input, expected|
|
185
|
+
it "interprets :undefined in scope as a non-value" do
|
186
|
+
Backend.parse_string(input, {"rspec" => :undefined}).should == expected
|
187
|
+
end
|
146
188
|
end
|
147
189
|
|
148
190
|
it "uses the value from extra_data when scope is :undefined" do
|
@@ -150,24 +192,51 @@ class Hiera
|
|
150
192
|
Backend.parse_string(input, {"rspec" => :undefined}, { "rspec" => "extra" }).should == "test_extra_test"
|
151
193
|
end
|
152
194
|
|
153
|
-
|
154
|
-
|
155
|
-
|
195
|
+
@exact_lookup_tests = {
|
196
|
+
"test_%{::rspec::data}_test" => "test_value_test",
|
197
|
+
"test_%{scope('::rspec::data')}_test" => "test_value_test"
|
198
|
+
}
|
199
|
+
|
200
|
+
@exact_lookup_tests.each do |input, expected|
|
201
|
+
it "looks up the interpolated value exactly as it appears in the input" do
|
202
|
+
Backend.parse_string(input, {"::rspec::data" => "value"}).should == expected
|
203
|
+
end
|
156
204
|
end
|
157
205
|
|
158
|
-
|
159
|
-
|
160
|
-
|
206
|
+
@surrounding_whitespace_tests = {
|
207
|
+
"test_%{\trspec::data }_test" => "test_value_test",
|
208
|
+
"test_%{scope('\trspec::data ')}_test" => "test_value_test"
|
209
|
+
}
|
210
|
+
@surrounding_whitespace_tests.each do |input, expected|
|
211
|
+
it "does not remove any surrounding whitespace when parsing the key to lookup" do
|
212
|
+
Backend.parse_string(input, {"\trspec::data " => "value"}).should == expected
|
213
|
+
end
|
161
214
|
end
|
162
215
|
|
163
|
-
|
164
|
-
|
165
|
-
|
216
|
+
@leading_double_colon_tests = {
|
217
|
+
"test_%{::rspec::data}_test" => "test__test",
|
218
|
+
"test_%{scope('::rspec::data')}_test" => "test__test"
|
219
|
+
}
|
220
|
+
|
221
|
+
@leading_double_colon_tests.each do |input, expected|
|
222
|
+
it "does not try removing leading :: when a full lookup fails (#17434)" do
|
223
|
+
Backend.parse_string(input, {"rspec::data" => "value"}).should == expected
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
@double_colon_key_tests = {
|
228
|
+
"test_%{::rspec::data}_test" => "test__test",
|
229
|
+
"test_%{scope('::rspec::data')}_test" => "test__test"
|
230
|
+
}
|
231
|
+
@double_colon_key_tests.each do |input, expected|
|
232
|
+
it "does not try removing leading sections separated by :: when a full lookup fails (#17434)" do
|
233
|
+
Backend.parse_string(input, {"data" => "value"}).should == expected
|
234
|
+
end
|
166
235
|
end
|
167
236
|
|
168
|
-
it "does not try removing
|
169
|
-
input = "test_%{
|
170
|
-
Backend.parse_string(input, {"
|
237
|
+
it "does not try removing unknown, preceeding characters when looking up values" do
|
238
|
+
input = "test_%{$var}_test"
|
239
|
+
Backend.parse_string(input, {"$var" => "value"}).should == "test_value_test"
|
171
240
|
end
|
172
241
|
|
173
242
|
it "looks up recursively" do
|
@@ -181,7 +250,17 @@ class Hiera
|
|
181
250
|
input = "test_%{first}_test"
|
182
251
|
expect do
|
183
252
|
Backend.parse_string(input, scope)
|
184
|
-
end.to raise_error
|
253
|
+
end.to raise_error Hiera::InterpolationLoop, "Detected in [first, second]"
|
254
|
+
end
|
255
|
+
|
256
|
+
it "replaces hiera interpolations with data looked up in hiera" do
|
257
|
+
input = "%{hiera('key1')}"
|
258
|
+
scope = {}
|
259
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
260
|
+
Config.load_backends
|
261
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("key1", scope, nil, :priority).returns("answer")
|
262
|
+
|
263
|
+
Backend.parse_string(input, scope).should == "answer"
|
185
264
|
end
|
186
265
|
end
|
187
266
|
|
@@ -201,11 +280,93 @@ class Hiera
|
|
201
280
|
Backend.parse_answer(input, {"rspec" => "test"}).should == {"foo"=>"test_test_test", "bar"=>"test_test_test"}
|
202
281
|
end
|
203
282
|
|
283
|
+
it "interpolates string in hash keys" do
|
284
|
+
input = {"%{rspec}" => "test"}
|
285
|
+
Backend.parse_answer(input, {"rspec" => "foo"}).should == {"foo"=>"test"}
|
286
|
+
end
|
287
|
+
|
288
|
+
it "interpolates strings in nested hash keys" do
|
289
|
+
input = {"topkey" => {"%{rspec}" => "test"}}
|
290
|
+
Backend.parse_answer(input, {"rspec" => "foo"}).should == {"topkey"=>{"foo" => "test"}}
|
291
|
+
end
|
292
|
+
|
204
293
|
it "interpolates strings in a mixed structure of arrays and hashes" do
|
205
294
|
input = {"foo" => "test_%{rspec}_test", "bar" => ["test_%{rspec}_test", "test_%{rspec}_test"]}
|
206
295
|
Backend.parse_answer(input, {"rspec" => "test"}).should == {"foo"=>"test_test_test", "bar"=>["test_test_test", "test_test_test"]}
|
207
296
|
end
|
208
297
|
|
298
|
+
it "interpolates hiera lookups values in strings" do
|
299
|
+
input = "test_%{hiera('rspec')}_test"
|
300
|
+
scope = {}
|
301
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
302
|
+
Config.load_backends
|
303
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("test")
|
304
|
+
Backend.parse_answer(input, scope).should == "test_test_test"
|
305
|
+
end
|
306
|
+
|
307
|
+
it "interpolates hiera lookups in each string in an array" do
|
308
|
+
input = ["test_%{hiera('rspec')}_test", "test_%{hiera('rspec')}_test", ["test_%{hiera('rspec')}_test"]]
|
309
|
+
scope = {}
|
310
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
311
|
+
Config.load_backends
|
312
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("test")
|
313
|
+
Backend.parse_answer(input, scope).should == ["test_test_test", "test_test_test", ["test_test_test"]]
|
314
|
+
end
|
315
|
+
|
316
|
+
it "interpolates hiera lookups in each string in a hash" do
|
317
|
+
input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => "test_%{hiera('rspec')}_test"}
|
318
|
+
scope = {}
|
319
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
320
|
+
Config.load_backends
|
321
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("test")
|
322
|
+
Backend.parse_answer(input, scope).should == {"foo"=>"test_test_test", "bar"=>"test_test_test"}
|
323
|
+
end
|
324
|
+
|
325
|
+
it "interpolates hiera lookups in string in hash keys" do
|
326
|
+
input = {"%{hiera('rspec')}" => "test"}
|
327
|
+
scope = {}
|
328
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
329
|
+
Config.load_backends
|
330
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("foo")
|
331
|
+
Backend.parse_answer(input, scope).should == {"foo"=>"test"}
|
332
|
+
end
|
333
|
+
|
334
|
+
it "interpolates hiera lookups in strings in nested hash keys" do
|
335
|
+
input = {"topkey" => {"%{hiera('rspec')}" => "test"}}
|
336
|
+
scope = {}
|
337
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
338
|
+
Config.load_backends
|
339
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("foo")
|
340
|
+
Backend.parse_answer(input, scope).should == {"topkey"=>{"foo" => "test"}}
|
341
|
+
end
|
342
|
+
|
343
|
+
it "interpolates hiera lookups in strings in a mixed structure of arrays and hashes" do
|
344
|
+
input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => ["test_%{hiera('rspec')}_test", "test_%{hiera('rspec')}_test"]}
|
345
|
+
scope = {}
|
346
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
347
|
+
Config.load_backends
|
348
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("test")
|
349
|
+
Backend.parse_answer(input, scope).should == {"foo"=>"test_test_test", "bar"=>["test_test_test", "test_test_test"]}
|
350
|
+
end
|
351
|
+
|
352
|
+
it "interpolates hiera lookups and scope lookups in the same string" do
|
353
|
+
input = {"foo" => "test_%{hiera('rspec')}_test", "bar" => "test_%{rspec2}_test"}
|
354
|
+
scope = {"rspec2" => "scope_rspec"}
|
355
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
356
|
+
Config.load_backends
|
357
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("hiera_rspec")
|
358
|
+
Backend.parse_answer(input, scope).should == {"foo"=>"test_hiera_rspec_test", "bar"=>"test_scope_rspec_test"}
|
359
|
+
end
|
360
|
+
|
361
|
+
it "interpolates hiera and scope lookups with the same lookup query in a single string" do
|
362
|
+
input = "test_%{hiera('rspec')}_test_%{rspec}"
|
363
|
+
scope = {"rspec" => "scope_rspec"}
|
364
|
+
Config.load({:yaml => {:datadir => "/tmp"}})
|
365
|
+
Config.load_backends
|
366
|
+
Backend::Yaml_backend.any_instance.stubs(:lookup).with("rspec", scope, nil, :priority).returns("hiera_rspec")
|
367
|
+
Backend.parse_answer(input, scope).should == "test_hiera_rspec_test_scope_rspec"
|
368
|
+
end
|
369
|
+
|
209
370
|
it "passes integers unchanged" do
|
210
371
|
input = 1
|
211
372
|
Backend.parse_answer(input, {"rspec" => "test"}).should == 1
|
@@ -225,6 +386,12 @@ class Hiera
|
|
225
386
|
input = false
|
226
387
|
Backend.parse_answer(input, {"rspec" => "test"}).should == false
|
227
388
|
end
|
389
|
+
|
390
|
+
it "interpolates lookups using single or double quotes" do
|
391
|
+
input = "test_%{scope(\"rspec\")}_test_%{scope('rspec')}"
|
392
|
+
scope = {"rspec" => "scope_rspec"}
|
393
|
+
Backend.parse_answer(input, scope).should == "test_scope_rspec_test_scope_rspec"
|
394
|
+
end
|
228
395
|
end
|
229
396
|
|
230
397
|
describe "#resolve_answer" do
|
@@ -244,6 +411,7 @@ class Hiera
|
|
244
411
|
end
|
245
412
|
|
246
413
|
it "caches loaded backends" do
|
414
|
+
Backend.clear!
|
247
415
|
Hiera.expects(:debug).with(regexp_matches(/Hiera YAML backend starting/)).once
|
248
416
|
|
249
417
|
Config.load({:yaml => {:datadir => "/tmp"}})
|
data/spec/unit/filecache_spec.rb
CHANGED
@@ -1,62 +1,141 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'tmpdir'
|
2
3
|
|
3
4
|
class Hiera
|
4
5
|
describe Filecache do
|
5
6
|
before do
|
6
|
-
File.stubs(:exist?).returns(true)
|
7
7
|
@cache = Filecache.new
|
8
8
|
end
|
9
9
|
|
10
|
+
def write_file(file, contents)
|
11
|
+
File.open(file, 'w') do |f|
|
12
|
+
f.write(contents)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
10
16
|
describe "#read" do
|
11
|
-
it "
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
it "reads data from a file" do
|
18
|
+
Dir.mktmpdir do |dir|
|
19
|
+
file = File.join(dir, "testing")
|
20
|
+
write_file(file, "my data")
|
21
|
+
|
22
|
+
@cache.read(file).should == "my data"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "rereads data when the file changes" do
|
27
|
+
Dir.mktmpdir do |dir|
|
28
|
+
file = File.join(dir, "testing")
|
29
|
+
write_file(file, "my data")
|
30
|
+
@cache.read(file).should == "my data"
|
15
31
|
|
16
|
-
|
17
|
-
|
32
|
+
write_file(file, "changed data")
|
33
|
+
@cache.read(file).should == "changed data"
|
34
|
+
end
|
18
35
|
end
|
19
36
|
|
20
|
-
it "
|
21
|
-
|
37
|
+
it "uses the provided default when the type does not match the expected type" do
|
38
|
+
Hiera.expects(:debug).with(regexp_matches(/String.*not.*Hash, setting defaults/))
|
39
|
+
Dir.mktmpdir do |dir|
|
40
|
+
file = File.join(dir, "testing")
|
41
|
+
write_file(file, "my data")
|
42
|
+
data = @cache.read(file, Hash, { :testing => "hash" }) do |data|
|
43
|
+
"a string"
|
44
|
+
end
|
22
45
|
|
23
|
-
|
46
|
+
data.should == { :testing => "hash" }
|
47
|
+
end
|
48
|
+
end
|
24
49
|
|
25
|
-
|
50
|
+
it "traps any errors from the block and uses the default value" do
|
51
|
+
Hiera.expects(:debug).with(regexp_matches(/Reading data.*failed:.*testing error/))
|
52
|
+
Dir.mktmpdir do |dir|
|
53
|
+
file = File.join(dir, "testing")
|
54
|
+
write_file(file, "my data")
|
55
|
+
data = @cache.read(file, Hash, { :testing => "hash" }) do |data|
|
56
|
+
raise ArgumentError, "testing error"
|
57
|
+
end
|
26
58
|
|
27
|
-
|
28
|
-
data = @cache.read("/nonexisting", Hash, {"rspec" => 1}) do |data|
|
29
|
-
nil
|
59
|
+
data.should == { :testing => "hash" }
|
30
60
|
end
|
61
|
+
end
|
31
62
|
|
32
|
-
|
63
|
+
it "raises an error when there is no default given and there is a problem" do
|
64
|
+
Dir.mktmpdir do |dir|
|
65
|
+
file = File.join(dir, "testing")
|
66
|
+
write_file(file, "my data")
|
67
|
+
|
68
|
+
expect do
|
69
|
+
@cache.read(file, Hash) do |data|
|
70
|
+
raise ArgumentError, "testing error"
|
71
|
+
end
|
72
|
+
end.to raise_error(ArgumentError, "testing error")
|
73
|
+
end
|
33
74
|
end
|
34
75
|
end
|
35
76
|
|
36
|
-
describe "#
|
37
|
-
it "
|
38
|
-
|
77
|
+
describe "#read_file" do
|
78
|
+
it "reads data from a file" do
|
79
|
+
Dir.mktmpdir do |dir|
|
80
|
+
file = File.join(dir, "testing")
|
81
|
+
write_file(file, "my data")
|
39
82
|
|
40
|
-
|
41
|
-
|
42
|
-
@cache.stale?("/nonexisting").should == false
|
83
|
+
@cache.read_file(file).should == "my data"
|
84
|
+
end
|
43
85
|
end
|
44
86
|
|
45
|
-
it "
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
87
|
+
it "rereads data when the file changes" do
|
88
|
+
Dir.mktmpdir do |dir|
|
89
|
+
file = File.join(dir, "testing")
|
90
|
+
write_file(file, "my data")
|
91
|
+
@cache.read_file(file).should == "my data"
|
92
|
+
|
93
|
+
write_file(file, "changed data")
|
94
|
+
@cache.read_file(file).should == "changed data"
|
95
|
+
end
|
50
96
|
end
|
51
|
-
end
|
52
97
|
|
53
|
-
|
54
|
-
|
55
|
-
|
98
|
+
it "errors when the type does not match the expected type" do
|
99
|
+
Dir.mktmpdir do |dir|
|
100
|
+
file = File.join(dir, "testing")
|
101
|
+
write_file(file, "my data")
|
56
102
|
|
57
|
-
|
103
|
+
expect do
|
104
|
+
@cache.read_file(file, Hash) do |data|
|
105
|
+
"a string"
|
106
|
+
end
|
107
|
+
end.to raise_error(TypeError)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it "converts the read data using the block" do
|
112
|
+
Dir.mktmpdir do |dir|
|
113
|
+
file = File.join(dir, "testing")
|
114
|
+
write_file(file, "my data")
|
58
115
|
|
59
|
-
|
116
|
+
@cache.read_file(file, Hash) do |data|
|
117
|
+
{ :data => data }
|
118
|
+
end.should == { :data => "my data" }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "errors when the file does not exist" do
|
123
|
+
expect do
|
124
|
+
@cache.read_file("/notexist")
|
125
|
+
end.to raise_error(Errno::ENOENT)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "propogates any errors from the block" do
|
129
|
+
Dir.mktmpdir do |dir|
|
130
|
+
file = File.join(dir, "testing")
|
131
|
+
write_file(file, "my data")
|
132
|
+
|
133
|
+
expect do
|
134
|
+
@cache.read_file(file) do |data|
|
135
|
+
raise ArgumentError, "testing error"
|
136
|
+
end
|
137
|
+
end.to raise_error(ArgumentError, "testing error")
|
138
|
+
end
|
60
139
|
end
|
61
140
|
end
|
62
141
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "hiera/version"
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
describe "Hiera.version Public API" do
|
6
|
+
subject() { Hiera }
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
Hiera.instance_eval do
|
10
|
+
if @hiera_version
|
11
|
+
@hiera_version = nil
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "without a VERSION file" do
|
17
|
+
before :each do
|
18
|
+
subject.stubs(:read_version_file).returns(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "is Hiera::VERSION" do
|
22
|
+
subject.version.should == Hiera::VERSION
|
23
|
+
end
|
24
|
+
it "respects the version= setter" do
|
25
|
+
subject.version = '1.2.3'
|
26
|
+
subject.version.should == '1.2.3'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with a VERSION file" do
|
31
|
+
it "is the content of the file" do
|
32
|
+
subject.expects(:read_version_file).with() do |path|
|
33
|
+
pathname = Pathname.new(path)
|
34
|
+
pathname.basename.to_s == "VERSION"
|
35
|
+
end.returns('1.2.1-9-g9fda440')
|
36
|
+
|
37
|
+
subject.version.should == '1.2.1-9-g9fda440'
|
38
|
+
end
|
39
|
+
it "respects the version= setter" do
|
40
|
+
subject.version = '1.2.3'
|
41
|
+
subject.version.should == '1.2.3'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
metadata
CHANGED
@@ -1,114 +1,104 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: hiera
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 2
|
9
|
-
- 1
|
10
|
-
version: 1.2.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.3.0.rc2
|
5
|
+
prerelease: 6
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Puppet Labs
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2013-11-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: json_pure
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
26
|
-
- -
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
version: "0"
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
32
22
|
type: :runtime
|
33
|
-
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
34
30
|
description: A pluggable data store for hierarcical data
|
35
31
|
email: info@puppetlabs.com
|
36
|
-
executables:
|
32
|
+
executables:
|
37
33
|
- hiera
|
38
34
|
extensions: []
|
39
|
-
|
40
35
|
extra_rdoc_files: []
|
41
|
-
|
42
|
-
files:
|
36
|
+
files:
|
43
37
|
- bin/hiera
|
44
|
-
- lib/hiera
|
45
|
-
- lib/hiera/recursive_lookup.rb
|
38
|
+
- lib/hiera.rb
|
46
39
|
- lib/hiera/console_logger.rb
|
47
|
-
- lib/hiera/filecache.rb
|
48
|
-
- lib/hiera/fallback_logger.rb
|
49
|
-
- lib/hiera/util.rb
|
50
|
-
- lib/hiera/backend.rb
|
51
40
|
- lib/hiera/noop_logger.rb
|
52
|
-
- lib/hiera/config.rb
|
53
41
|
- lib/hiera/backend/yaml_backend.rb
|
54
42
|
- lib/hiera/backend/json_backend.rb
|
55
|
-
- lib/hiera.rb
|
43
|
+
- lib/hiera/fallback_logger.rb
|
44
|
+
- lib/hiera/filecache.rb
|
45
|
+
- lib/hiera/version.rb
|
46
|
+
- lib/hiera/config.rb
|
47
|
+
- lib/hiera/interpolate.rb
|
48
|
+
- lib/hiera/recursive_guard.rb
|
49
|
+
- lib/hiera/util.rb
|
50
|
+
- lib/hiera/backend.rb
|
51
|
+
- lib/hiera/puppet_logger.rb
|
52
|
+
- lib/hiera/error.rb
|
56
53
|
- COPYING
|
57
54
|
- README.md
|
58
55
|
- LICENSE
|
59
|
-
- spec/unit/util_spec.rb
|
60
|
-
- spec/unit/puppet_logger_spec.rb
|
61
|
-
- spec/unit/config_spec.rb
|
62
|
-
- spec/unit/backend_spec.rb
|
63
|
-
- spec/unit/filecache_spec.rb
|
64
56
|
- spec/unit/hiera_spec.rb
|
65
|
-
- spec/unit/
|
57
|
+
- spec/unit/filecache_spec.rb
|
66
58
|
- spec/unit/backend/yaml_backend_spec.rb
|
67
59
|
- spec/unit/backend/json_backend_spec.rb
|
60
|
+
- spec/unit/console_logger_spec.rb
|
61
|
+
- spec/unit/backend_spec.rb
|
62
|
+
- spec/unit/version_spec.rb
|
63
|
+
- spec/unit/puppet_logger_spec.rb
|
64
|
+
- spec/unit/config_spec.rb
|
68
65
|
- spec/unit/fallback_logger_spec.rb
|
66
|
+
- spec/unit/util_spec.rb
|
69
67
|
- spec/spec_helper.rb
|
70
68
|
homepage: https://github.com/puppetlabs/hiera
|
71
69
|
licenses: []
|
72
|
-
|
73
70
|
post_install_message:
|
74
71
|
rdoc_options: []
|
75
|
-
|
76
|
-
require_paths:
|
72
|
+
require_paths:
|
77
73
|
- lib
|
78
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
79
75
|
none: false
|
80
|
-
requirements:
|
81
|
-
- -
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
|
84
|
-
|
85
|
-
- 0
|
86
|
-
version: "0"
|
87
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
81
|
none: false
|
89
|
-
requirements:
|
90
|
-
- -
|
91
|
-
- !ruby/object:Gem::Version
|
92
|
-
|
93
|
-
segments:
|
94
|
-
- 0
|
95
|
-
version: "0"
|
82
|
+
requirements:
|
83
|
+
- - ! '>'
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.3.1
|
96
86
|
requirements: []
|
97
|
-
|
98
87
|
rubyforge_project:
|
99
|
-
rubygems_version: 1.8.
|
88
|
+
rubygems_version: 1.8.23
|
100
89
|
signing_key:
|
101
90
|
specification_version: 3
|
102
91
|
summary: Light weight hierarchical data store
|
103
|
-
test_files:
|
104
|
-
- spec/unit/util_spec.rb
|
105
|
-
- spec/unit/puppet_logger_spec.rb
|
106
|
-
- spec/unit/config_spec.rb
|
107
|
-
- spec/unit/backend_spec.rb
|
108
|
-
- spec/unit/filecache_spec.rb
|
92
|
+
test_files:
|
109
93
|
- spec/unit/hiera_spec.rb
|
110
|
-
- spec/unit/
|
94
|
+
- spec/unit/filecache_spec.rb
|
111
95
|
- spec/unit/backend/yaml_backend_spec.rb
|
112
96
|
- spec/unit/backend/json_backend_spec.rb
|
97
|
+
- spec/unit/console_logger_spec.rb
|
98
|
+
- spec/unit/backend_spec.rb
|
99
|
+
- spec/unit/version_spec.rb
|
100
|
+
- spec/unit/puppet_logger_spec.rb
|
101
|
+
- spec/unit/config_spec.rb
|
113
102
|
- spec/unit/fallback_logger_spec.rb
|
103
|
+
- spec/unit/util_spec.rb
|
114
104
|
- spec/spec_helper.rb
|
@@ -1,31 +0,0 @@
|
|
1
|
-
# Allow for safe recursive lookup of values during variable interpolation.
|
2
|
-
#
|
3
|
-
# @api private
|
4
|
-
class Hiera::RecursiveLookup
|
5
|
-
def initialize(scope, extra_data)
|
6
|
-
@seen = []
|
7
|
-
@scope = scope
|
8
|
-
@extra_data = extra_data
|
9
|
-
end
|
10
|
-
|
11
|
-
def lookup(name, &block)
|
12
|
-
if @seen.include?(name)
|
13
|
-
raise Exception, "Interpolation loop detected in [#{@seen.join(', ')}]"
|
14
|
-
end
|
15
|
-
@seen.push(name)
|
16
|
-
ret = yield(current_value)
|
17
|
-
@seen.pop
|
18
|
-
ret
|
19
|
-
end
|
20
|
-
|
21
|
-
def current_value
|
22
|
-
name = @seen.last
|
23
|
-
|
24
|
-
scope_val = @scope[name]
|
25
|
-
if scope_val.nil? || scope_val == :undefined
|
26
|
-
@extra_data[name]
|
27
|
-
else
|
28
|
-
scope_val
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|