bolt 2.34.0 → 2.40.1
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/Puppetfile +1 -1
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -3
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +17 -6
- data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +56 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +24 -6
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +27 -8
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +21 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +18 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +24 -6
- data/lib/bolt/analytics.rb +27 -8
- data/lib/bolt/apply_result.rb +3 -3
- data/lib/bolt/bolt_option_parser.rb +45 -18
- data/lib/bolt/cli.rb +98 -116
- data/lib/bolt/config.rb +184 -80
- data/lib/bolt/config/options.rb +148 -87
- data/lib/bolt/config/transport/base.rb +10 -19
- data/lib/bolt/config/transport/local.rb +1 -7
- data/lib/bolt/config/transport/options.rb +12 -69
- data/lib/bolt/config/transport/ssh.rb +8 -19
- data/lib/bolt/error.rb +24 -0
- data/lib/bolt/executor.rb +92 -18
- data/lib/bolt/inventory.rb +25 -0
- data/lib/bolt/inventory/group.rb +0 -8
- data/lib/bolt/inventory/options.rb +130 -0
- data/lib/bolt/inventory/target.rb +10 -11
- data/lib/bolt/module_installer.rb +21 -13
- data/lib/bolt/module_installer/resolver.rb +1 -1
- data/lib/bolt/outputter.rb +19 -5
- data/lib/bolt/outputter/human.rb +22 -3
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +13 -2
- data/lib/bolt/pal.rb +18 -6
- data/lib/bolt/pal/yaml_plan.rb +7 -0
- data/lib/bolt/plugin.rb +41 -12
- data/lib/bolt/plugin/cache.rb +76 -0
- data/lib/bolt/plugin/module.rb +4 -4
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +59 -40
- data/lib/bolt/project_manager.rb +201 -0
- data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +49 -4
- data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +3 -3
- data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
- data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +5 -3
- data/lib/bolt/puppetdb/client.rb +11 -2
- data/lib/bolt/puppetdb/config.rb +4 -3
- data/lib/bolt/rerun.rb +1 -5
- data/lib/bolt/shell/bash.rb +8 -2
- data/lib/bolt/shell/powershell.rb +21 -3
- data/lib/bolt/target.rb +4 -0
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/local.rb +13 -0
- data/lib/bolt/transport/orch.rb +0 -5
- data/lib/bolt/transport/orch/connection.rb +10 -3
- data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
- data/lib/bolt/util.rb +36 -7
- data/lib/bolt/validator.rb +227 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt/yarn.rb +23 -0
- data/lib/bolt_server/base_config.rb +3 -1
- data/lib/bolt_server/config.rb +3 -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/schemas/partials/task.json +2 -2
- data/lib/bolt_server/transport_app.rb +82 -23
- data/lib/bolt_spec/plans/mock_executor.rb +4 -1
- data/libexec/apply_catalog.rb +1 -1
- data/libexec/custom_facts.rb +1 -1
- data/libexec/query_resources.rb +1 -1
- metadata +22 -14
- data/lib/bolt/project_migrator.rb +0 -80
@@ -8,6 +8,7 @@ module Bolt
|
|
8
8
|
module Transport
|
9
9
|
class Local < Base
|
10
10
|
WINDOWS_OPTIONS = %w[
|
11
|
+
bundled-ruby
|
11
12
|
cleanup
|
12
13
|
interpreters
|
13
14
|
tmpdir
|
@@ -29,13 +30,6 @@ module Bolt
|
|
29
30
|
if @config['interpreters']
|
30
31
|
@config['interpreters'] = normalize_interpreters(@config['interpreters'])
|
31
32
|
end
|
32
|
-
|
33
|
-
if (run_as_cmd = @config['run-as-command'])
|
34
|
-
unless run_as_cmd.all? { |n| n.is_a?(String) }
|
35
|
-
raise Bolt::ValidationError,
|
36
|
-
"run-as-command must be an Array of Strings, received #{run_as_cmd.class} #{run_as_cmd.inspect}"
|
37
|
-
end
|
38
|
-
end
|
39
33
|
end
|
40
34
|
end
|
41
35
|
end
|
@@ -6,73 +6,8 @@ module Bolt
|
|
6
6
|
module Options
|
7
7
|
LOGIN_SHELLS = %w[sh bash zsh dash ksh powershell].freeze
|
8
8
|
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# are the option's definition. These options are used in multiple locations:
|
12
|
-
#
|
13
|
-
# - Automatic type validation when loading and setting configuration
|
14
|
-
# - Generating reference documentation for configuration files
|
15
|
-
# - Generating JSON schemas for configuration files
|
16
|
-
#
|
17
|
-
# Data includes keys defined by JSON Schema Draft 07 as well as some metadata used
|
18
|
-
# by Bolt to generate documentation. The following keys are used:
|
19
|
-
#
|
20
|
-
# :description String A detailed description of the option and what it does. This
|
21
|
-
# field is used in both documentation and the JSON schemas,
|
22
|
-
# and should provide as much detail as possible, including
|
23
|
-
# links to relevant documentation.
|
24
|
-
#
|
25
|
-
# :type Class The expected type of a value. These should be Ruby classes,
|
26
|
-
# as this field is used to perform automatic type validation.
|
27
|
-
# If an option can accept more than one type, this should be
|
28
|
-
# an array of types. Boolean values should set :type to
|
29
|
-
# [TrueClass, FalseClass], as Ruby does not have a single
|
30
|
-
# Boolean class.
|
31
|
-
#
|
32
|
-
# :items Hash A definition hash for items in an array. Similar to values
|
33
|
-
# for top-level options, items can have a :description, :type,
|
34
|
-
# or any other key in this list.
|
35
|
-
#
|
36
|
-
# :uniqueItems Boolean Whether or not an array should contain only unique items.
|
37
|
-
#
|
38
|
-
# :properties Hash A hash where keys are sub-options and values are definitions
|
39
|
-
# for the sub-option. Similar to values for top-level options,
|
40
|
-
# properties can have a :description, :type, or any other key
|
41
|
-
# in this list.
|
42
|
-
#
|
43
|
-
# :additionalProperties A variation of the :properties key, where the hash is a
|
44
|
-
# Hash definition for any properties not specified in :properties.
|
45
|
-
# This can be used to permit arbitrary sub-options, such as
|
46
|
-
# logs for the 'log' option.
|
47
|
-
#
|
48
|
-
# :propertyNames Hash A hash that defines the properties that an option's property
|
49
|
-
# names must adhere to.
|
50
|
-
#
|
51
|
-
# :required Array An array of properties that are required for options that
|
52
|
-
# accept Hash values.
|
53
|
-
#
|
54
|
-
# :minimum Integer The minimum integer value for an option.
|
55
|
-
#
|
56
|
-
# :enum Array An array of values that the option recognizes.
|
57
|
-
#
|
58
|
-
# :pattern String A JSON regex pattern that the option's vaue should match.
|
59
|
-
#
|
60
|
-
# :format String Requires that a string value matches a format defined by the
|
61
|
-
# JSON Schema draft.
|
62
|
-
#
|
63
|
-
# :_plugin Boolean Whether the option accepts a plugin reference. This is used
|
64
|
-
# when generating the JSON schemas to determine whether or not
|
65
|
-
# to include a reference to the _plugin definition. If :_plugin
|
66
|
-
# is set to true, the script that generates JSON schemas will
|
67
|
-
# automatically recurse through the :items and :properties keys
|
68
|
-
# and add plugin references if applicable.
|
69
|
-
#
|
70
|
-
# :_example Any An example value for the option. This is used to generate
|
71
|
-
# reference documentation for configuration files.
|
72
|
-
#
|
73
|
-
# :_default Any The documented default value for the option. This is only
|
74
|
-
# used to generate reference documentation for configuration
|
75
|
-
# files and is not used by Bolt to actually set default values.
|
9
|
+
# Definitions used to validate config options.
|
10
|
+
# https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
|
76
11
|
TRANSPORT_OPTIONS = {
|
77
12
|
"basic-auth-only" => {
|
78
13
|
type: [TrueClass, FalseClass],
|
@@ -81,6 +16,13 @@ module Bolt
|
|
81
16
|
_default: false,
|
82
17
|
_example: true
|
83
18
|
},
|
19
|
+
"bundled-ruby" => {
|
20
|
+
description: "Whether to use the Ruby bundled with Bolt packages for local targets.",
|
21
|
+
type: [TrueClass, FalseClass],
|
22
|
+
_plugin: false,
|
23
|
+
_example: true,
|
24
|
+
_default: false
|
25
|
+
},
|
84
26
|
"cacert" => {
|
85
27
|
type: String,
|
86
28
|
description: "The path to the CA certificate.",
|
@@ -201,7 +143,8 @@ module Bolt
|
|
201
143
|
"`task.py`) and the extension is case sensitive. When a target's name is `localhost`, "\
|
202
144
|
"Ruby tasks run with the Bolt Ruby interpreter by default.",
|
203
145
|
additionalProperties: {
|
204
|
-
type: String
|
146
|
+
type: String,
|
147
|
+
_plugin: false
|
205
148
|
},
|
206
149
|
propertyNames: {
|
207
150
|
pattern: "^.?[a-zA-Z0-9]+$"
|
@@ -357,7 +300,7 @@ module Bolt
|
|
357
300
|
description: "The URL of the host used for API requests.",
|
358
301
|
format: "uri",
|
359
302
|
_plugin: true,
|
360
|
-
_example: "https://api.example.com
|
303
|
+
_example: "https://api.example.com:8143"
|
361
304
|
},
|
362
305
|
"shell-command" => {
|
363
306
|
type: String,
|
@@ -73,6 +73,14 @@ module Bolt
|
|
73
73
|
%w[ssh-command native-ssh].concat(OPTIONS)
|
74
74
|
end
|
75
75
|
|
76
|
+
def self.schema
|
77
|
+
{
|
78
|
+
type: Hash,
|
79
|
+
properties: self::TRANSPORT_OPTIONS.slice(*(self::OPTIONS + self::NATIVE_OPTIONS)),
|
80
|
+
_plugin: true
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
76
84
|
private def filter(unfiltered)
|
77
85
|
# Because we filter before merging config together it's impossible to
|
78
86
|
# know whether both ssh-command *and* native-ssh will be specified
|
@@ -87,12 +95,6 @@ module Bolt
|
|
87
95
|
super
|
88
96
|
|
89
97
|
if (key_opt = @config['private-key'])
|
90
|
-
unless key_opt.instance_of?(String) || (key_opt.instance_of?(Hash) && key_opt.include?('key-data'))
|
91
|
-
raise Bolt::ValidationError,
|
92
|
-
"private-key option must be a path to a private key file or a Hash containing the 'key-data', "\
|
93
|
-
"received #{key_opt.class} #{key_opt}"
|
94
|
-
end
|
95
|
-
|
96
98
|
if key_opt.instance_of?(String)
|
97
99
|
@config['private-key'] = File.expand_path(key_opt, @project)
|
98
100
|
|
@@ -109,19 +111,6 @@ module Bolt
|
|
109
111
|
@config['interpreters'] = normalize_interpreters(@config['interpreters'])
|
110
112
|
end
|
111
113
|
|
112
|
-
if @config['login-shell'] && !LOGIN_SHELLS.include?(@config['login-shell'])
|
113
|
-
raise Bolt::ValidationError,
|
114
|
-
"Unsupported login-shell #{@config['login-shell']}. Supported shells are #{LOGIN_SHELLS.join(', ')}"
|
115
|
-
end
|
116
|
-
|
117
|
-
%w[encryption-algorithms host-key-algorithms kex-algorithms mac-algorithms run-as-command].each do |opt|
|
118
|
-
next unless @config.key?(opt)
|
119
|
-
unless @config[opt].all? { |n| n.is_a?(String) }
|
120
|
-
raise Bolt::ValidationError,
|
121
|
-
"#{opt} must be an Array of Strings, received #{@config[opt].inspect}"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
114
|
if @config['login-shell'] == 'powershell'
|
126
115
|
%w[tty run-as].each do |key|
|
127
116
|
if @config[key]
|
data/lib/bolt/error.rb
CHANGED
@@ -90,6 +90,20 @@ module Bolt
|
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
93
|
+
class ParallelFailure < Bolt::Error
|
94
|
+
def initialize(results, failed_indices)
|
95
|
+
details = {
|
96
|
+
'action' => 'parallelize',
|
97
|
+
'failed_indices' => failed_indices,
|
98
|
+
'results' => results
|
99
|
+
}
|
100
|
+
message = "Plan aborted: parallel block failed on #{failed_indices.length} target"
|
101
|
+
message += "s" unless failed_indices.length == 1
|
102
|
+
super(message, 'bolt/parallel-failure', details)
|
103
|
+
@error_code = 2
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
93
107
|
class PlanFailure < Error
|
94
108
|
def initialize(*args)
|
95
109
|
super(*args)
|
@@ -131,6 +145,16 @@ module Bolt
|
|
131
145
|
end
|
132
146
|
end
|
133
147
|
|
148
|
+
class InvalidParallelResult < Error
|
149
|
+
def initialize(result_str, file, line)
|
150
|
+
super("Parallel block returned an invalid result: #{result_str}",
|
151
|
+
'bolt/invalid-plan-result',
|
152
|
+
{ 'file' => file,
|
153
|
+
'line' => line,
|
154
|
+
'result_string' => result_str })
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
134
158
|
class ValidationError < Bolt::Error
|
135
159
|
def initialize(msg)
|
136
160
|
super(msg, 'bolt/validation-error')
|
data/lib/bolt/executor.rb
CHANGED
@@ -17,6 +17,7 @@ require 'bolt/transport/orch'
|
|
17
17
|
require 'bolt/transport/local'
|
18
18
|
require 'bolt/transport/docker'
|
19
19
|
require 'bolt/transport/remote'
|
20
|
+
require 'bolt/yarn'
|
20
21
|
|
21
22
|
module Bolt
|
22
23
|
TRANSPORTS = {
|
@@ -29,7 +30,7 @@ module Bolt
|
|
29
30
|
}.freeze
|
30
31
|
|
31
32
|
class Executor
|
32
|
-
attr_reader :noop, :transports
|
33
|
+
attr_reader :noop, :transports, :in_parallel
|
33
34
|
attr_accessor :run_as
|
34
35
|
|
35
36
|
def initialize(concurrency = 1,
|
@@ -60,6 +61,7 @@ module Bolt
|
|
60
61
|
|
61
62
|
@noop = noop
|
62
63
|
@run_as = nil
|
64
|
+
@in_parallel = false
|
63
65
|
@pool = if concurrency > 0
|
64
66
|
Concurrent::ThreadPoolExecutor.new(name: 'exec', max_threads: concurrency)
|
65
67
|
else
|
@@ -84,6 +86,14 @@ module Bolt
|
|
84
86
|
self
|
85
87
|
end
|
86
88
|
|
89
|
+
def unsubscribe(subscriber, types = nil)
|
90
|
+
if types.nil? || types.sort == @subscribers[subscriber]&.sort
|
91
|
+
@subscribers.delete(subscriber)
|
92
|
+
elsif @subscribers[subscriber].is_a?(Array)
|
93
|
+
@subscribers[subscriber] = @subscribers[subscriber] - types
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
87
97
|
def publish_event(event)
|
88
98
|
@subscribers.each do |subscriber, types|
|
89
99
|
# If types isn't set or if the subscriber is subscribed to
|
@@ -246,10 +256,10 @@ module Bolt
|
|
246
256
|
@logger.trace { "Failed to submit analytics event: #{e.message}" }
|
247
257
|
end
|
248
258
|
|
249
|
-
def with_node_logging(description, batch)
|
250
|
-
@logger.
|
259
|
+
def with_node_logging(description, batch, log_level = :info)
|
260
|
+
@logger.send(log_level, "#{description} on #{batch.map(&:safe_name)}")
|
251
261
|
result = yield
|
252
|
-
@logger.
|
262
|
+
@logger.send(log_level, result.to_json)
|
253
263
|
result
|
254
264
|
end
|
255
265
|
|
@@ -279,32 +289,20 @@ module Bolt
|
|
279
289
|
end
|
280
290
|
end
|
281
291
|
|
282
|
-
def run_task(targets, task, arguments, options = {}, position = [])
|
292
|
+
def run_task(targets, task, arguments, options = {}, position = [], log_level = :info)
|
283
293
|
description = options.fetch(:description, "task #{task.name}")
|
284
294
|
log_action(description, targets) do
|
285
295
|
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
286
296
|
arguments['_task'] = task.name
|
287
297
|
|
288
298
|
batch_execute(targets) do |transport, batch|
|
289
|
-
with_node_logging("Running task #{task.name} with '#{arguments.to_json}'", batch) do
|
299
|
+
with_node_logging("Running task #{task.name} with '#{arguments.to_json}'", batch, log_level) do
|
290
300
|
transport.batch_task(batch, task, arguments, options, position, &method(:publish_event))
|
291
301
|
end
|
292
302
|
end
|
293
303
|
end
|
294
304
|
end
|
295
305
|
|
296
|
-
def run_task_with_minimal_logging(targets, task, arguments, options = {})
|
297
|
-
description = options.fetch(:description, "task #{task.name}")
|
298
|
-
log_action(description, targets) do
|
299
|
-
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
300
|
-
arguments['_task'] = task.name
|
301
|
-
|
302
|
-
batch_execute(targets) do |transport, batch|
|
303
|
-
transport.batch_task(batch, task, arguments, options, [], &method(:publish_event))
|
304
|
-
end
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
306
|
def run_task_with(target_mapping, task, options = {}, position = [])
|
309
307
|
targets = target_mapping.keys
|
310
308
|
description = options.fetch(:description, "task #{task.name}")
|
@@ -359,6 +357,82 @@ module Bolt
|
|
359
357
|
plan.call_by_name_with_scope(scope, params, true)
|
360
358
|
end
|
361
359
|
|
360
|
+
def create_yarn(scope, block, object, index)
|
361
|
+
fiber = Fiber.new do
|
362
|
+
# Create the new scope
|
363
|
+
newscope = Puppet::Parser::Scope.new(scope.compiler)
|
364
|
+
local = Puppet::Parser::Scope::LocalScope.new
|
365
|
+
|
366
|
+
# Compress the current scopes into a single vars hash to add to the new scope
|
367
|
+
current_scope = scope.effective_symtable(true)
|
368
|
+
until current_scope.nil?
|
369
|
+
current_scope.instance_variable_get(:@symbols)&.each_pair { |k, v| local[k] = v }
|
370
|
+
current_scope = current_scope.parent
|
371
|
+
end
|
372
|
+
newscope.push_ephemerals([local])
|
373
|
+
|
374
|
+
begin
|
375
|
+
result = catch(:return) do
|
376
|
+
args = { block.parameters[0][1].to_s => object }
|
377
|
+
block.closure.call_by_name_with_scope(newscope, args, true)
|
378
|
+
end
|
379
|
+
|
380
|
+
# If we got a return from the block, get it's value
|
381
|
+
# Otherwise the result is the last line from the block
|
382
|
+
result = result.value if result.is_a?(Puppet::Pops::Evaluator::Return)
|
383
|
+
|
384
|
+
# Validate the result is a PlanResult
|
385
|
+
unless Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult').instance?(result)
|
386
|
+
raise Bolt::InvalidParallelResult.new(result.to_s, *Puppet::Pops::PuppetStack.top_of_stack)
|
387
|
+
end
|
388
|
+
|
389
|
+
result
|
390
|
+
rescue Puppet::PreformattedError => e
|
391
|
+
if e.cause.is_a?(Bolt::Error)
|
392
|
+
e.cause
|
393
|
+
else
|
394
|
+
raise e
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
Bolt::Yarn.new(fiber, index)
|
400
|
+
end
|
401
|
+
|
402
|
+
def handle_event(event)
|
403
|
+
case event[:type]
|
404
|
+
when :node_result
|
405
|
+
@thread_completed = true
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def round_robin(skein)
|
410
|
+
subscribe(self, [:node_result])
|
411
|
+
results = Array.new(skein.length)
|
412
|
+
@in_parallel = true
|
413
|
+
|
414
|
+
until skein.empty?
|
415
|
+
@thread_completed = false
|
416
|
+
r = nil
|
417
|
+
|
418
|
+
skein.each do |yarn|
|
419
|
+
if yarn.alive?
|
420
|
+
r = yarn.resume
|
421
|
+
else
|
422
|
+
results[yarn.index] = yarn.value
|
423
|
+
skein.delete(yarn)
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
next unless r == 'unfinished'
|
428
|
+
sleep(0.1) until @thread_completed || skein.empty?
|
429
|
+
end
|
430
|
+
|
431
|
+
@in_parallel = false
|
432
|
+
unsubscribe(self, [:node_result])
|
433
|
+
results
|
434
|
+
end
|
435
|
+
|
362
436
|
class TimeoutError < RuntimeError; end
|
363
437
|
|
364
438
|
def wait_until_available(targets,
|
data/lib/bolt/inventory.rb
CHANGED
@@ -4,13 +4,17 @@ require 'set'
|
|
4
4
|
require 'bolt/config'
|
5
5
|
require 'bolt/inventory/group'
|
6
6
|
require 'bolt/inventory/inventory'
|
7
|
+
require 'bolt/inventory/options'
|
7
8
|
require 'bolt/target'
|
8
9
|
require 'bolt/util'
|
9
10
|
require 'bolt/plugin'
|
11
|
+
require 'bolt/validator'
|
10
12
|
require 'yaml'
|
11
13
|
|
12
14
|
module Bolt
|
13
15
|
class Inventory
|
16
|
+
include Bolt::Inventory::Options
|
17
|
+
|
14
18
|
ENVIRONMENT_VAR = 'BOLT_INVENTORY'
|
15
19
|
|
16
20
|
class ValidationError < Bolt::Error
|
@@ -45,11 +49,25 @@ module Bolt
|
|
45
49
|
end
|
46
50
|
end
|
47
51
|
|
52
|
+
# Builds the schema used by the validator.
|
53
|
+
#
|
54
|
+
def self.schema
|
55
|
+
schema = {
|
56
|
+
type: Hash,
|
57
|
+
properties: OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
|
58
|
+
definitions: DEFINITIONS
|
59
|
+
}
|
60
|
+
|
61
|
+
schema[:definitions]['config'][:properties] = Bolt::Config.transport_definitions
|
62
|
+
schema
|
63
|
+
end
|
64
|
+
|
48
65
|
def self.from_config(config, plugins)
|
49
66
|
logger = Bolt::Logger.logger(self)
|
50
67
|
|
51
68
|
if ENV.include?(ENVIRONMENT_VAR)
|
52
69
|
begin
|
70
|
+
source = ENVIRONMENT_VAR
|
53
71
|
data = YAML.safe_load(ENV[ENVIRONMENT_VAR])
|
54
72
|
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}" unless data.is_a?(Hash)
|
55
73
|
logger.debug("Loaded inventory from environment variable #{ENVIRONMENT_VAR}")
|
@@ -57,9 +75,11 @@ module Bolt
|
|
57
75
|
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
|
58
76
|
end
|
59
77
|
elsif config.inventoryfile
|
78
|
+
source = config.inventoryfile
|
60
79
|
data = Bolt::Util.read_yaml_hash(config.inventoryfile, 'inventory')
|
61
80
|
logger.debug("Loaded inventory from #{config.inventoryfile}")
|
62
81
|
else
|
82
|
+
source = config.default_inventoryfile
|
63
83
|
data = Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
|
64
84
|
|
65
85
|
if config.default_inventoryfile.exist?
|
@@ -74,6 +94,11 @@ module Bolt
|
|
74
94
|
t.resolve(plugins) unless t.resolved?
|
75
95
|
end
|
76
96
|
|
97
|
+
Bolt::Validator.new.tap do |validator|
|
98
|
+
validator.validate(data, schema, source)
|
99
|
+
validator.warnings.each { |warning| logger.warn(warning) }
|
100
|
+
end
|
101
|
+
|
77
102
|
inventory = create_version(data, config.transport, config.transports, plugins)
|
78
103
|
inventory.validate
|
79
104
|
inventory
|