bolt 2.36.0 → 2.37.0
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/lib/bolt/bolt_option_parser.rb +7 -3
- data/lib/bolt/cli.rb +16 -0
- data/lib/bolt/config.rb +14 -2
- data/lib/bolt/config/options.rb +30 -2
- data/lib/bolt/plugin.rb +41 -5
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/module.rb +4 -4
- data/lib/bolt/project.rb +18 -6
- data/lib/bolt/rerun.rb +1 -5
- data/lib/bolt/shell/bash.rb +7 -1
- data/lib/bolt/shell/powershell.rb +16 -0
- data/lib/bolt/util.rb +22 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/plugin.rb +13 -0
- data/lib/bolt_server/plugin/puppet_connect_data.rb +37 -0
- data/lib/bolt_server/schemas/connect-data.json +22 -0
- data/lib/bolt_server/transport_app.rb +39 -11
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cbd90e42181c76bad4eee913ee67369202296852cfa5153a1f5490def4e77d3
|
4
|
+
data.tar.gz: 87ac43d10edeebefaa31f581f49cbcf9f729e25f5d7efec583e09dcf04f97be6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 392f0d453c49a536852692e873e5c5f55807294fa0bd914593722c26d39459e494b83453a3b995319b04b8f2e6f3a1f04f7661400841192bdffc56df47e4170e
|
7
|
+
data.tar.gz: 750cd8e9c29008e8bb4dfc7350d52a268c01d91fa088e9007ff241648e31a90e76d9cabfef9f1db5675d33f6b82b2898a54f758bc21c17c18d888919d0441807
|
@@ -14,7 +14,7 @@ module Bolt
|
|
14
14
|
global_config_setters: PROJECT_PATHS + %w[modulepath],
|
15
15
|
transports: %w[transport connect-timeout tty native-ssh ssh-command copy-command],
|
16
16
|
display: %w[format color verbose trace],
|
17
|
-
global: %w[help version debug log-level] }.freeze
|
17
|
+
global: %w[help version debug log-level clear-cache] }.freeze
|
18
18
|
|
19
19
|
ACTION_OPTS = OPTIONS.values.flatten.freeze
|
20
20
|
|
@@ -969,8 +969,8 @@ module Bolt
|
|
969
969
|
end
|
970
970
|
|
971
971
|
separator "\nPLAN OPTIONS"
|
972
|
-
define('--pp', 'Create a new Puppet language plan.') do |
|
973
|
-
@options[:puppet] =
|
972
|
+
define('--pp', 'Create a new Puppet language plan.') do |_|
|
973
|
+
@options[:puppet] = true
|
974
974
|
end
|
975
975
|
|
976
976
|
separator "\nDISPLAY OPTIONS"
|
@@ -1025,6 +1025,10 @@ module Bolt
|
|
1025
1025
|
"trace, debug, info, warn, error, fatal, any.") do |level|
|
1026
1026
|
@options[:log] = { 'console' => { 'level' => level } }
|
1027
1027
|
end
|
1028
|
+
define('--clear-cache',
|
1029
|
+
"Clear plugin cache before executing") do |_|
|
1030
|
+
@options[:clear_cache] = true
|
1031
|
+
end
|
1028
1032
|
define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
|
1029
1033
|
@options[:plugin] = plug
|
1030
1034
|
end
|
data/lib/bolt/cli.rb
CHANGED
@@ -197,7 +197,12 @@ module Bolt
|
|
197
197
|
@parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
198
198
|
config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
199
199
|
|
200
|
+
if options[:clear_cache] && File.exist?(config.project.cache_file)
|
201
|
+
FileUtils.rm(config.project.cache_file)
|
202
|
+
end
|
203
|
+
|
200
204
|
warn_inventory_overrides_cli(options)
|
205
|
+
validate_ps_version
|
201
206
|
|
202
207
|
options
|
203
208
|
rescue Bolt::Error => e
|
@@ -205,6 +210,17 @@ module Bolt
|
|
205
210
|
raise e
|
206
211
|
end
|
207
212
|
|
213
|
+
private def validate_ps_version
|
214
|
+
if Bolt::Util.powershell?
|
215
|
+
target = inventory.get_target('localhost')
|
216
|
+
Bolt::Transport::Local.new.with_connection(target) do |conn|
|
217
|
+
# This will automatically validate the powershell version on the Bolt
|
218
|
+
# controller
|
219
|
+
Bolt::Shell::Powershell.new(target, conn)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
208
224
|
def update_targets(options)
|
209
225
|
target_opts = options.keys.select { |opt| %i[query rerun targets].include?(opt) }
|
210
226
|
target_string = "'--targets', '--rerun', or '--query'"
|
data/lib/bolt/config.rb
CHANGED
@@ -216,8 +216,8 @@ module Bolt
|
|
216
216
|
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
217
217
|
logs = [{ debug: "Loaded configuration from #{filepath}" }]
|
218
218
|
deprecations = [{ type: 'Using bolt.yaml for system configuration',
|
219
|
-
msg: "Configuration file #{filepath} is deprecated and will be removed in
|
220
|
-
"
|
219
|
+
msg: "Configuration file #{filepath} is deprecated and will be removed in Bolt 3.0. "\
|
220
|
+
"See https://pup.pt/update-bolt-config for how to update to the latest Bolt practices." }]
|
221
221
|
|
222
222
|
# Validate the config against the schema. This will raise a single error
|
223
223
|
# with all validation errors.
|
@@ -448,10 +448,18 @@ module Bolt
|
|
448
448
|
raise Bolt::ValidationError,
|
449
449
|
"level of log #{name} must be a String or Symbol, received #{v.class} #{v.inspect}"
|
450
450
|
end
|
451
|
+
|
451
452
|
unless Bolt::Logger.valid_level?(v)
|
452
453
|
raise Bolt::ValidationError,
|
453
454
|
"level of log #{name} must be one of #{Bolt::Logger.levels.join(', ')}; received #{v}"
|
454
455
|
end
|
456
|
+
|
457
|
+
if v == 'notice'
|
458
|
+
@deprecations << {
|
459
|
+
type: 'notice log level',
|
460
|
+
msg: "Log level 'notice' is deprecated and will be removed in Bolt 3.0. Use 'info' instead."
|
461
|
+
}
|
462
|
+
end
|
455
463
|
end
|
456
464
|
|
457
465
|
if (v = acc[name][:append]) && v != true && v != false
|
@@ -521,6 +529,10 @@ module Bolt
|
|
521
529
|
@data['modulepath'] = value
|
522
530
|
end
|
523
531
|
|
532
|
+
def plugin_cache
|
533
|
+
@project.plugin_cache || @data['plugin-cache'] || {}
|
534
|
+
end
|
535
|
+
|
524
536
|
def concurrency
|
525
537
|
@data['concurrency']
|
526
538
|
end
|
data/lib/bolt/config/options.rb
CHANGED
@@ -33,6 +33,18 @@ module Bolt
|
|
33
33
|
"_plugin" => {
|
34
34
|
description: "The name of the plugin.",
|
35
35
|
type: "string"
|
36
|
+
},
|
37
|
+
"_cache" => {
|
38
|
+
description: "This feature is experimental. Enable plugin caching and set a time-to-live.",
|
39
|
+
type: "object",
|
40
|
+
required: ["ttl"],
|
41
|
+
properties: {
|
42
|
+
"ttl" => {
|
43
|
+
description: "Time in seconds to keep the plugin cache.",
|
44
|
+
type: "integer",
|
45
|
+
minimum: 0
|
46
|
+
}
|
47
|
+
}
|
36
48
|
}
|
37
49
|
}
|
38
50
|
}
|
@@ -190,6 +202,20 @@ module Bolt
|
|
190
202
|
_example: "~/.puppetlabs/bolt/inventory.yaml",
|
191
203
|
_default: "project/inventory.yaml"
|
192
204
|
},
|
205
|
+
"plugin-cache" => {
|
206
|
+
description: "This feature is experimental. Enable plugin caching and set the time-to-live.",
|
207
|
+
type: Hash,
|
208
|
+
required: ["ttl"],
|
209
|
+
properties: {
|
210
|
+
"ttl" => {
|
211
|
+
description: "Time in seconds to keep the plugin cache.",
|
212
|
+
type: Integer,
|
213
|
+
minimum: 0
|
214
|
+
}
|
215
|
+
},
|
216
|
+
_plugin: false,
|
217
|
+
_example: { "ttl" => 3600 }
|
218
|
+
},
|
193
219
|
"log" => {
|
194
220
|
description: "A map of configuration for the logfile output. Under `log`, you can configure log options "\
|
195
221
|
"for `console` and add configuration for individual log files, such as "\
|
@@ -207,7 +233,7 @@ module Bolt
|
|
207
233
|
"level" => {
|
208
234
|
description: "The type of information to log.",
|
209
235
|
type: String,
|
210
|
-
enum: %w[trace debug error info warn fatal any],
|
236
|
+
enum: %w[trace debug error info notice warn fatal any],
|
211
237
|
_default: "warn"
|
212
238
|
}
|
213
239
|
}
|
@@ -226,7 +252,7 @@ module Bolt
|
|
226
252
|
"level" => {
|
227
253
|
description: "The type of information to log.",
|
228
254
|
type: String,
|
229
|
-
enum: %w[trace debug error info warn fatal any],
|
255
|
+
enum: %w[trace debug error info notice warn fatal any],
|
230
256
|
_default: "warn"
|
231
257
|
}
|
232
258
|
}
|
@@ -540,6 +566,7 @@ module Bolt
|
|
540
566
|
format
|
541
567
|
inventory-config
|
542
568
|
log
|
569
|
+
plugin-cache
|
543
570
|
plugin-hooks
|
544
571
|
plugin_hooks
|
545
572
|
plugins
|
@@ -563,6 +590,7 @@ module Bolt
|
|
563
590
|
modules
|
564
591
|
name
|
565
592
|
plans
|
593
|
+
plugin-cache
|
566
594
|
plugin-hooks
|
567
595
|
plugin_hooks
|
568
596
|
plugins
|
data/lib/bolt/plugin.rb
CHANGED
@@ -4,6 +4,7 @@ require 'bolt/inventory'
|
|
4
4
|
require 'bolt/executor'
|
5
5
|
require 'bolt/module'
|
6
6
|
require 'bolt/pal'
|
7
|
+
require 'bolt/plugin/cache'
|
7
8
|
require 'bolt/plugin/puppetdb'
|
8
9
|
|
9
10
|
module Bolt
|
@@ -36,6 +37,13 @@ module Bolt
|
|
36
37
|
super("Plugin #{plugin_name} does not support #{hook}", 'bolt/unsupported-hook')
|
37
38
|
end
|
38
39
|
end
|
40
|
+
|
41
|
+
class LoadingDisabled < PluginError
|
42
|
+
def initialize(plugin_name)
|
43
|
+
msg = "Cannot load plugin #{plugin_name}: plugin loading is disabled"
|
44
|
+
super(msg, 'bolt/plugin-loading-disabled', { 'plugin_name' => plugin_name })
|
45
|
+
end
|
46
|
+
end
|
39
47
|
end
|
40
48
|
|
41
49
|
class PluginContext
|
@@ -119,8 +127,8 @@ module Bolt
|
|
119
127
|
end
|
120
128
|
end
|
121
129
|
|
122
|
-
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new)
|
123
|
-
plugins = new(config, pal, analytics)
|
130
|
+
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new, **opts)
|
131
|
+
plugins = new(config, pal, analytics, **opts)
|
124
132
|
|
125
133
|
config.plugins.each_key do |plugin|
|
126
134
|
plugins.by_name(plugin)
|
@@ -141,12 +149,13 @@ module Bolt
|
|
141
149
|
|
142
150
|
private_class_method :new
|
143
151
|
|
144
|
-
def initialize(config, pal, analytics)
|
152
|
+
def initialize(config, pal, analytics, load_plugins: true)
|
145
153
|
@config = config
|
146
154
|
@analytics = analytics
|
147
155
|
@plugin_context = PluginContext.new(config, pal, self)
|
148
156
|
@plugins = {}
|
149
157
|
@pal = pal
|
158
|
+
@load_plugins = load_plugins
|
150
159
|
@unknown = Set.new
|
151
160
|
@resolution_stack = []
|
152
161
|
@unresolved_plugin_configs = config.plugins.dup
|
@@ -169,6 +178,8 @@ module Bolt
|
|
169
178
|
end
|
170
179
|
|
171
180
|
def add_ruby_plugin(plugin_name)
|
181
|
+
raise PluginError::LoadingDisabled, plugin_name unless @load_plugins
|
182
|
+
|
172
183
|
cls_name = Bolt::Util.snake_name_to_class_name(plugin_name)
|
173
184
|
filename = "bolt/plugin/#{plugin_name}"
|
174
185
|
require filename
|
@@ -185,10 +196,17 @@ module Bolt
|
|
185
196
|
def add_module_plugin(plugin_name)
|
186
197
|
opts = {
|
187
198
|
context: @plugin_context,
|
199
|
+
# Make sure that the plugin's config is validated _before_ the unknown-plugin
|
200
|
+
# and loading-disabled checks. This way, we can fail early on invalid plugin
|
201
|
+
# config instead of _after_ loading the modulepath (which can be expensive).
|
188
202
|
config: config_for_plugin(plugin_name)
|
189
203
|
}
|
190
204
|
|
191
|
-
|
205
|
+
mod = modules[plugin_name]
|
206
|
+
raise PluginError::Unknown, plugin_name unless mod&.plugin?
|
207
|
+
raise PluginError::LoadingDisabled, plugin_name unless @load_plugins
|
208
|
+
|
209
|
+
plugin = Bolt::Plugin::Module.load(mod, opts)
|
192
210
|
add_plugin(plugin)
|
193
211
|
end
|
194
212
|
|
@@ -277,6 +295,16 @@ module Bolt
|
|
277
295
|
# Evaluates a single reference. The value returned may be another
|
278
296
|
# reference.
|
279
297
|
def resolve_single_reference(reference)
|
298
|
+
plugin_cache = if cache?(reference)
|
299
|
+
cache = Bolt::Plugin::Cache.new(reference,
|
300
|
+
@config.project.cache_file,
|
301
|
+
@config.plugin_cache)
|
302
|
+
entry = cache.read_and_clean_cache
|
303
|
+
return entry unless entry.nil?
|
304
|
+
|
305
|
+
cache
|
306
|
+
end
|
307
|
+
|
280
308
|
plugin_name = reference['_plugin']
|
281
309
|
hook = get_hook(plugin_name, :resolve_reference)
|
282
310
|
|
@@ -288,16 +316,24 @@ module Bolt
|
|
288
316
|
|
289
317
|
validate_proc.call(reference)
|
290
318
|
|
291
|
-
begin
|
319
|
+
result = begin
|
292
320
|
# Evaluate the plugin and then recursively evaluate any plugin returned by it.
|
293
321
|
hook.call(reference)
|
294
322
|
rescue StandardError => e
|
295
323
|
loc = "resolve_reference in #{plugin_name}"
|
296
324
|
raise PluginError::ExecutionError.new(e.message, plugin_name, loc)
|
297
325
|
end
|
326
|
+
|
327
|
+
plugin_cache.write_cache(result) if cache?(reference)
|
328
|
+
|
329
|
+
result
|
298
330
|
end
|
299
331
|
private :resolve_single_reference
|
300
332
|
|
333
|
+
private def cache?(reference)
|
334
|
+
reference.key?('_cache') || @config.plugin_cache.key?('ttl')
|
335
|
+
end
|
336
|
+
|
301
337
|
# Checks whether a given value is a _plugin reference
|
302
338
|
def reference?(input)
|
303
339
|
input.is_a?(Hash) && input.key?('_plugin')
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'bolt/error'
|
5
|
+
require 'bolt/util'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class Plugin
|
9
|
+
class Cache
|
10
|
+
attr_reader :reference, :cache_file, :default_config, :id
|
11
|
+
|
12
|
+
def initialize(reference, cache_file, default_config)
|
13
|
+
@reference = reference
|
14
|
+
@cache_file = cache_file
|
15
|
+
@default_config = default_config
|
16
|
+
end
|
17
|
+
|
18
|
+
def read_and_clean_cache
|
19
|
+
return if ttl == 0
|
20
|
+
validate
|
21
|
+
|
22
|
+
# Luckily we don't need to use a serious hash algorithm
|
23
|
+
require 'digest/bubblebabble'
|
24
|
+
r = reference.select { |k, _| k == '_cache' }.sort.to_s
|
25
|
+
@id = Digest::SHA2.bubblebabble(r)[0..20]
|
26
|
+
|
27
|
+
unmodified = true
|
28
|
+
# First remove any cache entries past their ttl
|
29
|
+
# This prevents removing plugins from leaving orphaned cache entries
|
30
|
+
cache.delete_if do |_, entry|
|
31
|
+
expired = Time.now - Time.parse(entry['mtime']) >= entry['ttl']
|
32
|
+
unmodified = false if expired
|
33
|
+
expired
|
34
|
+
end
|
35
|
+
File.write(cache_file, cache.to_json) unless cache.empty? || unmodified
|
36
|
+
|
37
|
+
cache.dig(id, 'result')
|
38
|
+
end
|
39
|
+
|
40
|
+
private def cache
|
41
|
+
@cache ||= Bolt::Util.read_optional_json_file(@cache_file, 'cache')
|
42
|
+
end
|
43
|
+
|
44
|
+
def write_cache(result)
|
45
|
+
cache.merge!({ id => { 'result' => result,
|
46
|
+
'mtime' => Time.now,
|
47
|
+
'ttl' => ttl } })
|
48
|
+
FileUtils.touch(cache_file)
|
49
|
+
File.write(cache_file, cache.to_json)
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate
|
53
|
+
# The default cache `plugin-cache` will be validated by the config
|
54
|
+
# validator
|
55
|
+
return if reference['_cache'].nil?
|
56
|
+
r = reference['_cache']
|
57
|
+
unless r.is_a?(Hash)
|
58
|
+
raise Bolt::ValidationError,
|
59
|
+
"_cache must be a Hash, received #{r.class}: #{r.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
unless r.key?('ttl')
|
63
|
+
raise Bolt::ValidationError, "_cache must set 'ttl' key."
|
64
|
+
end
|
65
|
+
|
66
|
+
unless r['ttl'] >= 0
|
67
|
+
raise Bolt::ValidationError, "'ttl' key under '_cache' must be a minimum of 0."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private def ttl
|
72
|
+
@ttl ||= reference.dig('_cache', 'ttl') || default_config['ttl']
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/bolt/plugin/module.rb
CHANGED
@@ -12,15 +12,15 @@ module Bolt
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
if mod
|
15
|
+
# mod should not be nil
|
16
|
+
def self.load(mod, opts)
|
17
|
+
if mod.plugin?
|
18
18
|
opts[:mod] = mod
|
19
19
|
plugin = Bolt::Plugin::Module.new(**opts)
|
20
20
|
plugin.setup
|
21
21
|
plugin
|
22
22
|
else
|
23
|
-
raise PluginError::Unknown, name
|
23
|
+
raise PluginError::Unknown, mod.name
|
24
24
|
end
|
25
25
|
end
|
26
26
|
|
data/lib/bolt/project.rb
CHANGED
@@ -14,7 +14,7 @@ module Bolt
|
|
14
14
|
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
15
15
|
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
16
16
|
:deprecations, :downloads, :plans_path, :modulepath, :managed_moduledir,
|
17
|
-
:backup_dir
|
17
|
+
:backup_dir, :cache_file
|
18
18
|
|
19
19
|
def self.default_project(logs = [])
|
20
20
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
|
@@ -27,23 +27,31 @@ module Bolt
|
|
27
27
|
# directory called Boltdir or a file called bolt.yaml (for a control repo
|
28
28
|
# type Project). Otherwise, repeat the check on each directory up the
|
29
29
|
# hierarchy, falling back to the default if we reach the root.
|
30
|
-
def self.find_boltdir(dir, logs = [])
|
30
|
+
def self.find_boltdir(dir, logs = [], deprecations = [])
|
31
31
|
dir = Pathname.new(dir)
|
32
32
|
|
33
33
|
if (dir + BOLTDIR_NAME).directory?
|
34
34
|
create_project(dir + BOLTDIR_NAME, 'embedded', logs)
|
35
|
-
elsif (dir + 'bolt.yaml').file?
|
35
|
+
elsif (dir + 'bolt.yaml').file?
|
36
|
+
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
37
|
+
msg = "Configuration file #{dir + 'bolt.yaml'} is deprecated and will be "\
|
38
|
+
"removed in Bolt 3.0.\nUpdate your Bolt project to the latest Bolt practices "\
|
39
|
+
"using #{command}"
|
40
|
+
deprecations << { type: "Project level bolt.yaml",
|
41
|
+
msg: msg }
|
42
|
+
create_project(dir, 'local', logs, deprecations)
|
43
|
+
elsif (dir + CONFIG_NAME).file?
|
36
44
|
create_project(dir, 'local', logs)
|
37
45
|
elsif dir.root?
|
38
46
|
default_project(logs)
|
39
47
|
else
|
40
48
|
logs << { debug: "Did not detect Boltdir, bolt.yaml, or bolt-project.yaml at '#{dir}'. "\
|
41
49
|
"This directory won't be loaded as a project." }
|
42
|
-
find_boltdir(dir.parent, logs)
|
50
|
+
find_boltdir(dir.parent, logs, deprecations)
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
|
-
def self.create_project(path, type = 'option', logs = [])
|
54
|
+
def self.create_project(path, type = 'option', logs = [], deprecations = [])
|
47
55
|
fullpath = Pathname.new(path).expand_path
|
48
56
|
|
49
57
|
if type == 'user'
|
@@ -72,7 +80,6 @@ module Bolt
|
|
72
80
|
data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
|
73
81
|
default = type =~ /user|system/ ? 'default ' : ''
|
74
82
|
exist = File.exist?(File.expand_path(project_file))
|
75
|
-
deprecations = []
|
76
83
|
|
77
84
|
logs << { info: "Loaded #{default}project from '#{fullpath}'" } if exist
|
78
85
|
|
@@ -116,6 +123,7 @@ module Bolt
|
|
116
123
|
@plans_path = @path + 'plans'
|
117
124
|
@managed_moduledir = @path + '.modules'
|
118
125
|
@backup_dir = @path + '.bolt-bak'
|
126
|
+
@cache_file = @path + '.plugin_cache.json'
|
119
127
|
|
120
128
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
121
129
|
if tc.any?
|
@@ -184,6 +192,10 @@ module Bolt
|
|
184
192
|
@data['plans']
|
185
193
|
end
|
186
194
|
|
195
|
+
def plugin_cache
|
196
|
+
@data['plugin-cache']
|
197
|
+
end
|
198
|
+
|
187
199
|
def modules
|
188
200
|
@modules ||= @data['modules']&.map do |mod|
|
189
201
|
if mod.is_a?(String)
|
data/lib/bolt/rerun.rb
CHANGED
@@ -12,15 +12,11 @@ module Bolt
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def data
|
15
|
-
@data ||=
|
15
|
+
@data ||= Bolt::Util.read_json_file(@path, 'rerun')
|
16
16
|
unless @data.is_a?(Array) && @data.all? { |r| r['target'] && r['status'] }
|
17
17
|
raise Bolt::FileError.new("Missing data in rerun file: #{@path}", @path)
|
18
18
|
end
|
19
19
|
@data
|
20
|
-
rescue JSON::ParserError
|
21
|
-
raise Bolt::FileError.new("Could not parse rerun file: #{@path}", @path)
|
22
|
-
rescue IOError, SystemCallError
|
23
|
-
raise Bolt::FileError.new("Could not read rerun file: #{@path}", @path)
|
24
20
|
end
|
25
21
|
|
26
22
|
def get_targets(filter)
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -436,8 +436,14 @@ module Bolt
|
|
436
436
|
result_output.stderr << read_streams[err]
|
437
437
|
result_output.exit_code = t.value.respond_to?(:exitstatus) ? t.value.exitstatus : t.value
|
438
438
|
|
439
|
-
|
439
|
+
case result_output.exit_code
|
440
|
+
when 0
|
440
441
|
@logger.trace { "Command `#{command_str}` returned successfully" }
|
442
|
+
when 126
|
443
|
+
msg = "\n\nThis may be caused by the default tmpdir being mounted "\
|
444
|
+
"using 'noexec'. See http://pup.pt/task-failure for details and workarounds."
|
445
|
+
result_output.stderr << msg
|
446
|
+
@logger.trace { "Command #{command_str} failed with exit code #{result_output.exit_code}" }
|
441
447
|
else
|
442
448
|
@logger.trace { "Command #{command_str} failed with exit code #{result_output.exit_code}" }
|
443
449
|
end
|
@@ -14,6 +14,22 @@ module Bolt
|
|
14
14
|
extensions = [target.options['extensions'] || []].flatten.map { |ext| ext[0] == '.' ? ext : '.' + ext }
|
15
15
|
extensions += target.options['interpreters'].keys if target.options['interpreters']
|
16
16
|
@extensions = DEFAULT_EXTENSIONS + extensions
|
17
|
+
validate_ps_version
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate_ps_version
|
21
|
+
version = execute("$PSVersionTable.PSVersion.Major").stdout.string.chomp
|
22
|
+
if !version.empty? && version.to_i < 3
|
23
|
+
# This lets us know how many targets have Powershell 2, and lets the
|
24
|
+
# user know how many targets they have with PS2
|
25
|
+
msg = "Detected PowerShell 2 on one or more targets.\nPowerShell 2 "\
|
26
|
+
"is deprecated, and support will be removed in Bolt 3.0. See "\
|
27
|
+
"bolt-debug.log or run with '--log-level debug' to see the full "\
|
28
|
+
"list of targets with PowerShell 2."
|
29
|
+
|
30
|
+
Bolt::Logger.deprecation_warning("PowerShell 2", msg)
|
31
|
+
@logger.debug("Detected PowerShell 2 on #{target}.")
|
32
|
+
end
|
17
33
|
end
|
18
34
|
|
19
35
|
def provided_features
|
data/lib/bolt/util.rb
CHANGED
@@ -22,6 +22,28 @@ module Bolt
|
|
22
22
|
raise Bolt::FileError.new("Error attempting to read #{file}: #{e}", file)
|
23
23
|
end
|
24
24
|
|
25
|
+
def read_json_file(path, filename)
|
26
|
+
require 'json'
|
27
|
+
|
28
|
+
logger = Bolt::Logger.logger(self)
|
29
|
+
path = File.expand_path(path)
|
30
|
+
content = JSON.parse(File.read(path))
|
31
|
+
logger.trace("Loaded #{filename} from #{path}")
|
32
|
+
content
|
33
|
+
rescue Errno::ENOENT
|
34
|
+
raise Bolt::FileError.new("Could not read #{filename} file at #{path}", path)
|
35
|
+
rescue JSON::ParserError => e
|
36
|
+
msg = "Unable to parse #{filename} file at #{path} as JSON: #{e.message}"
|
37
|
+
raise Bolt::FileError.new(msg, path)
|
38
|
+
rescue IOError, SystemCallError => e
|
39
|
+
raise Bolt::FileError.new("Could not read #{filename} file at #{path}\n#{e.message}",
|
40
|
+
path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def read_optional_json_file(path, file_name)
|
44
|
+
File.exist?(path) && !File.zero?(path) ? read_yaml_hash(path, file_name) : {}
|
45
|
+
end
|
46
|
+
|
25
47
|
def read_yaml_hash(path, file_name)
|
26
48
|
require 'yaml'
|
27
49
|
|
data/lib/bolt/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
module BoltServer
|
6
|
+
class Plugin
|
7
|
+
class PluginNotSupported < Bolt::Error
|
8
|
+
def initialize(msg, plugin_name)
|
9
|
+
super(msg, 'bolt/plugin-not-supported', { "plugin_name" => plugin_name })
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BoltServer
|
4
|
+
class Plugin
|
5
|
+
class PuppetConnectData
|
6
|
+
def initialize(data, **_opts)
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
'puppet_connect_data'
|
12
|
+
end
|
13
|
+
|
14
|
+
def hooks
|
15
|
+
%i[resolve_reference validate_resolve_reference]
|
16
|
+
end
|
17
|
+
|
18
|
+
def resolve_reference(opts)
|
19
|
+
key = opts['key']
|
20
|
+
|
21
|
+
@data.dig(key, 'value')
|
22
|
+
end
|
23
|
+
|
24
|
+
def validate_resolve_reference(opts)
|
25
|
+
unless opts['key']
|
26
|
+
raise Bolt::ValidationError,
|
27
|
+
"puppet_connect_data plugin requires that 'key' be specified"
|
28
|
+
end
|
29
|
+
|
30
|
+
unless @data.key?(opts['key'])
|
31
|
+
raise Bolt::ValidationError,
|
32
|
+
"puppet_connect_data plugin tried to lookup key '#{opts['key']}' but no value was found"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "project_inventory_targets connect plugin data",
|
4
|
+
"description": "POST project_inventory_targets connect plugin data",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"puppet_connect_data": {
|
8
|
+
"type": "object",
|
9
|
+
"patternProperties": {
|
10
|
+
".*": {
|
11
|
+
"type": "object",
|
12
|
+
"properties": {
|
13
|
+
"value": {}
|
14
|
+
},
|
15
|
+
"required": ["value"]
|
16
|
+
}
|
17
|
+
},
|
18
|
+
"additionalProperties": false
|
19
|
+
}
|
20
|
+
},
|
21
|
+
"required": ["puppet_connect_data"]
|
22
|
+
}
|
@@ -8,6 +8,8 @@ require 'bolt/inventory'
|
|
8
8
|
require 'bolt/project'
|
9
9
|
require 'bolt/target'
|
10
10
|
require 'bolt_server/file_cache'
|
11
|
+
require 'bolt_server/plugin'
|
12
|
+
require 'bolt_server/plugin/puppet_connect_data'
|
11
13
|
require 'bolt/task/puppet_server'
|
12
14
|
require 'json'
|
13
15
|
require 'json-schema'
|
@@ -35,6 +37,7 @@ module BoltServer
|
|
35
37
|
action-upload_file
|
36
38
|
transport-ssh
|
37
39
|
transport-winrm
|
40
|
+
connect-data
|
38
41
|
].freeze
|
39
42
|
|
40
43
|
# PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
|
@@ -285,8 +288,6 @@ module BoltServer
|
|
285
288
|
config: bolt_config
|
286
289
|
}
|
287
290
|
yield context
|
288
|
-
rescue Bolt::Error => e
|
289
|
-
[400, e.to_json]
|
290
291
|
end
|
291
292
|
end
|
292
293
|
|
@@ -521,6 +522,8 @@ module BoltServer
|
|
521
522
|
plan_info = allowed_helper(plan_info, context[:config].project.plans)
|
522
523
|
[200, plan_info.to_json]
|
523
524
|
end
|
525
|
+
rescue Bolt::Error => e
|
526
|
+
[400, e.to_json]
|
524
527
|
end
|
525
528
|
|
526
529
|
# Fetches the metadata for a single task
|
@@ -549,6 +552,8 @@ module BoltServer
|
|
549
552
|
task_info = allowed_helper(task_info, context[:config].project.tasks)
|
550
553
|
[200, task_info.to_json]
|
551
554
|
end
|
555
|
+
rescue Bolt::Error => e
|
556
|
+
[400, e.to_json]
|
552
557
|
end
|
553
558
|
|
554
559
|
# Fetches the list of plans for an environment, optionally fetching all metadata for each plan
|
@@ -592,6 +597,8 @@ module BoltServer
|
|
592
597
|
# to bolt-server smaller/simpler.
|
593
598
|
[200, plans_response.to_json]
|
594
599
|
end
|
600
|
+
rescue Bolt::Error => e
|
601
|
+
[400, e.to_json]
|
595
602
|
end
|
596
603
|
|
597
604
|
# Fetches the list of tasks for an environment
|
@@ -626,6 +633,8 @@ module BoltServer
|
|
626
633
|
# to bolt-server smaller/simpler.
|
627
634
|
[200, tasks_response.to_json]
|
628
635
|
end
|
636
|
+
rescue Bolt::Error => e
|
637
|
+
[400, e.to_json]
|
629
638
|
end
|
630
639
|
|
631
640
|
# Implements puppetserver's file_metadatas endpoint for projects.
|
@@ -638,6 +647,8 @@ module BoltServer
|
|
638
647
|
metadatas = file_metadatas(context[:pal], params[:module_name], file)
|
639
648
|
[200, metadatas.to_json]
|
640
649
|
end
|
650
|
+
rescue Bolt::Error => e
|
651
|
+
[400, e.to_json]
|
641
652
|
rescue ArgumentError => e
|
642
653
|
[400, e.message]
|
643
654
|
end
|
@@ -647,16 +658,33 @@ module BoltServer
|
|
647
658
|
# @param project_ref [String] the project_ref to compute the inventory from
|
648
659
|
post '/project_inventory_targets' do
|
649
660
|
return MISSING_PROJECT_REF_RESPONSE if params['project_ref'].nil?
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
661
|
+
content_type :json
|
662
|
+
body = JSON.parse(request.body.read)
|
663
|
+
error = validate_schema(@schemas["connect-data"], body)
|
664
|
+
return [400, error_result(error).to_json] unless error.nil?
|
665
|
+
in_bolt_project(params['project_ref']) do |context|
|
666
|
+
if context[:config].inventoryfile &&
|
667
|
+
context[:config].project.inventory_file.to_s !=
|
668
|
+
context[:config].inventoryfile
|
669
|
+
raise Bolt::ValidationError, "Project inventory must be defined in the " \
|
670
|
+
"inventory.yaml file at the root of the project directory"
|
671
|
+
end
|
672
|
+
|
673
|
+
Bolt::Util.validate_file('inventory file', context[:config].project.inventory_file)
|
674
|
+
|
675
|
+
begin
|
676
|
+
connect_plugin = BoltServer::Plugin::PuppetConnectData.new(body['puppet_connect_data'])
|
677
|
+
plugins = Bolt::Plugin.setup(context[:config], context[:pal], load_plugins: false)
|
678
|
+
plugins.add_plugin(connect_plugin)
|
679
|
+
inventory = Bolt::Inventory.from_config(context[:config], plugins)
|
680
|
+
target_list = inventory.get_targets('all').map { |targ| targ.to_h.merge({ 'transport' => targ.transport }) }
|
681
|
+
rescue Bolt::Plugin::PluginError::LoadingDisabled => e
|
682
|
+
msg = "Cannot load plugin #{e.details['plugin_name']}: plugin not supported"
|
683
|
+
raise BoltServer::Plugin::PluginNotSupported.new(msg, e.details['plugin_name'])
|
684
|
+
end
|
658
685
|
|
659
|
-
|
686
|
+
[200, target_list.to_json]
|
687
|
+
end
|
660
688
|
rescue Bolt::Error => e
|
661
689
|
[500, e.to_json]
|
662
690
|
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: 2.
|
4
|
+
version: 2.37.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -505,6 +505,7 @@ files:
|
|
505
505
|
- lib/bolt/plan_creator.rb
|
506
506
|
- lib/bolt/plan_result.rb
|
507
507
|
- lib/bolt/plugin.rb
|
508
|
+
- lib/bolt/plugin/cache.rb
|
508
509
|
- lib/bolt/plugin/env_var.rb
|
509
510
|
- lib/bolt/plugin/module.rb
|
510
511
|
- lib/bolt/plugin/prompt.rb
|
@@ -556,11 +557,14 @@ files:
|
|
556
557
|
- lib/bolt_server/base_config.rb
|
557
558
|
- lib/bolt_server/config.rb
|
558
559
|
- lib/bolt_server/file_cache.rb
|
560
|
+
- lib/bolt_server/plugin.rb
|
561
|
+
- lib/bolt_server/plugin/puppet_connect_data.rb
|
559
562
|
- lib/bolt_server/schemas/action-check_node_connections.json
|
560
563
|
- lib/bolt_server/schemas/action-run_command.json
|
561
564
|
- lib/bolt_server/schemas/action-run_script.json
|
562
565
|
- lib/bolt_server/schemas/action-run_task.json
|
563
566
|
- lib/bolt_server/schemas/action-upload_file.json
|
567
|
+
- lib/bolt_server/schemas/connect-data.json
|
564
568
|
- lib/bolt_server/schemas/partials/target-any.json
|
565
569
|
- lib/bolt_server/schemas/partials/target-ssh.json
|
566
570
|
- lib/bolt_server/schemas/partials/target-winrm.json
|