hieracles 0.2.0 → 0.2.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +2 -0
  4. data/README.md +54 -8
  5. data/bin/hc +12 -6
  6. data/bin/ppdb +42 -0
  7. data/hc.1 +50 -8
  8. data/lib/hieracles.rb +2 -2
  9. data/lib/hieracles/config.rb +31 -19
  10. data/lib/hieracles/format.rb +4 -0
  11. data/lib/hieracles/formats/console.rb +49 -16
  12. data/lib/hieracles/formats/csv.rb +12 -8
  13. data/lib/hieracles/formats/json.rb +19 -6
  14. data/lib/hieracles/formats/plain.rb +24 -3
  15. data/lib/hieracles/formats/rawyaml.rb +4 -0
  16. data/lib/hieracles/formats/yaml.rb +4 -0
  17. data/lib/hieracles/node.rb +55 -10
  18. data/lib/hieracles/notification.rb +31 -0
  19. data/lib/hieracles/options/hc.rb +109 -0
  20. data/lib/hieracles/options/ppdb.rb +32 -0
  21. data/lib/hieracles/optparse.rb +4 -43
  22. data/lib/hieracles/puppetdb.rb +12 -0
  23. data/lib/hieracles/puppetdb/apierror.rb +10 -0
  24. data/lib/hieracles/puppetdb/client.rb +63 -0
  25. data/lib/hieracles/puppetdb/filter.rb +15 -0
  26. data/lib/hieracles/puppetdb/fixsslconnectionadapter.rb +25 -0
  27. data/lib/hieracles/puppetdb/query.rb +79 -0
  28. data/lib/hieracles/puppetdb/request.rb +44 -0
  29. data/lib/hieracles/puppetdb/response.rb +14 -0
  30. data/ppdb.1 +158 -0
  31. data/spec/files/config.yml +2 -0
  32. data/spec/files/config_withdb.yml +9 -0
  33. data/spec/files/facts.json +110 -0
  34. data/spec/files/facts.yaml +103 -0
  35. data/spec/files/hiera_columns.yaml +16 -0
  36. data/spec/files/hiera_deep.yaml +17 -0
  37. data/spec/files/hiera_deeper.yaml +17 -0
  38. data/spec/files/ssl/bad-ca.crt +1 -0
  39. data/spec/files/ssl/bad-cert.crt +1 -0
  40. data/spec/files/ssl/bad-key.pem +1 -0
  41. data/spec/files/ssl/ca.crt +16 -0
  42. data/spec/files/ssl/cert.crt +16 -0
  43. data/spec/files/ssl/key-pass.pem +18 -0
  44. data/spec/files/ssl/key.pem +15 -0
  45. data/spec/lib/config_spec.rb +51 -11
  46. data/spec/lib/format_spec.rb +5 -0
  47. data/spec/lib/formats/console_spec.rb +24 -3
  48. data/spec/lib/formats/csv_spec.rb +15 -0
  49. data/spec/lib/formats/json_spec.rb +22 -2
  50. data/spec/lib/formats/plain_spec.rb +23 -3
  51. data/spec/lib/formats/rawyaml_spec.rb +13 -0
  52. data/spec/lib/formats/yaml_spec.rb +138 -48
  53. data/spec/lib/hiera_spec.rb +0 -1
  54. data/spec/lib/hieracles_spec.rb +8 -0
  55. data/spec/lib/interpolate_spec.rb +49 -0
  56. data/spec/lib/node_spec.rb +50 -9
  57. data/spec/lib/notification_spec.rb +29 -0
  58. data/spec/lib/options/hc_spec.rb +82 -0
  59. data/spec/lib/optparse_spec.rb +7 -7
  60. data/spec/lib/puppetdb/apierror_spec.rb +11 -0
  61. data/spec/lib/puppetdb/client_spec.rb +64 -0
  62. data/spec/lib/puppetdb/fixsslconnectionadapter_spec.rb +107 -0
  63. data/spec/lib/puppetdb/query_spec.rb +39 -0
  64. data/spec/lib/puppetdb/request_spec.rb +61 -0
  65. data/spec/lib/puppetdb/response_spec.rb +13 -0
  66. data/spec/spec_helper.rb +4 -4
  67. metadata +133 -30
  68. data/Rakefile +0 -14
  69. data/hieracles.gemspec +0 -90
  70. data/lib/hieracles/help.rb +0 -38
  71. data/spec/lib/help_spec.rb +0 -8
@@ -0,0 +1,32 @@
1
+ require 'hieracles/optparse'
2
+
3
+ module Hieracles
4
+ module Options
5
+ class Ppdb < Hieracles::Optparse
6
+
7
+ def available_options
8
+ {
9
+ version: {
10
+ has_arg: false,
11
+ aliases: ['v', 'version']
12
+ }
13
+ }
14
+ end
15
+
16
+ def self.usage
17
+ return <<-END
18
+
19
+ Usage: ppdb <endpoint> <command> [extra_args]
20
+
21
+ Available commands:
22
+ node info <fqdn>
23
+ node facts <fqdn>
24
+ node resources <fqdn>
25
+
26
+ END
27
+ end
28
+
29
+ end
30
+ end
31
+ end
32
+
@@ -4,48 +4,9 @@ module Hieracles
4
4
 
5
5
  attr_reader :options, :payload
6
6
 
7
- OPTIONS = {
8
- config: {
9
- has_arg: true,
10
- aliases: ['c', 'conf', 'config']
11
- },
12
- format: {
13
- has_arg: true,
14
- aliases: ['f', 'format']
15
- },
16
- params: {
17
- has_arg: true,
18
- aliases: ['p', 'params']
19
- },
20
- hierafile: {
21
- has_arg: true,
22
- aliases: ['h', 'hierafile']
23
- },
24
- basepath: {
25
- has_arg: true,
26
- aliases: ['b', 'basepath']
27
- },
28
- encpath: {
29
- has_arg: true,
30
- aliases: ['e', 'encpath']
31
- },
32
- version: {
33
- has_arg: false,
34
- aliases: ['v', 'version']
35
- },
36
- yaml_facts: {
37
- has_arg: true,
38
- aliases: ['y', 'yaml']
39
- },
40
- json_facts: {
41
- has_arg: true,
42
- aliases: ['j', 'json']
43
- },
44
- interactive: {
45
- has_arg: false,
46
- aliases: ['i', 'interactive']
47
- }
48
- }
7
+ def available_options
8
+ {}
9
+ end
49
10
 
50
11
  def initialize(array)
51
12
  @options = {}
@@ -71,7 +32,7 @@ module Hieracles
71
32
 
72
33
  def optionkeys
73
34
  back = {}
74
- OPTIONS.each do |k, v|
35
+ available_options.each do |k, v|
75
36
  v[:aliases].each do |a|
76
37
  back[a] = { var: k, has_args: v[:has_arg] }
77
38
  end
@@ -0,0 +1,12 @@
1
+ require 'hieracles/puppetdb/fixsslconnectionadapter'
2
+ require 'hieracles/puppetdb/apierror'
3
+ require 'hieracles/puppetdb/query'
4
+ require 'hieracles/puppetdb/filter'
5
+ require 'hieracles/puppetdb/client'
6
+ require 'hieracles/puppetdb/request'
7
+ require 'hieracles/puppetdb/response'
8
+
9
+ module Hieracles
10
+ module Puppetdb
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ module Hieracles
2
+ module Puppetdb
3
+ class APIError < Exception
4
+ attr_reader :code, :response
5
+ def initialize(response)
6
+ @response = response
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,63 @@
1
+ require 'httparty'
2
+
3
+ module Hieracles
4
+ module Puppetdb
5
+ class Client
6
+ include HTTParty
7
+
8
+ def initialize(options, version = 3)
9
+ @version = version
10
+ setup_if_ssl(options)
11
+ options['port'] ||= 8080
12
+ options['host'] ||= 'localhost'
13
+ scheme = options['usessl'] ? "https://" : "http://"
14
+ self.class.base_uri(scheme +
15
+ options['host'] +
16
+ ':' + options['port'].to_s +
17
+ '/v' + @version.to_s())
18
+ end
19
+
20
+ def setup_if_ssl(options)
21
+ if options['usessl']
22
+ self.class.default_options = {:options => options}
23
+ self.class.connection_adapter(FixSSLConnectionAdapter)
24
+ end
25
+ end
26
+
27
+ def request(endpoint, method = 'get', query = nil, opts = {})
28
+ ret = send("#{method}_request".to_sym, endpoint, query, opts)
29
+ if ret.code.to_s() =~ /^[4|5]/ or ret.parsed_response.length < 1
30
+ notifications = [ Hieracles::Notification.new('puppetdb', 'No match.', 'error') ]
31
+ Response.new({}, 0, notifications)
32
+ else
33
+ total = ret.headers['X-Records']
34
+ if total.nil?
35
+ total = ret.parsed_response.length
36
+ end
37
+
38
+ Response.new(ret.parsed_response, total)
39
+ end
40
+ end
41
+
42
+ def get_request(endpoint, query, opts)
43
+ path = "/" + endpoint
44
+ if query
45
+ json_query = JSON.dump(query)
46
+ filtered_opts = {'query' => json_query}
47
+ opts.each do |k,v|
48
+ if k == :counts_filter
49
+ filtered_opts['counts-filter'] = JSON.dump(v)
50
+ else
51
+ filtered_opts[k.to_s.sub("_", "-")] = v
52
+ end
53
+ end
54
+ puts path ; puts filtered_opts ; exit(0)
55
+ self.class.get(path, query: filtered_opts)
56
+ else
57
+ self.class.get(path)
58
+ end
59
+ end
60
+
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ module Hieracles
2
+ module Puppetdb
3
+ class Filter
4
+
5
+ def initialize(regexp)
6
+ @regexp = Regexp.new(regexp)
7
+ end
8
+
9
+ def match(something)
10
+ @regexp.match something
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ require 'httparty'
2
+
3
+ module Hieracles
4
+ module Puppetdb
5
+ class FixSSLConnectionAdapter < HTTParty::ConnectionAdapter
6
+ def attach_ssl_certificates(http, options)
7
+ raise(IOError, "Cert file #{options['cert']} not found.") unless File.exist? options['cert'].to_s
8
+ raise(IOError, "Key file #{options['key']} not found.") unless File.exist? options['key']
9
+ raise(IOError, "CA file #{options['ca_file']} not found.") unless File.exist? options['ca_file']
10
+ http.cert = OpenSSL::X509::Certificate.new(File.read(options['cert']))
11
+ if options['key_password']
12
+ http.key = OpenSSL::PKey::RSA.new(File.read(options['key']), options['key_password'])
13
+ else
14
+ http.key = OpenSSL::PKey::RSA.new(File.read(options['key']))
15
+ end
16
+ http.ca_file = options['ca_file']
17
+ if options['verify_peer']
18
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
19
+ else
20
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,79 @@
1
+ module Hieracles
2
+ module Puppetdb
3
+ class Query
4
+
5
+ def initialize(elements)
6
+ @elements = parse(elements)
7
+ end
8
+
9
+ def parse(elements)
10
+ items = []
11
+ index = 0
12
+ if elements.length > 1
13
+ elements.each do |e|
14
+ puts items.inspect
15
+ if e == 'or'
16
+ index++
17
+ continue
18
+ end
19
+ if /(.*)(>|<|=|!=|~)(.*)/.match e
20
+ items[index] ||= []
21
+ items[index] << [$2, $1, $3]
22
+ end
23
+ end
24
+ else
25
+ if /(.*)(>|<|=|!=|~)(.*)/.match elements[0]
26
+ items = [$2, $1, $3]
27
+ end
28
+ end
29
+ items
30
+ end
31
+
32
+ def output
33
+ back = []
34
+ if @elements.length > 1
35
+ back << 'or'
36
+ @elements.each do |e|
37
+ back << build_and(e)
38
+ end
39
+ else
40
+ @elements.each do |e|
41
+ back << build_and(e)
42
+ end
43
+ end
44
+ back
45
+ end
46
+
47
+ def build_and(arrays)
48
+ if arrays.length > 1
49
+ ['and', arrays]
50
+ else
51
+ arrays
52
+ end
53
+ end
54
+
55
+ def defined?
56
+ @elements.count > 0
57
+ end
58
+
59
+ def match(item)
60
+ @elements.each do |e|
61
+ matched = false
62
+ e.each do |a|
63
+ case a[0]
64
+ when '='
65
+
66
+ when '!='
67
+ when '<'
68
+ when '>'
69
+ when '!'
70
+ else
71
+ end
72
+ end
73
+ return true if matched
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,44 @@
1
+ module Hieracles
2
+ module Puppetdb
3
+ class Request
4
+
5
+ def initialize(options)
6
+ @client = Hieracles::Puppetdb::Client.new options
7
+ end
8
+
9
+ def node_info(fqdn)
10
+ @client.request("nodes/#{fqdn}")
11
+ end
12
+ alias_method :node_infos, :node_info
13
+
14
+ def node_facts(fqdn, regex = //)
15
+ resp = @client.request("nodes/#{fqdn}/facts")
16
+ filter = Filter.new regex
17
+ resp.data = resp.data.reduce({}) do |a, d|
18
+ if filter.match(d['name'])
19
+ a[d['name'].to_sym] = d['value']
20
+ end
21
+ a
22
+ end
23
+ resp
24
+ end
25
+ alias_method :node_fact, :node_facts
26
+
27
+ def node_resources(fqdn, *args)
28
+ resp = @client.request("nodes/#{fqdn}/resources")
29
+ field = args.shift
30
+ resp.data = resp.data.reduce({}) do |a, d|
31
+ if !field || Regexp.new(field).match(d['title'])
32
+ a[d['title']] = d
33
+ end
34
+ a
35
+ end
36
+ resp
37
+ end
38
+ alias_method :node_res, :node_resources
39
+
40
+
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,14 @@
1
+ module Hieracles
2
+ module Puppetdb
3
+ class Response
4
+ attr_reader :data, :total_records, :notifications
5
+ attr_writer :data
6
+
7
+ def initialize(data, total_records = nil, notifications = nil)
8
+ @data = data
9
+ @total_records = total_records
10
+ @notifications = notifications
11
+ end
12
+ end
13
+ end
14
+ end
data/ppdb.1 ADDED
@@ -0,0 +1,158 @@
1
+ .TH ppdb 1 "2015-12-05" "version 0.2.1" "Hieracles command manual"
2
+
3
+ .SH NAME
4
+ ppdb \- Command line tool to query puppetdb
5
+ .SH SYNOPSIS
6
+ .B ppdb
7
+ .I object
8
+ .I command
9
+ .B [
10
+ .I options
11
+ .B ]
12
+
13
+ .SH DESCRIPTION
14
+ .PP
15
+ This tool is part of the Hieracles tools suite.
16
+ .PP
17
+ PuppetDB stores information gathered by Puppet when nodes run
18
+ the puppet client. It exposes a very neat REST interface, but
19
+ its query system can be a little complicated.
20
+ .PP
21
+ The purpose of
22
+ .B ppdb
23
+ is to overlay the calls to the REST API and propose a more
24
+ user-friendly way to query the Puppet Database (puppetdb).
25
+ .PP
26
+ Note that this tool is designed to work fine with the version 3
27
+ of the PuppetDB API.
28
+ .PP
29
+ Ref http://docs.puppetlabs.com/puppetdb/2.3/api/
30
+ .PP
31
+ .B ppdb
32
+ takes at minimum 2 arguments, an object (or endpoint)
33
+ and a subcommand, plus extra parameters depending the subcommand.
34
+ .PP
35
+ The object is like the endpoint in PuppetDB API, but we extend it
36
+ to a different list of possible objects:
37
+
38
+ .IP node 10
39
+ Uses the nodes endpoint. It accepts the subcommands:
40
+ .RS
41
+
42
+ .IP info 10
43
+ will display the equivalent of
44
+ .I "GET /v3/nodes/<NODE>"
45
+ and takes the node
46
+ .B certname
47
+ as an argument.
48
+ .RS 10
49
+ Optionaly, you can add a filter to limit what fields are displayed.
50
+ .TP
51
+ eg. ppdb node info <fqdn>
52
+ .RS
53
+ will display all the fields from the API response
54
+ .RE
55
+ eg. ppdb node info <fqdn> time
56
+ .RS
57
+ will only display the fields containing
58
+ .I time
59
+ in their label.
60
+ .RE
61
+ .RE
62
+
63
+ .IP facts 10
64
+ will display the equivalent of
65
+ .I "GET /v3/nodes/<NODE>/facts"
66
+ and takes the node
67
+ .B certname
68
+ as an argument.
69
+ .RS 10
70
+ As the result can be verbose, you can add a filter to limit what
71
+ fields are displayed.
72
+ .TP
73
+ eg. ppdb node facts <fqdn>
74
+ .RS
75
+ will display all the fields from the API response
76
+ .RE
77
+ eg. ppdb node facts <fqdn> memory
78
+ .RS
79
+ will only display the fields containing
80
+ .I memory
81
+ in their label.
82
+ .RE
83
+ .RE
84
+
85
+ .IP resources 10
86
+ will display the equivalent of
87
+ .I "GET /v3/nodes/<NODE>/resources"
88
+ and takes the node
89
+ .B certname
90
+ as an argument. You can use
91
+ .I res
92
+ instead of
93
+ .I resources
94
+ for shorter.
95
+ .RS 10
96
+ As the result can be verbose, you can add a filter to limit what
97
+ resources are displayed.
98
+ .TP
99
+ eg. ppdb node res <fqdn>
100
+ .RS
101
+ will display all the fields from the API response
102
+ .RE
103
+ eg. ppdb node res <fqdn> nagios
104
+ .RS
105
+ will only display the fields containing
106
+ .I nagios
107
+ in their label.
108
+ .RE
109
+ .RE
110
+
111
+ .SH OPTIONS
112
+
113
+ .TP
114
+ .PD 0
115
+ .B \-v
116
+ .TP
117
+ .PD
118
+ .B \-\-version
119
+ outputs version.
120
+
121
+
122
+ .SH FILES
123
+ .I ~/.config/hieracles/config.yaml
124
+ .RS
125
+ ppdb uses the same configuration file as hieracles, and relies on
126
+ its information to find the url of the PuppetDB
127
+
128
+ .SH EXAMPLES
129
+ A typical config file would contain, at minimum:
130
+ .PP
131
+ .RS
132
+ ---
133
+ .RE
134
+ .RS
135
+ puppetdb:
136
+ .RS
137
+ usessl: false
138
+ .RE
139
+ .RS
140
+ host: localhost
141
+ .RE
142
+ .RS
143
+ port: 8080
144
+ .RE
145
+ .RE
146
+
147
+ .SH SEE ALSO
148
+ hiera(1), puppet(8), hc(1)
149
+
150
+ .SH BUGS
151
+ Please report any bug to https://github.com/Gandi/hieracles/issues
152
+
153
+ .SH AUTHORS
154
+ Copyright (c) 2015 gandi.net https://gandi.net
155
+ .LP
156
+ Hieracles is written by mose@gandi.net
157
+ .LP
158
+ https://github.com/Gandi/hieracles