hiera 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -101,6 +101,48 @@ $ hiera ssh_users.0
101
101
  root
102
102
  </pre>
103
103
 
104
+ ### Use quotes to disable qualified key behavior
105
+ In case you have dotted keys and thus want to avoid using the qualified key semantics, you
106
+ can put segments of a dotted key, or the whole key, within quotes.
107
+
108
+ Given the following data:
109
+
110
+ <pre>
111
+ # yaml
112
+ a:
113
+ b.c:
114
+ d: 'Data for a => b.c => d'
115
+ </pre>
116
+
117
+ it is possible to do a lookup of the data like this:
118
+
119
+ <pre>
120
+ $ hiera 'a."b.c".d'
121
+ Data for a => b.c => d
122
+ </pre>
123
+
124
+ Quoting works in interpolation expressions as well.
125
+
126
+ Interpolating from global scope:
127
+
128
+ <pre>
129
+ # yaml
130
+ other.key: 'scope data: %{a."b.c".d}'
131
+ </pre>
132
+
133
+ or using an interpolation method:
134
+
135
+ <pre>
136
+ # yaml
137
+ a:
138
+ b.c:
139
+ d: 'Data for a => b.c => d'
140
+ other.key: 'hiera data %{hiera("a.''b.c''.d")}'
141
+ </pre>
142
+
143
+ Note that two single quotes are used to escape a single quote inside a single quoted string
144
+ (that's YAML syntax, not Hiera) and that the quoted key must be quoted in turn.
145
+
104
146
  ## Future Enhancements
105
147
 
106
148
  * More backends should be created
@@ -231,7 +231,7 @@ class Hiera
231
231
  # databases then do so in your constructor, future calls to your
232
232
  # backend will not create new instances
233
233
 
234
- # @param key [String] The key to lookup
234
+ # @param key [String] The key to lookup. May be quoted with single or double quotes to avoid subkey traversal on dot characters
235
235
  # @param scope [#[]] The primary source of data for substitutions.
236
236
  # @param order_override [#[],nil] An override that will be pre-pended to the hierarchy definition.
237
237
  # @param resolution_type [Symbol,Hash,nil] One of :hash, :array,:priority or a Hash with deep merge behavior and options
@@ -250,7 +250,7 @@ class Hiera
250
250
 
251
251
  strategy = resolution_type.is_a?(Hash) ? :hash : resolution_type
252
252
 
253
- segments = key.split('.')
253
+ segments = Util.split_key(key) { |problem| ArgumentError.new("#{problem} in key: #{key}") }
254
254
  subsegments = nil
255
255
  if segments.size > 1
256
256
  raise ArgumentError, "Resolution type :#{strategy} is illegal when doing segmented key lookups" unless strategy.nil? || strategy == :priority
@@ -265,7 +265,7 @@ class Hiera
265
265
  found_in_backend = false
266
266
  new_answer = catch(:no_such_key) do
267
267
  if subsegments.nil?
268
- value = backend.lookup(key, scope, order_override, resolution_type, context)
268
+ value = backend.lookup(segments[0], scope, order_override, resolution_type, context)
269
269
  elsif backend.respond_to?(:lookup_with_segments)
270
270
  value = backend.lookup_with_segments(segments, scope, order_override, resolution_type, context)
271
271
  else
@@ -4,34 +4,51 @@ require 'hiera/recursive_guard'
4
4
 
5
5
  class Hiera::InterpolationInvalidValue < StandardError; end
6
6
 
7
+ # @api private
7
8
  class Hiera::Interpolate
9
+ RX_INTERPOLATION = /%\{([^\}]*)\}/
10
+ RX_ONLY_INTERPOLATION = /^%\{([^\}]*)\}$/
11
+ RX_METHOD_AND_ARG = /^(\w+)\(([^)]*)\)$/
12
+
13
+ EMPTY_INTERPOLATIONS = {
14
+ '' => true,
15
+ '::' => true,
16
+ '""' => true,
17
+ "''" => true,
18
+ '"::"' => true,
19
+ "'::'" => true
20
+ }.freeze
21
+
22
+ INTERPOLATION_METHODS = {
23
+ 'hiera' => :hiera_interpolate,
24
+ 'scope' => :scope_interpolate,
25
+ 'literal' => :literal_interpolate,
26
+ 'alias' => :alias_interpolate
27
+ }.freeze
28
+
8
29
  class << self
30
+ # These two patterns are never used but kept here anyway since they used to be public and therefore
31
+ # must be considered API. The class is now marked @api private and these should be removed in a
32
+ # future version
33
+ #
34
+ # @deprecated
9
35
  INTERPOLATION = /%\{([^\}]*)\}/
36
+
37
+ # @deprecated
10
38
  METHOD_INTERPOLATION = /%\{(scope|hiera|literal|alias)\(['"]([^"']*)["']\)\}/
11
39
 
12
40
  def interpolate(data, scope, extra_data, context)
13
41
  if data.is_a?(String)
14
42
  # Wrapping do_interpolation in a gsub block ensures we process
15
43
  # each interpolation site in isolation using separate recursion guards.
16
- context ||= {}
17
- new_context = context.clone
44
+ new_context = context.nil? ? {} : context.clone
18
45
  new_context[:recurse_guard] ||= Hiera::RecursiveGuard.new
19
- data.gsub(INTERPOLATION) do |match|
20
- interp_val = do_interpolation(match, scope, extra_data, new_context)
21
-
22
- # Get interp method in case we are aliasing
23
- if data.is_a?(String) && (match = data.match(INTERPOLATION))
24
- interpolate_method, key = get_interpolation_method_and_key(data, new_context)
25
- else
26
- interpolate_method = nil
27
- end
46
+ data.gsub(RX_INTERPOLATION) do |match|
47
+ (interp_val, interpolate_method) = do_interpolation(match, scope, extra_data, new_context)
28
48
 
29
- if ( (interpolate_method == :alias_interpolate) and (!interp_val.is_a?(String)) )
30
- if data.match("^#{INTERPOLATION}$")
31
- return interp_val
32
- else
33
- raise Hiera::InterpolationInvalidValue, "Cannot call alias in the string context"
34
- end
49
+ if (interpolate_method == :alias_interpolate) && !interp_val.is_a?(String)
50
+ return interp_val if data.match(RX_ONLY_INTERPOLATION)
51
+ raise Hiera::InterpolationInvalidValue, "Cannot call alias in the string context"
35
52
  else
36
53
  interp_val
37
54
  end
@@ -42,47 +59,45 @@ class Hiera::Interpolate
42
59
  end
43
60
 
44
61
  def do_interpolation(data, scope, extra_data, context)
45
- if data.is_a?(String) && (match = data.match(INTERPOLATION))
62
+ if data.is_a?(String) && (match = data.match(RX_INTERPOLATION))
46
63
  interpolation_variable = match[1]
47
64
 
48
65
  # HI-494
49
- case interpolation_variable.strip
50
- when '', '::'
51
- return ''
52
- end
66
+ return ['', nil] if EMPTY_INTERPOLATIONS[interpolation_variable.strip]
53
67
 
54
68
  context[:recurse_guard].check(interpolation_variable) do
55
- interpolate_method, key = get_interpolation_method_and_key(data, context)
69
+ interpolate_method, key = get_interpolation_method_and_key(interpolation_variable, context)
56
70
  interpolated_data = send(interpolate_method, data, key, scope, extra_data, context)
57
71
 
58
72
  # Halt recursion if we encounter a literal.
59
- return interpolated_data if interpolate_method == :literal_interpolate
73
+ return [interpolated_data, interpolate_method] if interpolate_method == :literal_interpolate
60
74
 
61
- do_interpolation(interpolated_data, scope, extra_data, context)
75
+ [do_interpolation(interpolated_data, scope, extra_data, context)[0], interpolate_method]
62
76
  end
63
77
  else
64
- data
78
+ [data, nil]
65
79
  end
66
80
  end
67
81
  private :do_interpolation
68
82
 
69
- def get_interpolation_method_and_key(data, context)
70
- if (match = data.match(METHOD_INTERPOLATION))
83
+ def get_interpolation_method_and_key(interpolation_variable, context)
84
+ if (match = interpolation_variable.match(RX_METHOD_AND_ARG))
71
85
  Hiera.warn('Use of interpolation methods in hiera configuration file is deprecated') if context[:is_interpolate_config]
72
- case match[1]
73
- when 'hiera' then [:hiera_interpolate, match[2]]
74
- when 'scope' then [:scope_interpolate, match[2]]
75
- when 'literal' then [:literal_interpolate, match[2]]
76
- when 'alias' then [:alias_interpolate, match[2]]
77
- end
78
- elsif (match = data.match(INTERPOLATION))
79
- [:scope_interpolate, match[1]]
86
+ method = match[1]
87
+ method_sym = INTERPOLATION_METHODS[method]
88
+ raise Hiera::InterpolationInvalidValue, "Invalid interpolation method '#{method}'" unless method_sym
89
+ arg = match[2]
90
+ match_data = arg.match(Hiera::QUOTED_KEY)
91
+ raise Hiera::InterpolationInvalidValue, "Argument to interpolation method '#{method}' must be quoted, got '#{arg}'" unless match_data
92
+ [method_sym, match_data[1] || match_data[2]]
93
+ else
94
+ [:scope_interpolate, interpolation_variable]
80
95
  end
81
96
  end
82
97
  private :get_interpolation_method_and_key
83
98
 
84
99
  def scope_interpolate(data, key, scope, extra_data, context)
85
- segments = key.split('.')
100
+ segments = Hiera::Util.split_key(key) { |problem| Hiera::InterpolationInvalidValue.new("#{problem} in interpolation expression: #{data}") }
86
101
  catch(:no_such_key) { return Hiera::Backend.qualified_lookup(segments, scope) }
87
102
  catch(:no_such_key) { Hiera::Backend.qualified_lookup(segments, extra_data) }
88
103
  end
@@ -1,4 +1,9 @@
1
1
  class Hiera
2
+
3
+ # Matches a key that is quoted using a matching pair of either single or double quotes.
4
+ QUOTED_KEY = /^(?:"([^"]+)"|'([^']+)')$/
5
+ QUOTES = /[",]/
6
+
2
7
  module Util
3
8
  module_function
4
9
 
@@ -42,6 +47,23 @@ class Hiera
42
47
  def common_appdata
43
48
  Dir::COMMON_APPDATA
44
49
  end
50
+
51
+ def split_key(key)
52
+ segments = key.split(/(?:"([^"]+)"|'([^']+)'|([^'".]+))/)
53
+ if segments.empty?
54
+ # Only happens if the original key was an empty string
55
+ ''
56
+ elsif segments.shift == ''
57
+ count = segments.size
58
+ raise yield('Syntax error') unless count > 0
59
+
60
+ segments.keep_if { |seg| seg != '.' }
61
+ raise yield('Syntax error') unless segments.size * 2 == count + 1
62
+ segments
63
+ else
64
+ raise yield('Syntax error')
65
+ end
66
+ end
45
67
  end
46
68
  end
47
69
 
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
  class Hiera
10
- VERSION = "3.1.0"
10
+ VERSION = "3.1.1"
11
11
 
12
12
  ##
13
13
  # version is a public API method intended to always provide a fast and
@@ -4,4 +4,8 @@
4
4
  :hierarchy:
5
5
  - recursive
6
6
  - niltest
7
+ - complex
7
8
  - empty_%{}inter%{::}polation
9
+ - dotted_keys
10
+ - weird_keys
11
+ - bad_interpolation
@@ -0,0 +1,9 @@
1
+ ---
2
+ quote_mismatch: 'Key delimited with singe quote on one side aand double qoute on the other: %{''the.key"}'
3
+ quote_mismatch_arg: 'Arg delimited with singe quote on one side and double qoute on the other: %{hiera(''the.key")}'
4
+ non_existing_method: 'The method flubber does not exist: %{flubber("hello")}'
5
+
6
+ one_quote: 'Key with only one quote: %{the.''key}'
7
+ empty_segment: 'Key with only one quote: %{the..key}'
8
+ empty_quoted_segment: 'Key with only one quote: %{the.''''.key}'
9
+ partly_quoted_segment: 'Key with only one quote: %{the.''pa''key}'
@@ -0,0 +1,12 @@
1
+ ---
2
+ root:
3
+ a:
4
+ aa: "%{alias('aaa')}"
5
+
6
+ aaa:
7
+ b:
8
+ bb: "%{alias('bbb')}"
9
+
10
+ bbb: [ "%{alias('ccc')}" ]
11
+
12
+ ccc: text
@@ -0,0 +1,40 @@
1
+ ---
2
+ a.b: '(hiera) a dot b'
3
+ a.c.scope: "a dot c: %{'a.b'}"
4
+ a.c.hiera: 'a dot c: %{hiera("''a.b''")}'
5
+ a.c.scope: "a dot c: %{'a.b'}"
6
+ a.c.hiera: 'a dot c: %{hiera("''a.b''")}'
7
+ a.c.alias: '%{alias("''a.b''")}'
8
+ a:
9
+ d: '(hiera) a dot d is a hash entry'
10
+ d.x: '(hiera) a dot d.x is a hash entry'
11
+ d.z:
12
+ g: '(hiera) a dot d.z dot g is a hash entry'
13
+
14
+ a.x:
15
+ d: '(hiera) a.x dot d is a hash entry'
16
+ d.x: '(hiera) a.x dot d.x is a hash entry'
17
+ d.z:
18
+ g: '(hiera) a.x dot d.z dot g is a hash entry'
19
+
20
+ a.e.scope: "a dot e: %{a.d}"
21
+ a.e.hiera: "a dot e: %{hiera('a.d')}"
22
+
23
+ a.ex.scope: "a dot ex: %{a.'d.x'}"
24
+ a.ex.hiera: 'a dot ex: %{hiera("a.''d.x''")}'
25
+
26
+ a.xe.scope: "a dot xe: %{'a.x'.d}"
27
+ a.xe.hiera: 'a dot xe: %{hiera("''a.x''.d")}'
28
+
29
+ a.xm.scope: "a dot xm: %{a.'d.z'.g}"
30
+ a.xm.hiera: 'a dot xm: %{hiera("a.''d.z''.g")}'
31
+
32
+ a.xx.scope: "a dot xx: %{'a.x'.'d.z'.g}"
33
+ a.xx.hiera: 'a dot xx: %{hiera("''a.x''.''d.z''.g")}'
34
+
35
+ a.f.scope: "a dot f: %{'a.d'}"
36
+ a.f.hiera: 'a dot f: %{hiera("''a.d''")}'
37
+
38
+ x.1: '(hiera) x dot 1'
39
+ x.2.scope: "x dot 2: %{'x.1'}"
40
+ x.2.hiera: 'x dot 2: %{hiera("''x.1''")}'
@@ -4,3 +4,10 @@ only_empty_interpolation: '%{}'
4
4
  empty_namespace: '%{::}'
5
5
  whitespace1: '%{ :: }'
6
6
  whitespace2: '%{ }'
7
+
8
+ quoted_empty_interpolation: 'clown%{""}shoe'
9
+ quoted_escaped_empty_interpolation: 'clown%%{""}{shoe}s'
10
+ quoted_only_empty_interpolation: '%{""}'
11
+ quoted_empty_namespace: '%{"::"}'
12
+ quoted_whitespace1: '%{ "::" }'
13
+ quoted_whitespace2: '%{ "" }'
@@ -0,0 +1,12 @@
1
+ ---
2
+
3
+ 'a key with whitespace': 'value for a ws key'
4
+ ws_key: '%{alias("a key with whitespace")}'
5
+
6
+ '#@!&%|§': 'not happy'
7
+ angry: '%{alias("#@!&%|§")}'
8
+
9
+ '!$\%!':
10
+ '#@!&%|§': 'not happy at all'
11
+
12
+ very_angry: '%{alias("!$\%!.#@!&%|§")}'
@@ -2,98 +2,233 @@ require 'spec_helper'
2
2
  require 'hiera/util'
3
3
 
4
4
  describe "Hiera" do
5
- context "when doing interpolation" do
6
- let(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'interpolate') }
5
+ let!(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'interpolate') }
6
+ let!(:fixture_data) { File.join(fixtures, 'data') }
7
+ let(:hiera) { Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml')) }
8
+
9
+ before(:each) do
10
+ Hiera::Util.expects(:var_dir).at_most(3).returns(fixture_data)
11
+ end
7
12
 
13
+ context "when doing interpolation" do
8
14
  it 'should prevent endless recursion' do
9
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
10
15
  hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
11
16
  expect do
12
17
  hiera.lookup('foo', nil, {})
13
18
  end.to raise_error Hiera::InterpolationLoop, 'Lookup recursion detected in [hiera("bar"), hiera("foo")]'
14
19
  end
20
+
21
+ it 'produces a nested hash with arrays from nested aliases with hashes and arrays' do
22
+ Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
23
+ hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
24
+ expect(hiera.lookup('root', nil, {}, nil, :hash)).to eq({'a'=>{'aa'=>{'b'=>{'bb'=>['text']}}}})
25
+ end
26
+
27
+ it 'allows keys with white space' do
28
+ expect(hiera.lookup('ws_key', nil, {})).to eq('value for a ws key')
29
+ end
30
+
31
+ it 'allows keys with non alphanumeric characters' do
32
+ expect(hiera.lookup('angry', nil, {})).to eq('not happy')
33
+ end
15
34
  end
16
35
 
17
36
  context "when not finding value for interpolated key" do
18
- let(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'interpolate') }
19
-
20
37
  it 'should resolve the interpolation to an empty string' do
21
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
22
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
23
38
  expect(hiera.lookup('niltest', nil, {})).to eq('Missing key ##. Key with nil ##')
24
39
  end
25
40
  end
26
41
 
27
42
  context "when there are empty interpolations %{} in data" do
28
- let(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'interpolate') }
29
-
30
43
  it 'should should produce an empty string for the interpolation' do
31
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
32
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
33
44
  expect(hiera.lookup('empty_interpolation', nil, {})).to eq('clownshoe')
34
45
  end
35
46
 
36
47
  it 'the empty interpolation can be escaped' do
37
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
38
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
39
48
  expect(hiera.lookup('escaped_empty_interpolation', nil, {})).to eq('clown%{shoe}s')
40
49
  end
41
50
 
42
51
  it 'the value can consist of only an empty escape' do
43
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
44
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
45
52
  expect(hiera.lookup('only_empty_interpolation', nil, {})).to eq('')
46
53
  end
47
54
 
48
55
  it 'the value can consist of an empty namespace %{::}' do
49
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
50
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
51
56
  expect(hiera.lookup('empty_namespace', nil, {})).to eq('')
52
57
  end
53
58
 
54
59
  it 'the value can consist of whitespace %{ :: }' do
55
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
56
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
57
60
  expect(hiera.lookup('whitespace1', nil, {})).to eq('')
58
61
  end
59
62
 
60
63
  it 'the value can consist of whitespace %{ }' do
61
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
62
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
63
64
  expect(hiera.lookup('whitespace2', nil, {})).to eq('')
64
65
  end
65
66
  end
66
67
 
67
- context "when doing interpolation with override" do
68
- let(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'override') }
68
+ context 'when there are quoted empty interpolations %{} in data' do
69
+ it 'should should produce an empty string for the interpolation' do
70
+ expect(hiera.lookup('quoted_empty_interpolation', nil, {})).to eq('clownshoe')
71
+ end
72
+
73
+ it 'the empty interpolation can be escaped' do
74
+ expect(hiera.lookup('quoted_escaped_empty_interpolation', nil, {})).to eq('clown%{shoe}s')
75
+ end
76
+
77
+ it 'the value can consist of only an empty escape' do
78
+ expect(hiera.lookup('quoted_only_empty_interpolation', nil, {})).to eq('')
79
+ end
80
+
81
+ it 'the value can consist of an empty namespace %{::}' do
82
+ expect(hiera.lookup('quoted_empty_namespace', nil, {})).to eq('')
83
+ end
84
+
85
+ it 'the value can consist of whitespace %{ :: }' do
86
+ expect(hiera.lookup('quoted_whitespace1', nil, {})).to eq('')
87
+ end
88
+
89
+ it 'the value can consist of whitespace %{ }' do
90
+ expect(hiera.lookup('quoted_whitespace2', nil, {})).to eq('')
91
+ end
92
+ end
93
+
94
+ context 'when using dotted keys' do
95
+ it 'should find an entry using a quoted interpolation' do
96
+ expect(hiera.lookup('"a.c.scope"', nil, {'a.b' => '(scope) a dot b'})).to eq('a dot c: (scope) a dot b')
97
+ end
98
+
99
+ it 'should find an entry using a quoted interpolation with method hiera' do
100
+ expect(hiera.lookup('"a.c.hiera"', nil, {'a.b' => '(scope) a dot b'})).to eq('a dot c: (hiera) a dot b')
101
+ end
102
+
103
+ it 'should find an entry using a quoted interpolation with method alias' do
104
+ expect(hiera.lookup('"a.c.alias"', nil, {'a.b' => '(scope) a dot b'})).to eq('(hiera) a dot b')
105
+ end
106
+
107
+ it 'should use a dotted key to navigate into a structure when it is not quoted' do
108
+ expect(hiera.lookup('"a.e.scope"', nil, {'a' => { 'd' => '(scope) a dot d is a hash entry'}})).to eq('a dot e: (scope) a dot d is a hash entry')
109
+ end
110
+
111
+ it 'should use a dotted key to navigate into a structure when when it is not quoted with method hiera' do
112
+ expect(hiera.lookup('"a.e.hiera"', nil, {'a' => { 'd' => '(scope) a dot d is a hash entry'}})).to eq('a dot e: (hiera) a dot d is a hash entry')
113
+ end
114
+
115
+ it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is last' do
116
+ expect(hiera.lookup('"a.ex.scope"', nil, {'a' => { 'd.x' => '(scope) a dot d.x is a hash entry'}})).to eq('a dot ex: (scope) a dot d.x is a hash entry')
117
+ end
118
+
119
+ it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is last and method is hiera' do
120
+ expect(hiera.lookup('"a.ex.hiera"', nil, {'a' => { 'd.x' => '(scope) a dot d.x is a hash entry'}})).to eq('a dot ex: (hiera) a dot d.x is a hash entry')
121
+ end
122
+
123
+ it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is first' do
124
+ expect(hiera.lookup('"a.xe.scope"', nil, {'a.x' => { 'd' => '(scope) a.x dot d is a hash entry'}})).to eq('a dot xe: (scope) a.x dot d is a hash entry')
125
+ end
126
+
127
+ it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is first and method is hiera' do
128
+ expect(hiera.lookup('"a.xe.hiera"', nil, {'a.x' => { 'd' => '(scope) a.x dot d is a hash entry'}})).to eq('a dot xe: (hiera) a.x dot d is a hash entry')
129
+ end
130
+
131
+ it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle' do
132
+ expect(hiera.lookup('"a.xm.scope"', nil, {'a' => { 'd.z' => { 'g' => '(scope) a dot d.z dot g is a hash entry'}}})).to eq('a dot xm: (scope) a dot d.z dot g is a hash entry')
133
+ end
134
+
135
+ it 'should use a mix of quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle and method is hiera' do
136
+ expect(hiera.lookup('"a.xm.hiera"', nil, {'a' => { 'd.z' => { 'g' => '(scope) a dot d.z dot g is a hash entry'}}})).to eq('a dot xm: (hiera) a dot d.z dot g is a hash entry')
137
+ end
138
+
139
+ it 'should use a mix of several quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle' do
140
+ expect(hiera.lookup('"a.xx.scope"', nil, {'a.x' => { 'd.z' => { 'g' => '(scope) a.x dot d.z dot g is a hash entry'}}})).to eq('a dot xx: (scope) a.x dot d.z dot g is a hash entry')
141
+ end
142
+
143
+ it 'should use a mix of several quoted and dotted keys to navigate into a structure containing dotted keys and quoted key is in the middle and method is hiera' do
144
+ expect(hiera.lookup('"a.xx.hiera"', nil, {'a.x' => { 'd.z' => { 'g' => '(scope) a.x dot d.z dot g is a hash entry'}}})).to eq('a dot xx: (hiera) a.x dot d.z dot g is a hash entry')
145
+ end
146
+
147
+ it 'should find an entry using using a quoted interpolation on dotted key containing numbers' do
148
+ expect(hiera.lookup('"x.2.scope"', nil, {'x.1' => '(scope) x dot 1'})).to eq('x dot 2: (scope) x dot 1')
149
+ end
150
+
151
+ it 'should find an entry using using a quoted interpolation on dotted key containing numbers using method hiera' do
152
+ expect(hiera.lookup('"x.2.hiera"', nil, {'x.1' => '(scope) x dot 1'})).to eq('x dot 2: (hiera) x dot 1')
153
+ end
154
+
155
+ it 'will allow strange characters in the key' do
156
+ expect(hiera.lookup('very_angry', nil, {})).to eq('not happy at all')
157
+ end
158
+
159
+ it 'should not find a subkey when the dotted key is quoted' do
160
+ expect(hiera.lookup('"a.f.scope"', nil, {'a' => { 'f' => '(scope) a dot f is a hash entry'}})).to eq('a dot f: ')
161
+ end
162
+
163
+ it 'should not find a subkey when the dotted key is quoted with method hiera' do
164
+ expect(hiera.lookup('"a.f.hiera"', nil, {'a' => { 'f' => '(scope) a dot f is a hash entry'}})).to eq('a dot f: ')
165
+ end
166
+ end
167
+
168
+ context 'when bad interpolation expressions are encountered' do
169
+ it 'should produce an error when different quotes are used on either side' do
170
+ expect { hiera.lookup('quote_mismatch', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{'the\.key"\}/)
171
+ end
172
+
173
+ it 'should produce an if there is only one quote' do
174
+ expect { hiera.lookup('one_quote', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.'key\}/)
175
+ end
176
+
177
+ it 'should produce an error for an empty segment' do
178
+ expect { hiera.lookup('empty_segment', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.\.key\}/)
179
+ end
180
+
181
+ it 'should produce an error for an empty quoted segment' do
182
+ expect { hiera.lookup('empty_quoted_segment', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.''\.key\}/)
183
+ end
184
+
185
+ it 'should produce an error for an partly quoted segment' do
186
+ expect { hiera.lookup('partly_quoted_segment', nil, {}) }.to raise_error(/Syntax error in interpolation expression: \%\{the\.'pa'key\}/)
187
+ end
188
+
189
+ it 'should produce an error when different quotes are used on either side in a method argument' do
190
+ expect { hiera.lookup('quote_mismatch_arg', nil, {}) }.to raise_error(/Argument to interpolation method 'hiera' must be quoted, got ''the.key"'/)
191
+ end
192
+
193
+ it 'should produce an error unless a known interpolation method is used' do
194
+ expect { hiera.lookup('non_existing_method', nil, {}) }.to raise_error(/Invalid interpolation method 'flubber'/)
195
+ end
196
+
197
+ it 'should produce an error if there is only one quote' do
198
+ expect { hiera.lookup('one_quote', nil, {}) }.to raise_error(/Syntax error/)
199
+ end
200
+
201
+ it 'should produce an error when different quotes are used on either side in a top-level key' do
202
+ expect { hiera.lookup("'the.key\"", nil, {}) }.to raise_error(/Syntax error in key: 'the.key"/)
203
+ end
204
+ end
205
+
206
+ context 'when doing interpolation with override' do
207
+ let!(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'override') }
69
208
 
70
209
  it 'should resolve interpolation using the override' do
71
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
72
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera.yaml'))
73
210
  expect(hiera.lookup('foo', nil, {}, 'alternate')).to eq('alternate')
74
211
  end
75
212
  end
76
213
 
77
214
  context 'when doing interpolation in config file' do
78
- let(:fixtures) { File.join(HieraSpec::FIXTURE_DIR, 'interpolate') }
215
+ let(:hiera) { Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera.yaml')) }
79
216
 
80
217
  it 'should allow and resolve a correctly configured interpolation using "hiera" method' do
81
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
82
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera.yaml'))
83
218
  expect(hiera.lookup('foo', nil, {})).to eq('Foo')
84
219
  end
85
220
 
86
- it 'should detect interpolation recursion when using "hiera" method' do
87
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
88
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera_bad.yaml'))
89
- expect{ hiera.lookup('foo', nil, {}) }.to raise_error(Hiera::InterpolationLoop, "Lookup recursion detected in [hiera('role')]")
90
- end
91
-
92
221
  it 'should issue warning when interpolation methods are used' do
93
222
  Hiera.expects(:warn).with('Use of interpolation methods in hiera configuration file is deprecated').at_least_once
94
- Hiera::Util.expects(:var_dir).at_least_once.returns(File.join(fixtures, 'data'))
95
- hiera = Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera.yaml'))
96
223
  expect(hiera.lookup('foo', nil, {})).to eq('Foo')
97
224
  end
98
225
  end
226
+
227
+ context 'when doing interpolation in bad config file' do
228
+ let(:hiera) { Hiera.new(:config => File.join(fixtures, 'config', 'hiera_iplm_hiera_bad.yaml')) }
229
+
230
+ it 'should detect interpolation recursion when using "hiera" method' do
231
+ expect{ hiera.lookup('foo', nil, {}) }.to raise_error(Hiera::InterpolationLoop, "Lookup recursion detected in [hiera('role')]")
232
+ end
233
+ end
99
234
  end
metadata CHANGED
@@ -1,27 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiera
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Puppet Labs
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2016-03-11 00:00:00.000000000 Z
12
+ date: 2016-03-23 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: json_pure
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: '0'
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0'
27
30
  description: A pluggable data store for hierarcical data
@@ -61,11 +64,15 @@ files:
61
64
  - spec/unit/fixtures/interpolate/config/hiera.yaml
62
65
  - spec/unit/fixtures/interpolate/config/hiera_iplm_hiera.yaml
63
66
  - spec/unit/fixtures/interpolate/config/hiera_iplm_hiera_bad.yaml
67
+ - spec/unit/fixtures/interpolate/data/bad_interpolation.yaml
68
+ - spec/unit/fixtures/interpolate/data/complex.yaml
69
+ - spec/unit/fixtures/interpolate/data/dotted_keys.yaml
64
70
  - spec/unit/fixtures/interpolate/data/empty_interpolation.yaml
65
71
  - spec/unit/fixtures/interpolate/data/frontend.json
66
72
  - spec/unit/fixtures/interpolate/data/niltest.yaml
67
73
  - spec/unit/fixtures/interpolate/data/recursive.yaml
68
74
  - spec/unit/fixtures/interpolate/data/role.json
75
+ - spec/unit/fixtures/interpolate/data/weird_keys.yaml
69
76
  - spec/unit/fixtures/override/config/hiera.yaml
70
77
  - spec/unit/fixtures/override/data/alternate.yaml
71
78
  - spec/unit/fixtures/override/data/common.yaml
@@ -76,26 +83,27 @@ files:
76
83
  - spec/unit/version_spec.rb
77
84
  homepage: https://github.com/puppetlabs/hiera
78
85
  licenses: []
79
- metadata: {}
80
86
  post_install_message:
81
87
  rdoc_options: []
82
88
  require_paths:
83
89
  - lib
84
90
  required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
85
92
  requirements:
86
- - - '>='
93
+ - - ! '>='
87
94
  - !ruby/object:Gem::Version
88
95
  version: '0'
89
96
  required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
90
98
  requirements:
91
- - - '>='
99
+ - - ! '>='
92
100
  - !ruby/object:Gem::Version
93
101
  version: '0'
94
102
  requirements: []
95
103
  rubyforge_project:
96
- rubygems_version: 2.0.14
104
+ rubygems_version: 1.8.23
97
105
  signing_key:
98
- specification_version: 4
106
+ specification_version: 3
99
107
  summary: Light weight hierarchical data store
100
108
  test_files:
101
109
  - spec/spec_helper.rb
@@ -109,11 +117,15 @@ test_files:
109
117
  - spec/unit/fixtures/interpolate/config/hiera.yaml
110
118
  - spec/unit/fixtures/interpolate/config/hiera_iplm_hiera.yaml
111
119
  - spec/unit/fixtures/interpolate/config/hiera_iplm_hiera_bad.yaml
120
+ - spec/unit/fixtures/interpolate/data/bad_interpolation.yaml
121
+ - spec/unit/fixtures/interpolate/data/complex.yaml
122
+ - spec/unit/fixtures/interpolate/data/dotted_keys.yaml
112
123
  - spec/unit/fixtures/interpolate/data/empty_interpolation.yaml
113
124
  - spec/unit/fixtures/interpolate/data/frontend.json
114
125
  - spec/unit/fixtures/interpolate/data/niltest.yaml
115
126
  - spec/unit/fixtures/interpolate/data/recursive.yaml
116
127
  - spec/unit/fixtures/interpolate/data/role.json
128
+ - spec/unit/fixtures/interpolate/data/weird_keys.yaml
117
129
  - spec/unit/fixtures/override/config/hiera.yaml
118
130
  - spec/unit/fixtures/override/data/alternate.yaml
119
131
  - spec/unit/fixtures/override/data/common.yaml
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: b028f3d8c5d7d62503173b3a2460efe345866293
4
- data.tar.gz: 6fd01d4e6590e18449bc6a70a43c46288e55e7ce
5
- SHA512:
6
- metadata.gz: 35c2f04b974c34ea63366d97b9f74a90bbaba1ee9c6ff6a4189e8965d9bcc72edfe94b944b03d56fc14f059566e5a6570acf086c0396ec91689af49cc8e9f6da
7
- data.tar.gz: 0a5dfdc722e1c800339811f74a6e76354b380681f6d727f33285e383ff2a049bac1acaaface1705afea3e8c4e3f8cf205f7d935157f95419962593646877d39c