Skip to content

Commit 8be9894

Browse files
authored
Add dig feature. (crayfishx#66)
* added dig features * added docs * typo * updated README with more about digging * [skip ci] fixed example error in README
1 parent b807f68 commit 8be9894

File tree

3 files changed

+204
-22
lines changed

3 files changed

+204
-22
lines changed

README.md

Lines changed: 88 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,39 +64,47 @@ The following mandatory Hiera 5 options must be set for each level of the hierar
6464

6565
The following are optional configuration parameters supported in the `options` hash of the Hiera 5 config
6666

67-
`:output: ` : Specify what handler to use for the output of the request. Currently supported outputs are plain, which will just return the whole document, or YAML and JSON which parse the data and try to look up the key
67+
#### Lookup options
6868

69-
`:http_connect_timeout: ` : Timeout in seconds for the HTTP connect (default 10)
69+
`output: ` : Specify what handler to use for the output of the request. Currently supported outputs are plain, which will just return the whole document, or YAML and JSON which parse the data and try to look up the key
7070
71-
`:http_read_timeout: ` : Timeout in seconds for waiting for a HTTP response (default 10)
71+
`http_connect_timeout: ` : Timeout in seconds for the HTTP connect (default 10)
7272

73-
`:confine_to_keys: ` : Only use this backend if the key matches one of the regexes in the array
73+
`http_read_timeout: ` : Timeout in seconds for waiting for a HTTP response (default 10)
74+
75+
`confine_to_keys: ` : Only use this backend if the key matches one of the regexes in the array
7476

7577
confine_to_keys:
7678
- "application.*"
7779
- "apache::.*"
7880

79-
`:failure: ` : When set to `graceful` will stop hiera-http from throwing an exception in the event of a connection error, timeout or invalid HTTP response and move on. Without this option set hiera-http will throw an exception in such circumstances
81+
`failure: ` : When set to `graceful` will stop hiera-http from throwing an exception in the event of a connection error, timeout or invalid HTTP response and move on. Without this option set hiera-http will throw an exception in such circumstances
82+
83+
`ignore_404: ` : If `failure` is _not_ set to `graceful` then any error code received from the HTTP response will throw an exception. This option makes 404 responses exempt from exceptions. This is useful if you expect to get 404's for data items not in a certain part of the hierarchy and need to fall back to the next level in the hierarchy, but you still want to bomb out on other errors.
84+
85+
`dig:` : (true or false) When the output is parsed YAML or JSON, whether or not to dig into the hash and return the value defined by the `dig_key` option below. This option defaults to `true`
86+
87+
`dig_key` : When the `dig` option is true (default), this option specifies what key is looked up from the results hash returned by the HTTP endpoint. See [Digging values](#digging-values) below for more information
8088

81-
`:ignore_404: ` : If `failure` is _not_ set to `graceful` then any error code received from the HTTP response will throw an exception. This option makes 404 responses exempt from exceptions. This is useful if you expect to get 404's for data items not in a certain part of the hierarchy and need to fall back to the next level in the hierarchy, but you still want to bomb out on other errors.
89+
#### HTTP options
8290

83-
`:use_ssl:`: When set to true, enable SSL (default: false)
91+
`use_ssl:`: When set to true, enable SSL (default: false)
8492

85-
`:ssl_ca_cert`: Specify a CA cert for use with SSL
93+
`ssl_ca_cert`: Specify a CA cert for use with SSL
8694

87-
`:ssl_cert`: Specify location of SSL certificate
95+
`ssl_cert`: Specify location of SSL certificate
8896

89-
`:ssl_key`: Specify location of SSL key
97+
`ssl_key`: Specify location of SSL key
9098

91-
`:ssl_verify`: Specify whether to verify SSL certificates (default: true)
99+
`ssl_verify`: Specify whether to verify SSL certificates (default: true)
92100

93-
`:use_auth:`: When set to true, enable basic auth (default: false)
101+
`use_auth:`: When set to true, enable basic auth (default: false)
94102

95-
`:auth_user:`: The user for basic auth
103+
`auth_user:`: The user for basic auth
96104

97-
`:auth_pass:`: The password for basic auth
105+
`auth_pass:`: The password for basic auth
98106

99-
`:headers:`: Hash of headers to send in the request
107+
`headers:`: Hash of headers to send in the request
100108

101109
### Interpolating special tags
102110

@@ -130,6 +138,71 @@ hierarchy:
130138
failure: graceful
131139
```
132140

141+
### Digging values
142+
143+
Hiera-HTTP supports options to automatically dig into the returned data structure to find a corresponding key. Puppet lookup itself supports similar dig functionality but being able to specify it at the backend means that where an API wraps the required data up in a different way, we can always lookup the desired value before passing it to Puppet to ensure that class parameter lookups work without having to hard code the `lookup` function and dig down into the data for each request. The dig functionality in Puppet is intended to enable you to parse your data more effectivly, the dig functionality in hiera-http is intended to make the API of the endpoint you are talking to compatible.
144+
145+
By default, when a hash is returned by the HTTP endpoint (eg: JSON) then hiera-http will attempt to lookup the key corresponding with the lookup key. For example, when looking up a key `apache::port` we would expect the HTTP endpoint to return something like;
146+
147+
```json
148+
{
149+
"apache::port": 80
150+
}
151+
```
152+
153+
Returned value would be `80`
154+
155+
Depending on what HTTP endpoint we are hitting, the returned output may contain other data with the key that we want to look up nested below it. This behaviour can be overriden by using the options `dig` and `dig_key`.
156+
157+
The `dig_key` option can be used to change the key that is looked up, it also supports a dot-notation for digging values in nested hashes. [Special tags](#interpolating-special-tags) can also be used in the `dig_key` option. Consider the following example output from our HTTP endpoint;
158+
159+
```json
160+
{
161+
"document": {
162+
"settings": {
163+
"apache::port": 80
164+
}
165+
}
166+
}
167+
```
168+
169+
170+
In this scenario we wouldn't be able to use class parameter lookups out-of-the-box, even if we just returned the whole structure, because we always need to drill down into `document.settings` to get the correct value, so In order to map the lookup to find the correct value, we can interpolate the __KEY__ tag into `lookup_key` and tell hiera-http to always dig into the hash with the following option;
171+
172+
```yaml
173+
options:
174+
dig_key: document.settings.__KEY__
175+
```
176+
177+
A more complicated example;
178+
179+
```json
180+
{
181+
"document": {
182+
"settings": {
183+
"apache": {
184+
"port": 80
185+
}
186+
}
187+
}
188+
}
189+
```
190+
191+
Can be looked up with;
192+
193+
```
194+
options:
195+
dig_key: document.settings.__MODULE__.__PARAMETER__
196+
```
197+
198+
In both examples, the returned value to Puppet will be `80`
199+
200+
### Returning the entire data structure
201+
202+
The `dig` option can be used to disable digging altogether and the entire data hash will be returned with no attempt to resolve a key
203+
204+
205+
133206
### Author
134207
135208
* Craig Dunn <[email protected]>

lib/puppet/functions/hiera_http.rb

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,57 @@ def lookup_key(key, options, context)
3030
options['uri'] = parse_tags(key, options['uri'])
3131
result = http_get(context, options)
3232

33-
answer = result.is_a?(Hash) ? result[key] : result
34-
context.not_found if answer.nil?
35-
return answer
33+
answer = return_answer(result, key, options)
34+
if answer == :not_found
35+
context.not_found
36+
return nil
37+
else
38+
return answer
39+
end
40+
41+
end
42+
43+
def return_answer(result, key, options)
44+
45+
# dig defaults to true, dig_key defaults to the value of the
46+
# lookup key.
47+
#
48+
dig = options.has_key?('dig') ? options['dig'] : true
49+
dig_key = options.has_key?('dig_key') ? options['dig_key'] : key
50+
51+
# Interpolate values such as __KEY__ into each element of the
52+
# dig path, eg: dig_key: document.data.__MODULE__
53+
#
54+
dig_path = dig_key.split(/\./).map { |p| parse_tags(key, p) }
55+
56+
57+
if result.is_a?(String)
58+
return result
59+
else
60+
return dig ? hash_dig(result, dig_path) : result
61+
end
62+
63+
end
64+
65+
66+
def hash_dig(data, dig_path)
67+
key = dig_path.shift
68+
if dig_path.empty?
69+
if data.has_key?(key)
70+
return data[key]
71+
else
72+
return :not_found
73+
end
74+
else
75+
return :not_found unless data[key].is_a?(Hash)
76+
return hash_dig(data[key], dig_path)
77+
end
3678
end
3779

38-
def parse_tags(key,uri)
80+
def parse_tags(key,str)
3981
key_parts = key.split(/::/)
4082

41-
parsed_uri = uri.gsub(/__(\w+)__/i) do
83+
parsed_str = str.gsub(/__(\w+)__/i) do
4284
case $1
4385
when 'KEY'
4486
key
@@ -51,7 +93,7 @@ def parse_tags(key,uri)
5193
end
5294
end
5395

54-
return parsed_uri
96+
return parsed_str
5597
end
5698

5799

spec/functions/hiera_http_spec.rb

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
@lookuphttp = instance_double("LookupHttp")
1010
@context = instance_double("Puppet::LookupContext")
1111
allow(LookupHttp).to receive(:new).and_return(@lookuphttp)
12+
allow(@lookuphttp).to receive(:get_parsed).and_return('value')
1213
allow(@context).to receive(:cache_has_key)
1314
allow(@context).to receive(:explain)
1415
allow(@context).to receive(:interpolate)
@@ -98,6 +99,7 @@
9899
context "Output of lookup_http" do
99100
let(:options) { {
100101
'uri' => 'http://localhost/path',
102+
'output' => 'json'
101103
} }
102104
it "should dig if the value is a hash" do
103105
expect(@lookuphttp).to receive(:get_parsed).with('/path').and_return({ 'tango' => 'delta' })
@@ -111,7 +113,7 @@
111113

112114
context "cached values" do
113115
let(:options) { {
114-
'uri' => 'http://localhost/path'
116+
'uri' => 'http://localhost/path',
115117
} }
116118

117119
it "should used cached value when available" do
@@ -121,5 +123,70 @@
121123
expect(function.lookup_key('tango', options, @context)).to eq('bar')
122124
end
123125
end
126+
127+
context "nil versus undefined" do
128+
let(:options) { {
129+
'uri' => 'http://localhost/path',
130+
} }
131+
132+
it "should return nil when a value is set to nil" do
133+
response = { "config" => nil }
134+
expect(@lookuphttp).to receive(:get_parsed).and_return(response)
135+
expect(@context).not_to receive(:not_found)
136+
expect(function.lookup_key('config', options, @context)).to eq(nil)
137+
end
138+
it "should return call not_found when the key doesn't exist" do
139+
response = { "foo" => "bar" }
140+
expect(@lookuphttp).to receive(:get_parsed).and_return(response)
141+
expect(@context).to receive(:not_found)
142+
expect(function.lookup_key('config', options, @context)).to eq(nil)
143+
end
144+
end
145+
146+
147+
148+
149+
context "Digging values" do
150+
let(:options) { {
151+
'uri' => 'http://localhost/path',
152+
} }
153+
154+
let(:response) { {
155+
"document" => {
156+
"settings" => {
157+
"docroot" => "/www"
158+
}
159+
}
160+
} }
161+
before(:each) do
162+
expect(@lookuphttp).to receive(:get_parsed).with('/path').and_return(response)
163+
end
164+
165+
it "should default to digging for the lookup key" do
166+
expect(function.lookup_key('document', options, @context)).to eq({ "settings" => { "docroot" => "/www" } } )
167+
end
168+
169+
it "should be able to dig for other keys" do
170+
extra_opts = { "dig_key" => "document" }
171+
expect(function.lookup_key('bar', options.merge(extra_opts), @context)).to eq({ "settings" => { "docroot" => "/www" } } )
172+
end
173+
174+
it "should dig nested values using the dot notation" do
175+
extra_opts = { "dig_key" => "document.settings" }
176+
expect(function.lookup_key('bar', options.merge(extra_opts), @context)).to eq({ "docroot" => "/www" } )
177+
end
178+
179+
it "should interpolate tags in the key path" do
180+
extra_opts = { "dig_key" => "document.__MODULE__.__PARAMETER__" }
181+
expect(function.lookup_key('settings::docroot', options.merge(extra_opts), @context)).to eq("/www")
182+
end
183+
184+
it "should return the whole data structure if dig is disabled" do
185+
extra_opts = { "dig" => false }
186+
expect(function.lookup_key('bar', options.merge(extra_opts), @context)).to eq(response)
187+
end
188+
189+
190+
end
124191
end
125192
end

0 commit comments

Comments
 (0)