bolt 0.16.3 → 0.16.4
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/exe/bolt-inventory-pdb +6 -0
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +2 -2
- data/lib/bolt/inventory/group.rb +22 -19
- data/lib/bolt/transport/orch.rb +20 -9
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_ext/puppetdb_inventory.rb +243 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dabc4d0d53c4dc91e19ca332cc074c70026d9934
|
4
|
+
data.tar.gz: e4bd3c566ddb2cb37724de78063c62eafc4e2fef
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 579ed3e93ab4dc82d5ded1dedac8f42793217a6832360c13b8594cab22e5b2a6f6222ecb2a280bb7d6024f93374e3caca9037350c6dc05e80856202be813f84c
|
7
|
+
data.tar.gz: 25bff2cb74e014d109f3de109a1bf330a169367a528ffa40b29246020ca905578e90d0c4db08334455cb0d1b2a7ad98e1b8039fce5393546feb0de6947c85528
|
data/lib/bolt/executor.rb
CHANGED
@@ -84,7 +84,7 @@ module Bolt
|
|
84
84
|
next if promise.fulfilled?
|
85
85
|
error = $ERROR_INFO || Bolt::Error.new("No result was returned for #{target.uri}",
|
86
86
|
"puppetlabs.bolt/missing-result-error")
|
87
|
-
promise.set(Bolt::Result.from_exception(error))
|
87
|
+
promise.set(Bolt::Result.from_exception(target, error))
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
data/lib/bolt/inventory.rb
CHANGED
@@ -91,9 +91,9 @@ module Bolt
|
|
91
91
|
# Pass a target to get_targets for a public version of this
|
92
92
|
# Should this reconfigure configured targets?
|
93
93
|
def update_target(target)
|
94
|
-
inv_conf = config_for(target.
|
94
|
+
inv_conf = config_for(target.name)
|
95
95
|
unless inv_conf
|
96
|
-
@logger.debug("Did not find #{target.
|
96
|
+
@logger.debug("Did not find #{target.name} in inventory")
|
97
97
|
inv_conf = {}
|
98
98
|
end
|
99
99
|
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -6,19 +6,21 @@ module Bolt
|
|
6
6
|
attr_accessor :name, :nodes, :groups, :config, :rest
|
7
7
|
|
8
8
|
def initialize(data)
|
9
|
+
@logger = Logging.logger[self]
|
9
10
|
@name = data['name']
|
10
11
|
|
11
|
-
@nodes =
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
12
|
+
@nodes = {}
|
13
|
+
if data['nodes']
|
14
|
+
data['nodes'].each do |n|
|
15
|
+
n = { 'name' => n } if n.is_a? String
|
16
|
+
if @nodes.include? n['name']
|
17
|
+
@logger.warn("Ignoring duplicate node in #{@name}: #{n}")
|
18
|
+
else
|
19
|
+
@nodes[n['name']] = n
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
22
24
|
@config = data['config'] || {}
|
23
25
|
@groups = if data['groups']
|
24
26
|
data['groups'].map { |g| Group.new(g) }
|
@@ -44,11 +46,12 @@ module Bolt
|
|
44
46
|
|
45
47
|
used_names << @name
|
46
48
|
|
47
|
-
@nodes.
|
48
|
-
# Require nodes to be
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
@nodes.each_value do |n|
|
50
|
+
# Require nodes to be parseable as a Target.
|
51
|
+
begin
|
52
|
+
Target.new(n['name'])
|
53
|
+
rescue Addressable::URI::InvalidURIError => e
|
54
|
+
@logger.debug(e)
|
52
55
|
raise ValidationError.new("Invalid node name #{n['name']}", n['name'])
|
53
56
|
end
|
54
57
|
|
@@ -80,7 +83,7 @@ module Bolt
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def node_data(node_name)
|
83
|
-
if (data = @nodes
|
86
|
+
if (data = @nodes[node_name])
|
84
87
|
{ 'config' => data['config'] || {},
|
85
88
|
# groups come from group_data
|
86
89
|
'groups' => [] }
|
@@ -123,7 +126,7 @@ module Bolt
|
|
123
126
|
end
|
124
127
|
|
125
128
|
def local_node_names
|
126
|
-
|
129
|
+
Set.new(@nodes.keys)
|
127
130
|
end
|
128
131
|
private :local_node_names
|
129
132
|
|
@@ -149,7 +152,7 @@ module Bolt
|
|
149
152
|
|
150
153
|
if data
|
151
154
|
data_merge(group_data, data)
|
152
|
-
elsif
|
155
|
+
elsif @nodes.include?(node_name)
|
153
156
|
group_data
|
154
157
|
end
|
155
158
|
end
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -11,15 +11,18 @@ module Bolt
|
|
11
11
|
CONF_FILE = File.expand_path('~/.puppetlabs/client-tools/orchestrator.conf')
|
12
12
|
BOLT_MOCK_TASK = Struct.new(:name, :executable).new('bolt', 'bolt/tasks/init').freeze
|
13
13
|
|
14
|
-
def
|
15
|
-
super
|
16
|
-
|
14
|
+
def create_client(opts)
|
17
15
|
client_keys = %i[service-url token-file cacert]
|
18
|
-
|
19
|
-
|
16
|
+
client_opts = opts.reduce({}) do |acc, (k, v)|
|
17
|
+
if client_keys.include?(k)
|
18
|
+
acc.merge(k.to_s => v)
|
19
|
+
else
|
20
|
+
acc
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@logger.debug("Creating orchestrator client for #{client_opts}")
|
20
24
|
|
21
|
-
|
22
|
-
OrchestratorClient.new(@client_opts, true)
|
25
|
+
OrchestratorClient.new(client_opts, true)
|
23
26
|
end
|
24
27
|
|
25
28
|
def build_request(targets, task, arguments)
|
@@ -113,7 +116,11 @@ module Bolt
|
|
113
116
|
end
|
114
117
|
|
115
118
|
def batches(targets)
|
116
|
-
targets.group_by
|
119
|
+
targets.group_by do |target|
|
120
|
+
[target.options[:orch_task_environment],
|
121
|
+
target.options[:"service-url"],
|
122
|
+
target.options[:"token-file"]]
|
123
|
+
end.values
|
117
124
|
end
|
118
125
|
|
119
126
|
def run_task_job(targets, task, arguments)
|
@@ -124,9 +131,13 @@ module Bolt
|
|
124
131
|
end
|
125
132
|
|
126
133
|
begin
|
127
|
-
results = create_client.run_task(body)
|
134
|
+
results = create_client(targets.first.options).run_task(body)
|
128
135
|
|
129
136
|
process_run_results(targets, results)
|
137
|
+
rescue OrchestratorClient::ApiError => e
|
138
|
+
targets.map do |target|
|
139
|
+
Bolt::Result.new(target, error: e.data)
|
140
|
+
end
|
130
141
|
rescue StandardError => e
|
131
142
|
targets.map do |target|
|
132
143
|
Bolt::Result.from_exception(target, e)
|
data/lib/bolt/version.rb
CHANGED
@@ -0,0 +1,243 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'httpclient'
|
5
|
+
require 'optparse'
|
6
|
+
require 'yaml'
|
7
|
+
|
8
|
+
module Bolt
|
9
|
+
class PuppetDBInventory
|
10
|
+
class Client
|
11
|
+
def self.from_config(config)
|
12
|
+
uri = URI.parse(config['server_urls'].first)
|
13
|
+
uri.port ||= 8081
|
14
|
+
|
15
|
+
cacert = File.expand_path(config['cacert'])
|
16
|
+
token = config.token
|
17
|
+
|
18
|
+
cert = config['cert']
|
19
|
+
key = config['key']
|
20
|
+
|
21
|
+
new(uri, cacert, token: token, cert: cert, key: key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(uri, cacert, token: nil, cert: nil, key: nil)
|
25
|
+
@uri = uri
|
26
|
+
@cacert = cacert
|
27
|
+
@token = token
|
28
|
+
@cert = cert
|
29
|
+
@key = key
|
30
|
+
end
|
31
|
+
|
32
|
+
def query_certnames(query)
|
33
|
+
return [] unless query
|
34
|
+
|
35
|
+
body = JSON.generate(query: query)
|
36
|
+
|
37
|
+
response = http_client.post("#{@uri}/pdb/query/v4", body: body, header: headers)
|
38
|
+
if response.code != 200
|
39
|
+
raise "Failed to query PuppetDB: #{response.body}"
|
40
|
+
else
|
41
|
+
results = JSON.parse(response.body)
|
42
|
+
if results.first && !results.first.key?('certname')
|
43
|
+
raise "Query results did not contain a 'certname' field: got #{results.first.keys.join(', ')}"
|
44
|
+
end
|
45
|
+
results.map { |result| result['certname'] }.uniq
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def http_client
|
50
|
+
return @http if @http
|
51
|
+
@http = HTTPClient.new
|
52
|
+
@http.ssl_config.set_client_cert_file(@cert, @key)
|
53
|
+
@http.ssl_config.add_trust_ca(@cacert)
|
54
|
+
|
55
|
+
@http
|
56
|
+
end
|
57
|
+
|
58
|
+
def headers
|
59
|
+
headers = { 'Content-Type' => 'application/json' }
|
60
|
+
headers['X-Authentication'] = @token if @token
|
61
|
+
headers
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Config
|
66
|
+
DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
|
67
|
+
DEFAULT_CONFIG = File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf')
|
68
|
+
|
69
|
+
def initialize(config_file, options)
|
70
|
+
@settings = load_config(config_file)
|
71
|
+
@settings.merge!(options)
|
72
|
+
|
73
|
+
expand_paths
|
74
|
+
validate
|
75
|
+
end
|
76
|
+
|
77
|
+
def load_config(filename)
|
78
|
+
if filename
|
79
|
+
if File.exist?(filename)
|
80
|
+
config = JSON.parse(File.read(filename))
|
81
|
+
else
|
82
|
+
raise "config file #{filename} does not exist"
|
83
|
+
end
|
84
|
+
elsif File.exist?(DEFAULT_CONFIG)
|
85
|
+
config = JSON.parse(File.read(DEFAULT_CONFIG))
|
86
|
+
end
|
87
|
+
config.fetch('puppetdb', {})
|
88
|
+
end
|
89
|
+
|
90
|
+
def token
|
91
|
+
return @token if @token
|
92
|
+
if @settings['token']
|
93
|
+
File.read(@settings['token'])
|
94
|
+
elsif File.exist?(DEFAULT_TOKEN)
|
95
|
+
File.read(DEFAULT_TOKEN)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def [](key)
|
100
|
+
@settings[key]
|
101
|
+
end
|
102
|
+
|
103
|
+
def expand_paths
|
104
|
+
%w[cacert cert key token].each do |file|
|
105
|
+
@settings[file] = File.expand_path(@settings[file]) if @settings[file]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def validate_file_exists(file)
|
110
|
+
if @settings[file] && !File.exist?(@settings[file])
|
111
|
+
raise "#{file} file #{@settings[file]} does not exist"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def validate
|
116
|
+
unless @settings['server_urls']
|
117
|
+
raise "server_urls must be specified in the config file or with --url"
|
118
|
+
end
|
119
|
+
unless @settings['cacert']
|
120
|
+
raise "cacert must be specified in the config file or with --cacert"
|
121
|
+
end
|
122
|
+
|
123
|
+
if (@settings['cert'] && !@settings['key']) ||
|
124
|
+
(!@settings['cert'] && @settings['key'])
|
125
|
+
raise "cert and key must be specified together"
|
126
|
+
end
|
127
|
+
|
128
|
+
validate_file_exists('cacert')
|
129
|
+
validate_file_exists('cert')
|
130
|
+
validate_file_exists('key')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class CLI
|
135
|
+
def initialize(args)
|
136
|
+
@args = args
|
137
|
+
@cli_opts = {}
|
138
|
+
@parser = build_parser
|
139
|
+
end
|
140
|
+
|
141
|
+
def build_parser
|
142
|
+
parser = OptionParser.new('') do |opts|
|
143
|
+
opts.on('--cacert CACERT', "Path to the CA certificate") do |cacert|
|
144
|
+
@cli_opts['cacert'] = cacert
|
145
|
+
end
|
146
|
+
opts.on('--cert CERT', "Path to the certificate") do |cert|
|
147
|
+
@cli_opts['cert'] = cert
|
148
|
+
end
|
149
|
+
opts.on('--key KEY', "Path to the private key") do |key|
|
150
|
+
@cli_opts['key'] = key
|
151
|
+
end
|
152
|
+
opts.on('--token-file TOKEN',
|
153
|
+
"Path to the token file",
|
154
|
+
"Default: #{Config::DEFAULT_TOKEN} if present") do |token|
|
155
|
+
@cli_opts['token'] = token
|
156
|
+
end
|
157
|
+
opts.on('--url URL', "The URL of the PuppetDB server to connect to") do |url|
|
158
|
+
@cli_opts['server_urls'] = [url]
|
159
|
+
end
|
160
|
+
opts.on('--config CONFIG',
|
161
|
+
"The puppetdb.conf file to read configuration from",
|
162
|
+
"Default: #{Config::DEFAULT_CONFIG} if present") do |file|
|
163
|
+
@config_file = File.expand_path(file)
|
164
|
+
end
|
165
|
+
opts.on('--output FILE', '-o FILE',
|
166
|
+
"Where to write the generated inventory file, defaults to stdout") do |file|
|
167
|
+
@output_file = file
|
168
|
+
end
|
169
|
+
opts.on('--trace', "Show stacktraces for exceptions") do |trace|
|
170
|
+
@trace = trace
|
171
|
+
end
|
172
|
+
opts.on('-h', '--help', "Display help") do |_|
|
173
|
+
@show_help = true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
parser.banner = <<-BANNER
|
177
|
+
Usage: bolt-inventory-pdb <input-file> [--output <output-file>] [--url <url>] [auth-options]
|
178
|
+
|
179
|
+
Populate the nodes in an inventory file based on PuppetDB queries.
|
180
|
+
|
181
|
+
The input file should be a Bolt inventory file, where each 'nodes' entry is
|
182
|
+
replaced with a 'query' entry to be executed against PuppetDB. The output will
|
183
|
+
be the input file, with the 'nodes' entry for each group populated with the
|
184
|
+
query results.
|
185
|
+
|
186
|
+
BANNER
|
187
|
+
parser
|
188
|
+
end
|
189
|
+
|
190
|
+
def run
|
191
|
+
positional_args = @parser.permute(@args)
|
192
|
+
|
193
|
+
if @show_help
|
194
|
+
puts @parser.help
|
195
|
+
return 0
|
196
|
+
end
|
197
|
+
|
198
|
+
inventory_file = positional_args.shift
|
199
|
+
unless inventory_file
|
200
|
+
raise "--inventory is a required option"
|
201
|
+
end
|
202
|
+
|
203
|
+
if positional_args.any?
|
204
|
+
raise "Unknown argument(s) #{positional_args.join(', ')}"
|
205
|
+
end
|
206
|
+
|
207
|
+
config = Config.new(@config_file, @cli_opts)
|
208
|
+
@puppetdb_client = Client.from_config(config)
|
209
|
+
|
210
|
+
unless File.readable?(inventory_file)
|
211
|
+
raise "Can't read the inventory file #{inventory_file}"
|
212
|
+
end
|
213
|
+
|
214
|
+
inventory = YAML.load_file(inventory_file)
|
215
|
+
resolve_group(inventory)
|
216
|
+
|
217
|
+
result = inventory.to_yaml
|
218
|
+
|
219
|
+
if @output_file
|
220
|
+
File.write(@output_file, result)
|
221
|
+
else
|
222
|
+
puts result
|
223
|
+
end
|
224
|
+
|
225
|
+
return 0
|
226
|
+
rescue StandardError => e
|
227
|
+
puts "Error: #{e}"
|
228
|
+
puts e.backtrace if @trace
|
229
|
+
return 1
|
230
|
+
end
|
231
|
+
|
232
|
+
def resolve_group(group)
|
233
|
+
group['nodes'] = @puppetdb_client.query_certnames(group['query'])
|
234
|
+
|
235
|
+
group.fetch('groups', []).each do |child|
|
236
|
+
resolve_group(child)
|
237
|
+
end
|
238
|
+
|
239
|
+
group
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-02-
|
11
|
+
date: 2018-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -72,14 +72,14 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0.2.
|
75
|
+
version: 0.2.3
|
76
76
|
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0.2.
|
82
|
+
version: 0.2.3
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: terminal-table
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -329,6 +329,7 @@ email:
|
|
329
329
|
- puppet@puppet.com
|
330
330
|
executables:
|
331
331
|
- bolt
|
332
|
+
- bolt-inventory-pdb
|
332
333
|
extensions: []
|
333
334
|
extra_rdoc_files: []
|
334
335
|
files:
|
@@ -344,6 +345,7 @@ files:
|
|
344
345
|
- bolt-modules/boltlib/lib/puppet/functions/run_task.rb
|
345
346
|
- bolt-modules/boltlib/types/targetspec.pp
|
346
347
|
- exe/bolt
|
348
|
+
- exe/bolt-inventory-pdb
|
347
349
|
- lib/bolt.rb
|
348
350
|
- lib/bolt/cli.rb
|
349
351
|
- lib/bolt/config.rb
|
@@ -370,6 +372,7 @@ files:
|
|
370
372
|
- lib/bolt/transport/winrm/connection.rb
|
371
373
|
- lib/bolt/util.rb
|
372
374
|
- lib/bolt/version.rb
|
375
|
+
- lib/bolt_ext/puppetdb_inventory.rb
|
373
376
|
- vendored/facter/lib/facter.rb
|
374
377
|
- vendored/facter/lib/facter/Cfkey.rb
|
375
378
|
- vendored/facter/lib/facter/application.rb
|