hieracles 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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