hiera 3.1.0 → 3.1.1

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.
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