ruby-puppetdb 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 06d30fe03dc440c05f38f596006a7fe936ae8b21
4
- data.tar.gz: 81ce06c2b619e6784d5b9c1aa0f87909bd30f602
3
+ metadata.gz: 9ed96d47f9a2eae2e54dccea8d9961cd185d0631
4
+ data.tar.gz: dc1ba65a4259f59d583dda883957b3bc5127df4a
5
5
  SHA512:
6
- metadata.gz: 2e7675f6543e5a22395b54461f8529ee61b84ffd09cc9146ce83bc2f1d1ef3930a301384b1d5431bf8564244a66adef5e988e80b4674b62d69f8c06eccca4985
7
- data.tar.gz: 1cbe4bf2114a646c0a09eee902c0247cf79e76782f24ea9f785011968bb93153fb203700370823e06089bfeb96aedc77be400752f07aaeadc1a168561ae120f0
6
+ metadata.gz: 15f7ae9d9c39d487f772ea24d46229dfced946265d9fd52e5b7603a319eeb420c335d57ce618ab45344b47cfa10728645ddacf9a79d3b02ab6f6cb39ec961e94
7
+ data.tar.gz: 79e0500fd751c24e7ca74d5df6342d4eb76b8b2f1721672472d2d99832d6d4a87a654c49cd3c3a375aa327919f535bd3099d926b925171ccf5559d8159b95f85
@@ -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
- factquery = parser.facts_query(query, options[:facts].split(','))
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, facts if query.is_a? String
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 a optional
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 (this argument is optional)
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 fact
32
- query = parser.facts_query(query, [fact])
33
- puppetdb.query(:facts, query, :extract => :value).collect { |f| f['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'] }
@@ -68,6 +68,7 @@ EOT
68
68
  results.reduce({}) do |ret, resource|
69
69
  ret[resource['certname']] = [] unless ret.key? resource['certname']
70
70
  ret[resource['certname']] << resource
71
+ ret
71
72
  end
72
73
  else
73
74
  results
@@ -1,4 +1,4 @@
1
1
  module PuppetDB
2
2
  # Current version of this module
3
- VERSION = [2, 1, 1]
3
+ VERSION ||= [2, 2, 0]
4
4
  end
@@ -22,7 +22,13 @@ module PuppetDB::ParserHelper
22
22
  if facts.nil?
23
23
  nodequery
24
24
  else
25
- factquery = ['or', *facts.collect { |f| ['=', 'name', 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 facts [Array] fact values
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
- facts.reduce({}) do |ret, fact|
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']][fact['name']] = fact['value']
67
+ ret[fact['certname']][name] = value
42
68
  else
43
- ret[fact['certname']] = { fact['name'] => fact['value'] }
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.1.1
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: 2015-12-14 00:00:00.000000000 Z
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: '0'
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: '0'
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.2.5
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: