ruby-puppetdb 2.1.1 → 2.2.0
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.
- checksums.yaml +4 -4
- data/lib/puppet/face/query.rb +4 -2
- data/lib/puppet/parser/functions/query_facts.rb +4 -2
- data/lib/puppet/parser/functions/query_nodes.rb +17 -5
- data/lib/puppet/parser/functions/query_resources.rb +1 -0
- data/lib/puppetdb.rb +1 -1
- data/lib/puppetdb/parser_helper.rb +53 -6
- data/spec/functions/query_facts_spec.rb +39 -0
- data/spec/functions/query_nodes_spec.rb +51 -0
- data/spec/unit/puppetdb/parser_spec.rb +13 -1
- metadata +15 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9ed96d47f9a2eae2e54dccea8d9961cd185d0631
|
4
|
+
data.tar.gz: dc1ba65a4259f59d583dda883957b3bc5127df4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 15f7ae9d9c39d487f772ea24d46229dfced946265d9fd52e5b7603a319eeb420c335d57ce618ab45344b47cfa10728645ddacf9a79d3b02ab6f6cb39ec961e94
|
7
|
+
data.tar.gz: 79e0500fd751c24e7ca74d5df6342d4eb76b8b2f1721672472d2d99832d6d4a87a654c49cd3c3a375aa327919f535bd3099d926b925171ccf5559d8159b95f85
|
data/lib/puppet/face/query.rb
CHANGED
@@ -48,11 +48,13 @@ Puppet::Face.define(:query, '1.0.0') do
|
|
48
48
|
puppetdb = PuppetDB::Connection.new options[:host], options[:port], !options[:no_ssl]
|
49
49
|
parser = PuppetDB::Parser.new
|
50
50
|
if options[:facts] != ''
|
51
|
-
|
51
|
+
facts = options[:facts].split(',')
|
52
|
+
factquery = parser.facts_query(query, facts)
|
52
53
|
else
|
54
|
+
facts = [:all]
|
53
55
|
factquery = parser.parse(query, :facts)
|
54
56
|
end
|
55
|
-
parser.facts_hash(puppetdb.query(:facts, factquery, :extract => [:certname, :name, :value]))
|
57
|
+
parser.facts_hash(puppetdb.query(:facts, factquery, :extract => [:certname, :name, :value]), facts)
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
@@ -15,6 +15,8 @@ Puppet::Parser::Functions.newfunction(:query_facts, :type => :rvalue, :arity =>
|
|
15
15
|
EOT
|
16
16
|
) do |args|
|
17
17
|
query, facts = args
|
18
|
+
facts = facts.map { |fact| fact.match(/\./) ? fact.split('.') : fact }
|
19
|
+
facts_for_query = facts.map { |fact| fact.is_a?(Array) ? fact.first : fact }
|
18
20
|
|
19
21
|
require 'puppet/util/puppetdb'
|
20
22
|
|
@@ -31,6 +33,6 @@ EOT
|
|
31
33
|
uri = URI(Puppet::Util::Puppetdb.config.server_urls.first)
|
32
34
|
puppetdb = PuppetDB::Connection.new(uri.host, uri.port, uri.scheme == 'https')
|
33
35
|
parser = PuppetDB::Parser.new
|
34
|
-
query = parser.facts_query query,
|
35
|
-
parser.facts_hash(puppetdb.query(:facts, query, :extract => [:certname, :name, :value]))
|
36
|
+
query = parser.facts_query query, facts_for_query if query.is_a? String
|
37
|
+
parser.facts_hash(puppetdb.query(:facts, query, :extract => [:certname, :name, :value]), facts)
|
36
38
|
end
|
@@ -1,17 +1,23 @@
|
|
1
1
|
Puppet::Parser::Functions.newfunction(:query_nodes, :type => :rvalue, :arity => -2, :doc => <<-EOT
|
2
2
|
|
3
|
-
accepts two arguments, a query used to discover nodes, and
|
3
|
+
accepts two arguments, a query used to discover nodes, and an optional
|
4
4
|
fact that should be returned.
|
5
5
|
|
6
6
|
The query specified should conform to the following format:
|
7
7
|
(Type[title] and fact_name<operator>fact_value) or ...
|
8
8
|
Package["mysql-server"] and cluster_id=my_first_cluster
|
9
9
|
|
10
|
-
The second argument should be single fact
|
10
|
+
The second argument should be single fact or series of keys joined on periods
|
11
|
+
(this argument is optional)
|
11
12
|
|
12
13
|
EOT
|
13
14
|
) do |args|
|
14
15
|
query, fact = args
|
16
|
+
fact_for_query = if fact && fact.match(/\./)
|
17
|
+
fact.split('.').first
|
18
|
+
else
|
19
|
+
fact
|
20
|
+
end
|
15
21
|
|
16
22
|
require 'puppet/util/puppetdb'
|
17
23
|
|
@@ -28,9 +34,15 @@ EOT
|
|
28
34
|
uri = URI(Puppet::Util::Puppetdb.config.server_urls.first)
|
29
35
|
puppetdb = PuppetDB::Connection.new(uri.host, uri.port, uri.scheme == 'https')
|
30
36
|
parser = PuppetDB::Parser.new
|
31
|
-
if
|
32
|
-
query = parser.facts_query(query, [
|
33
|
-
puppetdb.query(:facts, query, :extract => :value)
|
37
|
+
if fact_for_query
|
38
|
+
query = parser.facts_query(query, [fact_for_query])
|
39
|
+
response = puppetdb.query(:facts, query, :extract => :value)
|
40
|
+
|
41
|
+
if fact.split('.').size > 1
|
42
|
+
parser.extract_nested_fact(response, fact.split('.')[1..-1])
|
43
|
+
else
|
44
|
+
response.collect { |f| f['value'] }
|
45
|
+
end
|
34
46
|
else
|
35
47
|
query = parser.parse(query, :nodes) if query.is_a? String
|
36
48
|
puppetdb.query(:nodes, query, :extract => :certname).collect { |n| n['certname'] }
|
data/lib/puppetdb.rb
CHANGED
@@ -22,7 +22,13 @@ module PuppetDB::ParserHelper
|
|
22
22
|
if facts.nil?
|
23
23
|
nodequery
|
24
24
|
else
|
25
|
-
factquery = ['or', *facts.collect { |f|
|
25
|
+
factquery = ['or', *facts.collect { |f|
|
26
|
+
if (f =~ /^\/(.+)\/$/)
|
27
|
+
['~', 'name', f.scan(/^\/(.+)\/$/).last.first]
|
28
|
+
else
|
29
|
+
['=', 'name', f]
|
30
|
+
end
|
31
|
+
}]
|
26
32
|
if nodequery
|
27
33
|
['and', nodequery, factquery]
|
28
34
|
else
|
@@ -33,19 +39,60 @@ module PuppetDB::ParserHelper
|
|
33
39
|
|
34
40
|
# Turn an array of facts into a hash of nodes containing facts
|
35
41
|
#
|
36
|
-
# @param
|
42
|
+
# @param fact_hash [Array] fact values
|
43
|
+
# @param facts [Array] fact names
|
37
44
|
# @return [Hash] nodes as keys containing a hash of facts as value
|
38
|
-
def facts_hash(facts)
|
39
|
-
|
45
|
+
def facts_hash(fact_hash, facts)
|
46
|
+
fact_hash.reduce({}) do |ret, fact|
|
47
|
+
# Array#include? only matches on values of the same type, so if we find
|
48
|
+
# a matching string, it's not a nested query.
|
49
|
+
name, value = if facts.include?(fact['name']) || facts == [:all]
|
50
|
+
[fact['name'], fact['value']]
|
51
|
+
else
|
52
|
+
# Find the set of keys where the first value is the fact name
|
53
|
+
nested_keys = facts.select do |x|
|
54
|
+
x.is_a?(Array) && x.first == fact['name']
|
55
|
+
end.flatten
|
56
|
+
|
57
|
+
# Join all the key names together with an underscore to give
|
58
|
+
# us a unique name, and then send all the keys but the fact
|
59
|
+
# name (which was already queried out) to extract_nested_fact
|
60
|
+
[
|
61
|
+
nested_keys.join("_"),
|
62
|
+
extract_nested_fact([fact], nested_keys[1..-1]).first
|
63
|
+
]
|
64
|
+
end
|
65
|
+
|
40
66
|
if ret.include? fact['certname']
|
41
|
-
ret[fact['certname']][
|
67
|
+
ret[fact['certname']][name] = value
|
42
68
|
else
|
43
|
-
ret[fact['certname']] = {
|
69
|
+
ret[fact['certname']] = { name => value }
|
44
70
|
end
|
45
71
|
ret
|
46
72
|
end
|
47
73
|
end
|
48
74
|
|
75
|
+
# Take an array of hashes of fact hashes and get a nested value from each
|
76
|
+
# of them.
|
77
|
+
#
|
78
|
+
# @param fact_hashes [Array] an array of hashes of fact hashes
|
79
|
+
# @param keys [Array] an array of keys to dig into the hash
|
80
|
+
# @returt [Array] an array of extracted values
|
81
|
+
def extract_nested_fact(fact_hashes, keys)
|
82
|
+
fact_hashes.map do |fact_hash|
|
83
|
+
hash = fact_hash['value']
|
84
|
+
|
85
|
+
# Traverse the hash, setting `hash` equal to the next level deep each step
|
86
|
+
keys[0..-2].each do |key|
|
87
|
+
hash = hash.fetch(key, {})
|
88
|
+
end
|
89
|
+
|
90
|
+
# Lookup the final key. This will convert to nil if we've been defaulting
|
91
|
+
# to empty hash beforehand.
|
92
|
+
hash[keys.last]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
49
96
|
# Turn a query into one for only certain fields
|
50
97
|
def self.extract(*field, query)
|
51
98
|
['extract', field.collect(&:to_s), query]
|
@@ -12,4 +12,43 @@ describe 'query_facts' do
|
|
12
12
|
]
|
13
13
|
should run.with_params('', ['ipaddress']).and_return('apache4.puppetexplorer.io' => { 'ipaddress' => '172.31.6.80' })
|
14
14
|
end
|
15
|
+
|
16
|
+
it do
|
17
|
+
PuppetDB::Connection.any_instance.expects(:query)
|
18
|
+
.with(:facts, ['or', ['=', 'name', 'ipaddress'], ['=', 'name', 'network_eth0']], {:extract => [:certname, :name, :value]})
|
19
|
+
.returns [
|
20
|
+
{ 'certname' => 'apache4.puppetexplorer.io', 'environment' => 'production', 'name' => 'ipaddress', 'value' => '172.31.6.80' },
|
21
|
+
{ 'certname' => 'apache4.puppetexplorer.io', 'environment' => 'production', 'name' => 'network_eth0', 'value' => '172.31.0.0' }
|
22
|
+
]
|
23
|
+
should run.with_params('', ['ipaddress', 'network_eth0']).and_return('apache4.puppetexplorer.io' => { 'ipaddress' => '172.31.6.80', 'network_eth0' => '172.31.0.0' })
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'with a nested fact parameter' do
|
27
|
+
it do
|
28
|
+
PuppetDB::Connection.any_instance.expects(:query)
|
29
|
+
.with(:facts, ['or', ['=', 'name', 'ipaddress'], ['=', 'name', 'networking']], {:extract => [:certname, :name, :value]})
|
30
|
+
.returns [
|
31
|
+
{ 'certname' => 'apache4.puppetexplorer.io', 'environment' => 'production', 'name' => 'ipaddress', 'value' => '172.31.6.80' },
|
32
|
+
{
|
33
|
+
'certname' => 'apache4.puppetexplorer.io',
|
34
|
+
'environment' => 'production',
|
35
|
+
'name' => 'networking',
|
36
|
+
'value' => {
|
37
|
+
'interfaces' => {
|
38
|
+
'eth0' => {
|
39
|
+
'ip' => '172.31.6.80',
|
40
|
+
'network' => '172.31.0.0',
|
41
|
+
},
|
42
|
+
'eth1' => {
|
43
|
+
'ip' => '172.32.6.80',
|
44
|
+
'network' => '172.32.0.0',
|
45
|
+
},
|
46
|
+
'bond0' => {},
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
]
|
51
|
+
should run.with_params('', ['ipaddress', 'networking.interfaces.eth0.ip']).and_return('apache4.puppetexplorer.io' => { 'ipaddress' => '172.31.6.80', 'networking_interfaces_eth0_ip' => '172.31.6.80' })
|
52
|
+
end
|
53
|
+
end
|
15
54
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
#! /usr/bin/env ruby -S rspec
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
+
require 'puppetdb/connection'
|
4
5
|
|
5
6
|
describe 'query_nodes' do
|
6
7
|
context 'without fact parameter' do
|
@@ -20,4 +21,54 @@ describe 'query_nodes' do
|
|
20
21
|
should run.with_params('hostname="apache4"', 'ipaddress').and_return(['172.31.6.80'])
|
21
22
|
end
|
22
23
|
end
|
24
|
+
|
25
|
+
context 'with a nested fact parameter' do
|
26
|
+
it do
|
27
|
+
PuppetDB::Connection.any_instance.expects(:query)
|
28
|
+
.with(:facts, ['and', ['in', 'certname', ['extract', 'certname', ['select_fact_contents', ['and', ['=', 'path', ['hostname']], ['=', 'value', 'apache4']]]]], ['or', ['=', 'name', 'networking']]], :extract => :value)
|
29
|
+
.returns [
|
30
|
+
{
|
31
|
+
'value' => {
|
32
|
+
'interfaces' => {
|
33
|
+
'eth0' => {
|
34
|
+
'ip' => '172.31.6.80',
|
35
|
+
'network' => '172.31.0.0',
|
36
|
+
},
|
37
|
+
'eth1' => {
|
38
|
+
'ip' => '172.32.6.80',
|
39
|
+
'network' => '172.32.0.0',
|
40
|
+
},
|
41
|
+
'bond0' => {},
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
]
|
46
|
+
should run.with_params('hostname="apache4"', 'networking.interfaces.eth1.ip').and_return(['172.32.6.80'])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'with a missing nested fact parameter' do
|
51
|
+
it do
|
52
|
+
PuppetDB::Connection.any_instance.expects(:query)
|
53
|
+
.with(:facts, ['and', ['in', 'certname', ['extract', 'certname', ['select_fact_contents', ['and', ['=', 'path', ['hostname']], ['=', 'value', 'apache4']]]]], ['or', ['=', 'name', 'networking']]], :extract => :value)
|
54
|
+
.returns [
|
55
|
+
{
|
56
|
+
'value' => {
|
57
|
+
'interfaces' => {
|
58
|
+
'eth0' => {
|
59
|
+
'ip' => '172.31.6.80',
|
60
|
+
'network' => '172.31.0.0',
|
61
|
+
},
|
62
|
+
'eth1' => {
|
63
|
+
'ip' => '172.32.6.80',
|
64
|
+
'network' => '172.32.0.0',
|
65
|
+
},
|
66
|
+
'bond0' => {},
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
]
|
71
|
+
should run.with_params('hostname="apache4"', 'networking.interfaces.missing_interface.ip').and_return([nil])
|
72
|
+
end
|
73
|
+
end
|
23
74
|
end
|
@@ -215,12 +215,24 @@ describe PuppetDB::Parser do
|
|
215
215
|
{ 'certname' => 'ip-172-31-45-32.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'fqdn', 'value' => 'ip-172-31-45-32.eu-west-1.compute.internal' },
|
216
216
|
{ 'certname' => 'ip-172-31-33-234.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'fqdn', 'value' => 'ip-172-31-33-234.eu-west-1.compute.internal' },
|
217
217
|
{ 'certname' => 'ip-172-31-5-147.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'fqdn', 'value' => 'ip-172-31-5-147.eu-west-1.compute.internal' }
|
218
|
-
]).should eq(
|
218
|
+
], ['kernel', 'fqdn']).should eq(
|
219
219
|
'ip-172-31-45-32.eu-west-1.compute.internal' => { 'kernel' => 'Linux', 'fqdn' => 'ip-172-31-45-32.eu-west-1.compute.internal' },
|
220
220
|
'ip-172-31-33-234.eu-west-1.compute.internal' => { 'kernel' => 'Linux', 'fqdn' => 'ip-172-31-33-234.eu-west-1.compute.internal' },
|
221
221
|
'ip-172-31-5-147.eu-west-1.compute.internal' => { 'kernel' => 'Linux', 'fqdn' => 'ip-172-31-5-147.eu-west-1.compute.internal' }
|
222
222
|
)
|
223
223
|
end
|
224
|
+
|
225
|
+
it 'should handle nested facts' do
|
226
|
+
parser.facts_hash([
|
227
|
+
{ 'certname' => 'ip-172-31-45-32.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'kernel', 'value' => 'Linux' },
|
228
|
+
{ 'certname' => 'ip-172-31-33-234.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'kernel', 'value' => 'Linux' },
|
229
|
+
{ 'certname' => 'ip-172-31-45-32.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'networking', 'value' => { 'interfaces' => { 'eth0' => { 'ip' => '172.31.45.32' } } } },
|
230
|
+
{ 'certname' => 'ip-172-31-33-234.eu-west-1.compute.internal', 'environment' => 'production', 'name' => 'networking', 'value' => { 'interfaces' => { 'eth0' => { 'ip' => '172.31.33.234' } } } },
|
231
|
+
], ['kernel', ['networking', 'interfaces', 'eth0', 'ip']]).should eq(
|
232
|
+
'ip-172-31-45-32.eu-west-1.compute.internal' => { 'kernel' => 'Linux', 'networking_interfaces_eth0_ip' => '172.31.45.32' },
|
233
|
+
'ip-172-31-33-234.eu-west-1.compute.internal' => { 'kernel' => 'Linux', 'networking_interfaces_eth0_ip' => '172.31.33.234' },
|
234
|
+
)
|
235
|
+
end
|
224
236
|
end
|
225
237
|
|
226
238
|
context 'extract' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-puppetdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erik Dalen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -44,26 +44,32 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 3.0.0
|
48
|
+
- - "<"
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 5.0.0
|
48
51
|
type: :runtime
|
49
52
|
prerelease: false
|
50
53
|
version_requirements: !ruby/object:Gem::Requirement
|
51
54
|
requirements:
|
52
55
|
- - ">="
|
53
56
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
57
|
+
version: 3.0.0
|
58
|
+
- - "<"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 5.0.0
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
62
|
name: rspec
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
58
64
|
requirements:
|
59
|
-
- -
|
65
|
+
- - "~>"
|
60
66
|
- !ruby/object:Gem::Version
|
61
67
|
version: '2.13'
|
62
68
|
type: :development
|
63
69
|
prerelease: false
|
64
70
|
version_requirements: !ruby/object:Gem::Requirement
|
65
71
|
requirements:
|
66
|
-
- -
|
72
|
+
- - "~>"
|
67
73
|
- !ruby/object:Gem::Version
|
68
74
|
version: '2.13'
|
69
75
|
- !ruby/object:Gem::Dependency
|
@@ -129,9 +135,6 @@ dependencies:
|
|
129
135
|
- - "~>"
|
130
136
|
- !ruby/object:Gem::Version
|
131
137
|
version: '1.4'
|
132
|
-
- - "<"
|
133
|
-
- !ruby/object:Gem::Version
|
134
|
-
version: 1.4.12
|
135
138
|
type: :development
|
136
139
|
prerelease: false
|
137
140
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -139,9 +142,6 @@ dependencies:
|
|
139
142
|
- - "~>"
|
140
143
|
- !ruby/object:Gem::Version
|
141
144
|
version: '1.4'
|
142
|
-
- - "<"
|
143
|
-
- !ruby/object:Gem::Version
|
144
|
-
version: 1.4.12
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
146
|
name: rexical
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
@@ -222,18 +222,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
222
|
version: '0'
|
223
223
|
requirements: []
|
224
224
|
rubyforge_project:
|
225
|
-
rubygems_version: 2.
|
225
|
+
rubygems_version: 2.4.8
|
226
226
|
signing_key:
|
227
227
|
specification_version: 4
|
228
228
|
summary: Query functions for PuppetDB
|
229
229
|
test_files:
|
230
|
+
- spec/spec_helper.rb
|
230
231
|
- spec/functions/query_facts_spec.rb
|
231
232
|
- spec/functions/query_nodes_spec.rb
|
232
233
|
- spec/functions/query_resources_spec.rb
|
233
|
-
- spec/puppet/util/puppetdb.rb
|
234
|
-
- spec/spec_helper.rb
|
235
234
|
- spec/unit/puppetdb/parser_spec.rb
|
235
|
+
- spec/puppet/util/puppetdb.rb
|
236
236
|
- examples/nova_functions.pp
|
237
237
|
- examples/query_node_examples.pp
|
238
238
|
- examples/query_resource_examples.pp
|
239
|
-
has_rdoc:
|