pl-puppetdb-ruby 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +71 -0
- data/LICENSE +17 -0
- data/README.md +248 -0
- data/lib/puppetdb.rb +8 -0
- data/lib/puppetdb/client.rb +197 -0
- data/lib/puppetdb/config.rb +87 -0
- data/lib/puppetdb/error.rb +17 -0
- data/lib/puppetdb/query.rb +58 -0
- data/lib/puppetdb/response.rb +10 -0
- data/lib/puppetdb/version.rb +3 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d0a1afa1ff2b6914268c1cc3872c03bc00ccf34e
|
4
|
+
data.tar.gz: f9293bb504fcb8aef8876db1887c269bdf2afae5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b5b66af95faddc9f0355c5e1ff0ef7a617cb32fb6ec883cbb3cfdf2701773ee65b374621ec7165a8a2f9c8f20cd736943c0b4ca009672c1bf3317cf0ecca6846
|
7
|
+
data.tar.gz: 8eb00096fad175271a91fd77552dbbb056ca8a0b3fe3c130aa21ba51b9e951d6942ee2f3e992291fece5608fd5cf279d4f3fbc2c7a34328c3260a9321360b851
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
Each new release typically also includes the latest modulesync defaults.
|
5
|
+
These should not impact the functionality of the module.
|
6
|
+
|
7
|
+
## Unreleased
|
8
|
+
|
9
|
+
**Implemented enhancements:**
|
10
|
+
|
11
|
+
- Add AccessDeniedError, UnauthorizedError and ForbiddenError exceptions
|
12
|
+
- Silence warnings about unspecified exception types in spec tests
|
13
|
+
- Add support for Puppet Enterprise RBAC token authentication
|
14
|
+
- Add support for loading configuration defaults from Puppet Enterprise Client Tools configuration files
|
15
|
+
<https://puppet.com/docs/pe/2018.1/installing_pe_client_tools.html#configuring-and-using-client-tools>
|
16
|
+
|
17
|
+
## [v1.1.1](https://github.com/voxpupuli/puppetdb-ruby/tree/v1.1.1) (2017-08-17)
|
18
|
+
[Full Changelog](https://github.com/voxpupuli/puppetdb-ruby/compare/1.1.0...v1.1.1)
|
19
|
+
|
20
|
+
**Fixed bugs:**
|
21
|
+
|
22
|
+
- Rubygem is broken for 1.1.0 [\#31](https://github.com/voxpupuli/puppetdb-ruby/issues/31)
|
23
|
+
|
24
|
+
**Merged pull requests:**
|
25
|
+
|
26
|
+
- Add badges [\#30](https://github.com/voxpupuli/puppetdb-ruby/pull/30) ([raphink](https://github.com/raphink))
|
27
|
+
|
28
|
+
## [1.1.0](https://github.com/voxpupuli/puppetdb-ruby/tree/1.1.0) (2017-08-17)
|
29
|
+
[Full Changelog](https://github.com/voxpupuli/puppetdb-ruby/compare/1.0.0...1.1.0)
|
30
|
+
|
31
|
+
**Closed issues:**
|
32
|
+
|
33
|
+
- Improve documentation [\#22](https://github.com/voxpupuli/puppetdb-ruby/issues/22)
|
34
|
+
- support for PQL in PuppetDB::Query [\#16](https://github.com/voxpupuli/puppetdb-ruby/issues/16)
|
35
|
+
- Any plans to revive this project? [\#12](https://github.com/voxpupuli/puppetdb-ruby/issues/12)
|
36
|
+
|
37
|
+
**Merged pull requests:**
|
38
|
+
|
39
|
+
- Add PQL support [\#28](https://github.com/voxpupuli/puppetdb-ruby/pull/28) ([raphink](https://github.com/raphink))
|
40
|
+
|
41
|
+
## [1.0.0](https://github.com/voxpupuli/puppetdb-ruby/tree/1.0.0) (2017-08-02)
|
42
|
+
**Implemented enhancements:**
|
43
|
+
|
44
|
+
- Preparatory cleanup for refactor [\#21](https://github.com/voxpupuli/puppetdb-ruby/pull/21) ([dhollinger](https://github.com/dhollinger))
|
45
|
+
- Allow usage of unauthenticated PuppetDB over SSL [\#14](https://github.com/voxpupuli/puppetdb-ruby/pull/14) ([cassianoleal](https://github.com/cassianoleal))
|
46
|
+
|
47
|
+
**Closed issues:**
|
48
|
+
|
49
|
+
- Refactor to support v4 endpoints [\#20](https://github.com/voxpupuli/puppetdb-ruby/issues/20)
|
50
|
+
- API V4 Example [\#13](https://github.com/voxpupuli/puppetdb-ruby/issues/13)
|
51
|
+
- 'summarize-by' parameter not supported [\#9](https://github.com/voxpupuli/puppetdb-ruby/issues/9)
|
52
|
+
- Thanks! [\#6](https://github.com/voxpupuli/puppetdb-ruby/issues/6)
|
53
|
+
- Only GET requests supported [\#4](https://github.com/voxpupuli/puppetdb-ruby/issues/4)
|
54
|
+
|
55
|
+
**Merged pull requests:**
|
56
|
+
|
57
|
+
- rename gemspec to fix deploy [\#27](https://github.com/voxpupuli/puppetdb-ruby/pull/27) ([dhollinger](https://github.com/dhollinger))
|
58
|
+
- Remove branches restriction in travis.yml [\#26](https://github.com/voxpupuli/puppetdb-ruby/pull/26) ([dhollinger](https://github.com/dhollinger))
|
59
|
+
- Add rubygems deploy key [\#25](https://github.com/voxpupuli/puppetdb-ruby/pull/25) ([dhollinger](https://github.com/dhollinger))
|
60
|
+
- Release 1.0.0 [\#24](https://github.com/voxpupuli/puppetdb-ruby/pull/24) ([dhollinger](https://github.com/dhollinger))
|
61
|
+
- Update Docs for Release 1.0.0 [\#23](https://github.com/voxpupuli/puppetdb-ruby/pull/23) ([dhollinger](https://github.com/dhollinger))
|
62
|
+
- Add command API support [\#19](https://github.com/voxpupuli/puppetdb-ruby/pull/19) ([bekbulatov](https://github.com/bekbulatov))
|
63
|
+
- \(maint\) Transfer ownership to Vox Pupuli [\#18](https://github.com/voxpupuli/puppetdb-ruby/pull/18) ([rlinehan](https://github.com/rlinehan))
|
64
|
+
- "Voxpupulify" [\#17](https://github.com/voxpupuli/puppetdb-ruby/pull/17) ([nibalizer](https://github.com/nibalizer))
|
65
|
+
- Add Contributing Guidelines [\#7](https://github.com/voxpupuli/puppetdb-ruby/pull/7) ([rlinehan](https://github.com/rlinehan))
|
66
|
+
- Feature/fix readme [\#3](https://github.com/voxpupuli/puppetdb-ruby/pull/3) ([robinbowes](https://github.com/robinbowes))
|
67
|
+
- Update README with correct initialization of client [\#1](https://github.com/voxpupuli/puppetdb-ruby/pull/1) ([ccaum](https://github.com/ccaum))
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
data/LICENSE
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
puppetdb-ruby
|
2
|
+
|
3
|
+
Copyright (C) 2013 Puppet Labs Inc
|
4
|
+
|
5
|
+
Puppet Labs can be contacted at: info@puppetlabs.com
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
you may not use this file except in compliance with the License.
|
9
|
+
You may obtain a copy of the License at
|
10
|
+
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
See the License for the specific language governing permissions and
|
17
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
# puppetdb-ruby
|
2
|
+
|
3
|
+
[![Build Status](https://img.shields.io/travis/voxpupuli/puppetdb-ruby.svg)](https://travis-ci.org/voxpupuli/puppetdb-ruby)
|
4
|
+
[![Gem Version](https://img.shields.io/gem/v/puppetdb-ruby.svg)](https://rubygems.org/gems/puppetdb-ruby)
|
5
|
+
[![Gem Downloads](https://img.shields.io/gem/dt/puppetdb-ruby.svg)](https://rubygems.org/gems/puppetdb-ruby)
|
6
|
+
[![By VoxPupuli](https://img.shields.io/badge/voxpupuli-%F0%9F%90%B1-orange.svg)](https://voxpupuli.org/)
|
7
|
+
|
8
|
+
a simple gem for interacting with the
|
9
|
+
[PuppetDB](https://github.com/puppetlabs/puppetdb) API.
|
10
|
+
|
11
|
+
This library was migrated from [puppetlabs](https://github.com/puppetlabs)
|
12
|
+
ownership to VoxPupuli on 19 October 2016.
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Installing from Ruby CLI:
|
17
|
+
```
|
18
|
+
gem install puppetdb-ruby
|
19
|
+
```
|
20
|
+
|
21
|
+
Include in Gemfile:
|
22
|
+
``` ruby
|
23
|
+
gem 'puppetdb-ruby'
|
24
|
+
```
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
Require the puppetdb gem in your ruby code.
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require 'puppetdb'
|
32
|
+
|
33
|
+
# Defaults to latest API version.
|
34
|
+
```
|
35
|
+
|
36
|
+
#### Create a new connection:
|
37
|
+
|
38
|
+
Non-SSL:
|
39
|
+
``` ruby
|
40
|
+
client = PuppetDB::Client.new({:server => 'http://localhost:8080'})
|
41
|
+
```
|
42
|
+
|
43
|
+
SSL with cert-based authentication:
|
44
|
+
``` ruby
|
45
|
+
client = PuppetDB::Client.new({
|
46
|
+
server_urls: 'https://localhost:8081',
|
47
|
+
cacert: '/path/to/ca.pem'
|
48
|
+
cert: '/path/to/certfile.pem',
|
49
|
+
key: '/path/to/keyfile.pem'
|
50
|
+
})
|
51
|
+
```
|
52
|
+
|
53
|
+
SSL with PE RBAC token based authentication:
|
54
|
+
``` ruby
|
55
|
+
client = PuppetDB::Client.new({
|
56
|
+
:server_urls => "https://localhost:8081",
|
57
|
+
:token => "my_pe_rbac_token",
|
58
|
+
:cacert => "/path/to/cacert.pem",
|
59
|
+
})
|
60
|
+
```
|
61
|
+
|
62
|
+
Configure connections to multiple PuppetDB's via `server_urls`
|
63
|
+
``` ruby
|
64
|
+
client = PuppetDB::Client.new({
|
65
|
+
:server_urls => "https://localhost:8081,https://localhost:8083",
|
66
|
+
:token => "my_pe_rbac_token",
|
67
|
+
:cacert => "/path/to/cacert.pem",
|
68
|
+
})
|
69
|
+
```
|
70
|
+
|
71
|
+
SSL with PE RBAC token based authentication, using all settings from PE Client Tools configurations:
|
72
|
+
``` ruby
|
73
|
+
client = PuppetDB::Client.new()
|
74
|
+
```
|
75
|
+
|
76
|
+
Note: When using cert-based authentication you must specify the full pem structure. When using token based authentication
|
77
|
+
you must NOT provide the pem structure and instead pass ':token' and ':cacert' (or allow them to be read from the
|
78
|
+
PE Client Tools configuration).
|
79
|
+
|
80
|
+
#### Query API usage
|
81
|
+
|
82
|
+
The Query Feature allows the user to request data from PuppetDB using the Query endpoints. It defaults to the latest version of the Query Endpoint.
|
83
|
+
|
84
|
+
Currently, `puppetdb-ruby` only supports the [AST Query Language](https://docs.puppet.com/puppetdb/5.0/api/query/v4/ast.html).
|
85
|
+
|
86
|
+
Support for the [PQL Query Language](https://docs.puppet.com/puppetdb/5.0/api/query/tutorial-pql.html) is planned for a future release.
|
87
|
+
|
88
|
+
Example:
|
89
|
+
``` ruby
|
90
|
+
response = client.request(
|
91
|
+
'nodes',
|
92
|
+
[:and,
|
93
|
+
[:'=', ['fact', 'kernel'], 'Linux'],
|
94
|
+
[:>, ['fact', 'uptime_days'], 30]
|
95
|
+
],
|
96
|
+
{:limit => 10}
|
97
|
+
)
|
98
|
+
|
99
|
+
nodes = response.data
|
100
|
+
|
101
|
+
# queries are composable
|
102
|
+
|
103
|
+
uptime = PuppetDB::Query[:>, [:fact, 'uptime_days'], 30]
|
104
|
+
redhat = PuppetDB::Query[:'=', [:fact, 'osfamily'], 'RedHat']
|
105
|
+
debian = PuppetDB::Query[:'=', [:fact, 'osfamily'], 'Debian']
|
106
|
+
|
107
|
+
client.request uptime.and(debian)
|
108
|
+
client.request uptime.and(redhat)
|
109
|
+
client.request uptime.and(debian.or(redhat))
|
110
|
+
```
|
111
|
+
|
112
|
+
If you have configured multiple PuppetDB's via [`server_urls`](https://puppet.com/docs/puppetdb/latest/pdb_client_tools.html#step-3-install-and-configure-the-puppetdb-cli)
|
113
|
+
then you can query in `:failover` mode. This will query each PuppetDB in `server_urls`
|
114
|
+
in order until it gets a successful response. It will fail with an `APIError` only if all queries fail.
|
115
|
+
|
116
|
+
``` ruby
|
117
|
+
response = client.request(
|
118
|
+
'nodes',
|
119
|
+
[:"=", "certname", "foo"],
|
120
|
+
{
|
121
|
+
:limit => 10
|
122
|
+
:query_mode => :failover
|
123
|
+
}
|
124
|
+
)
|
125
|
+
```
|
126
|
+
|
127
|
+
See the [PuppetDB API Docs](https://docs.puppet.com/puppetdb/5.0/api/index.html) for more.
|
128
|
+
|
129
|
+
|
130
|
+
#### PQL Queries usage
|
131
|
+
|
132
|
+
PQL queries are supported by using the empty endpoint.
|
133
|
+
|
134
|
+
Example:
|
135
|
+
``` ruby
|
136
|
+
response = client.request(
|
137
|
+
'',
|
138
|
+
'resources[title] { nodes { deactivated is null } }',
|
139
|
+
{:limit => 10}
|
140
|
+
)
|
141
|
+
|
142
|
+
resources = response.data
|
143
|
+
```
|
144
|
+
|
145
|
+
See the [PuppetDB API Docs](https://docs.puppet.com/puppetdb/5.0/api/query/v4/pql.html) for more on PQL queries.
|
146
|
+
|
147
|
+
|
148
|
+
#### Command API Usage
|
149
|
+
|
150
|
+
The Command Feature allows the user to execute REST Commands against the PuppetDB Command API Endpoints. It defaults to the latest version of the Command Endpoint.
|
151
|
+
|
152
|
+
The command method takes three arguments:
|
153
|
+
|
154
|
+
* `command`: a string identifying the command
|
155
|
+
* `payload`: a valid JSON object of any sort. It’s up to an individual handler function to determine how to interpret that object.
|
156
|
+
* `version`: a JSON integer describing what version of the given command you’re attempting to invoke. The version of the command also indicates the version of the wire format to use for the command.
|
157
|
+
|
158
|
+
Example:
|
159
|
+
``` ruby
|
160
|
+
client.command(
|
161
|
+
'deactivate node',
|
162
|
+
{'certname' => 'test1', 'producer_timestamp' => '2015-01-01'},
|
163
|
+
3
|
164
|
+
)
|
165
|
+
```
|
166
|
+
|
167
|
+
See the PuppetDB [Commands Endpoint Docs](https://docs.puppet.com/puppetdb/5.0/api/command/v1/commands.html) for more information.
|
168
|
+
|
169
|
+
#### Query the status endpoint(s)
|
170
|
+
|
171
|
+
You can get the status of all configured PuppetDB's by querying the `/status/v1/services` endpoints.
|
172
|
+
``` ruby
|
173
|
+
client.status
|
174
|
+
|
175
|
+
# The result will be of the form (one entry per server)
|
176
|
+
# {
|
177
|
+
# "http://localhost:8080": {
|
178
|
+
# "puppetdb-status": {
|
179
|
+
# "service_version": "6.3.1-SNAPSHOT",
|
180
|
+
# "service_status_version": 1,
|
181
|
+
# "detail_level": "info",
|
182
|
+
# "state": "running",
|
183
|
+
# "status": {
|
184
|
+
# "maintenance_mode?": false,
|
185
|
+
# "queue_depth": 0,
|
186
|
+
# "read_db_up?": true,
|
187
|
+
# "write_db_up?": true
|
188
|
+
# },
|
189
|
+
# "active_alerts": [
|
190
|
+
# ]
|
191
|
+
# },
|
192
|
+
# "status-service": {
|
193
|
+
# "service_version": "1.1.0",
|
194
|
+
# "service_status_version": 1,
|
195
|
+
# "detail_level": "info",
|
196
|
+
# "state": "running",
|
197
|
+
# "status": {
|
198
|
+
# },
|
199
|
+
# "active_alerts": [
|
200
|
+
# ]
|
201
|
+
# }
|
202
|
+
# }
|
203
|
+
# }
|
204
|
+
```
|
205
|
+
|
206
|
+
#### Export an archive of PuppetDB
|
207
|
+
|
208
|
+
You can [`export`](https://puppet.com/docs/puppetdb/5.1/anonymization.html#using-the-export-command) an archive
|
209
|
+
of your PuppetDB to a file. Optionally you can override the `anonymization_profile` (default: none).
|
210
|
+
|
211
|
+
``` ruby
|
212
|
+
client.export('path/for/new_puppetdb_export.tar.gz', anonymization_profile: :high)
|
213
|
+
```
|
214
|
+
|
215
|
+
#### Import an archive into PuppetDB
|
216
|
+
|
217
|
+
Once you have a PuppetDB export, it can be loaded into PuppetDB with [`import`](https://puppet.com/docs/puppetdb/5.1/anonymization.html#using-the-import-command).
|
218
|
+
|
219
|
+
``` ruby
|
220
|
+
client.import('path/to/existing_puppetdb_export.tar.gz')
|
221
|
+
```
|
222
|
+
|
223
|
+
## Tests
|
224
|
+
|
225
|
+
```
|
226
|
+
bundle install
|
227
|
+
bundle exec rspec
|
228
|
+
```
|
229
|
+
|
230
|
+
## Issues & Contributions
|
231
|
+
|
232
|
+
File issues or feature requests using [GitHub
|
233
|
+
issues](https://github.com/voxpupuli/puppetdb-ruby/issues).
|
234
|
+
|
235
|
+
If you are interested in contributing to this project, please see the
|
236
|
+
[Contribution Guidelines](CONTRIBUTING.md)
|
237
|
+
|
238
|
+
## Authors
|
239
|
+
|
240
|
+
This module was donated to VoxPupuli by Puppet Inc on 10-19-2016.
|
241
|
+
|
242
|
+
Nathaniel Smith <nathaniel@puppetlabs.com>
|
243
|
+
Lindsey Smith <lindsey@puppetlabs.com>
|
244
|
+
Ruth Linehan <ruth@puppetlabs.com>
|
245
|
+
|
246
|
+
## License
|
247
|
+
|
248
|
+
See LICENSE.
|
data/lib/puppetdb.rb
ADDED
@@ -0,0 +1,197 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
require 'puppetdb/error'
|
5
|
+
|
6
|
+
module PuppetDB
|
7
|
+
class FixSSLConnectionAdapter < HTTParty::ConnectionAdapter
|
8
|
+
def attach_ssl_certificates(http, options)
|
9
|
+
http.ca_file = options[:cacert]
|
10
|
+
http.cert = OpenSSL::X509::Certificate.new(File.read(options[:cert])) if options[:cert]
|
11
|
+
http.key = OpenSSL::PKey::RSA.new(File.read(options[:key])) if options[:key]
|
12
|
+
|
13
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Client
|
18
|
+
include HTTParty
|
19
|
+
attr_reader :use_ssl
|
20
|
+
attr_writer :logger
|
21
|
+
|
22
|
+
def hash_includes?(hash, *sought_keys)
|
23
|
+
sought_keys.each { |x| return false unless hash.include?(x) }
|
24
|
+
true
|
25
|
+
end
|
26
|
+
|
27
|
+
def debug(msg)
|
28
|
+
@logger.debug(msg) if @logger
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(settings = {}, query_api_version = 4, command_api_version = 1, admin_api_version = 1)
|
32
|
+
config = Config.new(settings, load_files: true)
|
33
|
+
@query_api_version = query_api_version
|
34
|
+
@command_api_version = command_api_version
|
35
|
+
@admin_api_version = admin_api_version
|
36
|
+
|
37
|
+
@servers = config.server_urls
|
38
|
+
pem = config.pem
|
39
|
+
token = config.token
|
40
|
+
puts config.config
|
41
|
+
puts pem
|
42
|
+
puts token.nil?
|
43
|
+
puts @servers
|
44
|
+
|
45
|
+
@servers.each do |server|
|
46
|
+
scheme = URI.parse(server).scheme
|
47
|
+
@use_ssl ||= scheme == 'https'
|
48
|
+
|
49
|
+
unless %w[http https].include? scheme
|
50
|
+
error_msg = "Configuration error: server_url '#{server}' must specify a protocol of either http or https"
|
51
|
+
raise error_msg
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
return unless @use_ssl
|
56
|
+
unless hash_includes?(pem, :cacert, :cert, :key) || (pem[:cert].nil? && pem[:key].nil?)
|
57
|
+
error_msg = 'Configuration error: https:// specified, but configuration is incomplete. It requires cacert and either cert and key, or a valid token.'
|
58
|
+
raise error_msg
|
59
|
+
end
|
60
|
+
|
61
|
+
self.class.default_options = pem
|
62
|
+
self.class.headers('X-Authentication' => token) if token
|
63
|
+
self.class.connection_adapter(FixSSLConnectionAdapter)
|
64
|
+
end
|
65
|
+
|
66
|
+
def raise_if_error(response)
|
67
|
+
raise UnauthorizedError, response if response.code == 401
|
68
|
+
raise ForbiddenError, response if response.code == 403
|
69
|
+
raise APIError, response if response.code.to_s =~ %r{^[4|5]}
|
70
|
+
end
|
71
|
+
|
72
|
+
def request(endpoint, query, opts = {})
|
73
|
+
path = "/pdb/query/v#{@query_api_version}"
|
74
|
+
if endpoint == ''
|
75
|
+
# PQL
|
76
|
+
json_query = query
|
77
|
+
else
|
78
|
+
path += "/#{endpoint}"
|
79
|
+
query = PuppetDB::Query.maybe_promote(query)
|
80
|
+
json_query = query.build
|
81
|
+
end
|
82
|
+
|
83
|
+
query_mode = opts.delete(:query_mode) || :first
|
84
|
+
filtered_opts = { 'query' => json_query }
|
85
|
+
opts.each do |k, v|
|
86
|
+
if k == :counts_filter
|
87
|
+
filtered_opts['counts-filter'] = JSON.dump(v)
|
88
|
+
else
|
89
|
+
filtered_opts[k.to_s.sub('_', '-')] = v
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
debug("#{path} #{json_query} #{opts}")
|
94
|
+
|
95
|
+
if query_mode == :first
|
96
|
+
self.class.base_uri(@servers.first)
|
97
|
+
ret = self.class.get(path, body: filtered_opts)
|
98
|
+
raise_if_error(ret)
|
99
|
+
|
100
|
+
total = ret.headers['X-Records']
|
101
|
+
total = ret.parsed_response.length if total.nil?
|
102
|
+
|
103
|
+
Response.new(ret.parsed_response, total)
|
104
|
+
elsif query_mode == :failover
|
105
|
+
|
106
|
+
ret = nil
|
107
|
+
@servers.each do |server|
|
108
|
+
self.class.base_uri(server)
|
109
|
+
ret = self.class.get(path, body: filtered_opts)
|
110
|
+
if ret.code < 400
|
111
|
+
total = ret.headers['X-Records']
|
112
|
+
total = ret.parsed_response.length if total.nil?
|
113
|
+
|
114
|
+
return Response.new(ret.parsed_response, total)
|
115
|
+
else
|
116
|
+
debug("query on '#{server}' failed with #{ret.code}")
|
117
|
+
end
|
118
|
+
end
|
119
|
+
raise APIError, ret
|
120
|
+
else
|
121
|
+
raise ArgumentError, "Query mode '#{query_mode}' is not supported (try :first or :failover)."
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def command(command, payload, version)
|
126
|
+
path = "/pdb/cmd/v#{@command_api_version}"
|
127
|
+
|
128
|
+
query = {
|
129
|
+
'command' => command,
|
130
|
+
'version' => version,
|
131
|
+
'certname' => payload['certname']
|
132
|
+
}
|
133
|
+
|
134
|
+
debug("#{path} #{query} #{payload}")
|
135
|
+
|
136
|
+
self.class.base_uri(@servers.first)
|
137
|
+
ret = self.class.post(
|
138
|
+
path,
|
139
|
+
query: query,
|
140
|
+
body: payload.to_json,
|
141
|
+
headers: {
|
142
|
+
'Accept' => 'application/json',
|
143
|
+
'Content-Type' => 'application/json'
|
144
|
+
}
|
145
|
+
)
|
146
|
+
raise_if_error(ret)
|
147
|
+
|
148
|
+
Response.new(ret.parsed_response)
|
149
|
+
end
|
150
|
+
|
151
|
+
def export(filename, opts = {})
|
152
|
+
self.class.base_uri(@servers.first)
|
153
|
+
path = "/pdb/admin/v#{@admin_api_version}/archive"
|
154
|
+
|
155
|
+
# Allow opts to override anonymization_profile, but enforce
|
156
|
+
# stream_body to avoid using memory
|
157
|
+
params = { anonymization_profile: 'none' }.
|
158
|
+
merge(opts).
|
159
|
+
merge(stream_body: true)
|
160
|
+
|
161
|
+
File.open(filename, 'w') do |file|
|
162
|
+
self.class.get(path, params) do |fragment|
|
163
|
+
if [301, 302].include?(fragment.code)
|
164
|
+
debug 'Skip streaming write for redirect'
|
165
|
+
elsif fragment.code == 200
|
166
|
+
file.write(fragment)
|
167
|
+
else
|
168
|
+
raise StandardError, "Non-success status code while streaming #{fragment.code}"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def import(filename)
|
175
|
+
self.class.base_uri(@servers.first)
|
176
|
+
path = "/pdb/admin/v#{@admin_api_version}/archive"
|
177
|
+
self.class.post(path, body: { archive: File.open(filename) })
|
178
|
+
end
|
179
|
+
|
180
|
+
def status
|
181
|
+
status_endpoint = '/status/v1/services'
|
182
|
+
status_map = {}
|
183
|
+
|
184
|
+
@servers.each do |server|
|
185
|
+
self.class.base_uri(server)
|
186
|
+
ret = self.class.get(status_endpoint)
|
187
|
+
|
188
|
+
status_map[server] = if ret.code >= 400
|
189
|
+
{ error: "Unable to build JSON object from server: #{server}" }
|
190
|
+
else
|
191
|
+
ret.parsed_response
|
192
|
+
end
|
193
|
+
end
|
194
|
+
status_map
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
class PuppetDB::Config
|
4
|
+
def initialize(overrides = nil, load_files = false)
|
5
|
+
@overrides = {}
|
6
|
+
overrides.each { |k, v| @overrides[k.to_sym] = v } unless overrides.nil?
|
7
|
+
|
8
|
+
@load_files = load_files
|
9
|
+
end
|
10
|
+
|
11
|
+
def load_file(path)
|
12
|
+
File.open(path) { |f| JSON.parse(f.read, symbolize_names: true) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def puppetlabs_root
|
16
|
+
'/etc/puppetlabs'
|
17
|
+
end
|
18
|
+
|
19
|
+
def global_conf
|
20
|
+
File.join(puppetlabs_root, 'client-tools', 'puppetdb.conf')
|
21
|
+
end
|
22
|
+
|
23
|
+
def user_root
|
24
|
+
File.join(Dir.home, '.puppetlabs')
|
25
|
+
end
|
26
|
+
|
27
|
+
def user_conf
|
28
|
+
File.join(user_root, 'client-tools', 'puppetdb.conf')
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_cacert
|
32
|
+
"#{puppetlabs_root}/puppet/ssl/certs/ca.pem"
|
33
|
+
end
|
34
|
+
|
35
|
+
def defaults
|
36
|
+
{
|
37
|
+
cacert: default_cacert,
|
38
|
+
token_file: File.join(user_root, 'token')
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_config
|
43
|
+
config = defaults
|
44
|
+
if @load_files
|
45
|
+
if File.exist?(global_conf) && File.readable?(global_conf)
|
46
|
+
config = config.merge(load_file(global_conf))
|
47
|
+
end
|
48
|
+
|
49
|
+
if @overrides[:config_file]
|
50
|
+
config = config.merge(load_file(@overrides[:config_file]))
|
51
|
+
elsif File.exist?(user_conf) && File.readable?(user_conf)
|
52
|
+
config = config.merge(load_file(user_conf))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
config.merge(@overrides)
|
57
|
+
end
|
58
|
+
|
59
|
+
def config
|
60
|
+
@config ||= load_config
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_token
|
64
|
+
if @config.include?(:token)
|
65
|
+
@config[:token]
|
66
|
+
elsif File.readable?(config[:token_file])
|
67
|
+
File.read(config[:token_file]).strip
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def token
|
72
|
+
@token ||= load_token
|
73
|
+
end
|
74
|
+
|
75
|
+
def server_urls
|
76
|
+
return config[:server_urls].split(',') if config[:server_urls].is_a?(String)
|
77
|
+
config[:server_urls] || []
|
78
|
+
end
|
79
|
+
|
80
|
+
def pem
|
81
|
+
@config.select { |k, _| [:cacert, :cert, :key].include?(k) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def [](key)
|
85
|
+
@config[key]
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module PuppetDB
|
2
|
+
class APIError < RuntimeError
|
3
|
+
attr_reader :code, :response
|
4
|
+
def initialize(response)
|
5
|
+
@response = response
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class AccessDeniedError < APIError
|
10
|
+
end
|
11
|
+
|
12
|
+
class ForbiddenError < AccessDeniedError
|
13
|
+
end
|
14
|
+
|
15
|
+
class UnauthorizedError < AccessDeniedError
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PuppetDB
|
4
|
+
class Query
|
5
|
+
attr_reader :sexpr
|
6
|
+
|
7
|
+
def initialize(query = [])
|
8
|
+
@sexpr = query
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.[](*args)
|
12
|
+
Query.new(args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.maybe_promote(query)
|
16
|
+
return Query.new(query) unless query.class == Query
|
17
|
+
query
|
18
|
+
end
|
19
|
+
|
20
|
+
def empty?
|
21
|
+
@sexpr.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def compose(query)
|
25
|
+
query = self.class.maybe_promote(query)
|
26
|
+
|
27
|
+
# If an operand is the empty query ([]), compose returns a copy
|
28
|
+
# of the non-empty operand. If both operands are empty, the
|
29
|
+
# empty query is returned. If both operands are non-empty, the
|
30
|
+
# compose continues.
|
31
|
+
if query.empty? && !empty?
|
32
|
+
Query.new(@sexpr)
|
33
|
+
elsif empty? && !query.empty?
|
34
|
+
Query.new(query.sexpr)
|
35
|
+
elsif empty? && query.empty?
|
36
|
+
Query.new([])
|
37
|
+
else
|
38
|
+
yield query
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def and(query)
|
43
|
+
compose(query) { |q| Query.new([:and, @sexpr, q.sexpr]) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def or(query)
|
47
|
+
compose(query) { |q| Query.new([:or, @sexpr, q.sexpr]) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def push(query)
|
51
|
+
compose(query) { |q| Query.new(@sexpr.dup.push(q.sexpr)) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def build
|
55
|
+
JSON.dump(@sexpr)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pl-puppetdb-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Austin Blatt
|
8
|
+
- Vox Pupuli
|
9
|
+
- Nathaniel Smith
|
10
|
+
- Lindsey Smith
|
11
|
+
autorequire:
|
12
|
+
bindir: bin
|
13
|
+
cert_chain: []
|
14
|
+
date: 2019-04-29 00:00:00.000000000 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: httparty
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '0'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: rspec
|
46
|
+
requirement: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
type: :development
|
52
|
+
prerelease: false
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: mocha
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
type: :development
|
66
|
+
prerelease: false
|
67
|
+
version_requirements: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
- !ruby/object:Gem::Dependency
|
73
|
+
name: rubocop
|
74
|
+
requirement: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 0.48.1
|
79
|
+
type: :development
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - '='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.48.1
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: rubocop-rspec
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - '='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.15.1
|
93
|
+
type: :development
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - '='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.15.1
|
100
|
+
- !ruby/object:Gem::Dependency
|
101
|
+
name: github_changelog_generator
|
102
|
+
requirement: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
type: :development
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
description:
|
115
|
+
email: austin.blatt@puppet.com
|
116
|
+
executables: []
|
117
|
+
extensions: []
|
118
|
+
extra_rdoc_files: []
|
119
|
+
files:
|
120
|
+
- CHANGELOG.md
|
121
|
+
- LICENSE
|
122
|
+
- README.md
|
123
|
+
- lib/puppetdb.rb
|
124
|
+
- lib/puppetdb/client.rb
|
125
|
+
- lib/puppetdb/config.rb
|
126
|
+
- lib/puppetdb/error.rb
|
127
|
+
- lib/puppetdb/query.rb
|
128
|
+
- lib/puppetdb/response.rb
|
129
|
+
- lib/puppetdb/version.rb
|
130
|
+
homepage: https://github.com/austb/puppetdb-ruby
|
131
|
+
licenses:
|
132
|
+
- apache
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.6.14.1
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Simple Ruby client library for PuppetDB API
|
154
|
+
test_files: []
|