bolt 2.9.0 → 2.13.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/Puppetfile +2 -2
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +28 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/datatypes/target.rb +4 -3
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +61 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +4 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +8 -2
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +144 -0
- data/bolt-modules/boltlib/types/planresult.pp +1 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +3 -1
- data/lib/bolt/analytics.rb +21 -2
- data/lib/bolt/applicator.rb +7 -2
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +3 -2
- data/lib/bolt/bolt_option_parser.rb +18 -8
- data/lib/bolt/catalog.rb +4 -1
- data/lib/bolt/cli.rb +15 -5
- data/lib/bolt/config.rb +44 -16
- data/lib/bolt/config/transport/ssh.rb +47 -1
- data/lib/bolt/inventory.rb +2 -1
- data/lib/bolt/inventory/inventory.rb +5 -0
- data/lib/bolt/inventory/target.rb +17 -1
- data/lib/bolt/node/output.rb +1 -1
- data/lib/bolt/pal.rb +3 -0
- data/lib/bolt/pal/yaml_plan.rb +1 -0
- data/lib/bolt/plugin.rb +13 -7
- data/lib/bolt/plugin/puppetdb.rb +5 -2
- data/lib/bolt/project.rb +25 -7
- data/lib/bolt/puppetdb/config.rb +14 -26
- data/lib/bolt/resource_instance.rb +129 -0
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/target.rb +12 -1
- data/lib/bolt/transport/ssh.rb +6 -2
- data/lib/bolt/transport/ssh/connection.rb +4 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +110 -0
- data/lib/bolt/transport/winrm/connection.rb +4 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/pe/pal.rb +1 -38
- data/lib/bolt_spec/bolt_context.rb +1 -4
- data/lib/bolt_spec/plans/mock_executor.rb +1 -0
- data/lib/bolt_spec/run.rb +2 -5
- metadata +6 -2
data/lib/bolt/analytics.rb
CHANGED
@@ -29,7 +29,7 @@ module Bolt
|
|
29
29
|
def self.build_client
|
30
30
|
logger = Logging.logger[self]
|
31
31
|
begin
|
32
|
-
config_file =
|
32
|
+
config_file = config_path(logger)
|
33
33
|
config = load_config(config_file, logger)
|
34
34
|
rescue ArgumentError
|
35
35
|
config = { 'disabled' => true }
|
@@ -51,6 +51,25 @@ module Bolt
|
|
51
51
|
NoopClient.new
|
52
52
|
end
|
53
53
|
|
54
|
+
def self.config_path(logger)
|
55
|
+
path = File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt', 'analytics.yaml'))
|
56
|
+
old_path = File.expand_path(File.join('~', '.puppetlabs', 'bolt', 'analytics.yaml'))
|
57
|
+
|
58
|
+
if File.exist?(path)
|
59
|
+
if File.exist?(old_path)
|
60
|
+
message = "Detected analytics configuration files at '#{old_path}' and '#{path}'. Loading "\
|
61
|
+
"analytics configuration from '#{path}'."
|
62
|
+
logger.warn(message)
|
63
|
+
end
|
64
|
+
|
65
|
+
path
|
66
|
+
elsif File.exist?(old_path)
|
67
|
+
old_path
|
68
|
+
else
|
69
|
+
path
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
54
73
|
def self.load_config(filename, logger)
|
55
74
|
if File.exist?(filename)
|
56
75
|
YAML.load_file(filename)
|
@@ -59,7 +78,7 @@ module Bolt
|
|
59
78
|
logger.warn <<~ANALYTICS
|
60
79
|
Bolt collects data about how you use it. You can opt out of providing this data.
|
61
80
|
|
62
|
-
To disable analytics data collection, add this line to ~/.puppetlabs/bolt/analytics.yaml :
|
81
|
+
To disable analytics data collection, add this line to ~/.puppetlabs/etc/bolt/analytics.yaml :
|
63
82
|
disabled: true
|
64
83
|
|
65
84
|
Read more about what data Bolt collects and why here:
|
data/lib/bolt/applicator.rb
CHANGED
@@ -14,7 +14,8 @@ require 'open3'
|
|
14
14
|
|
15
15
|
module Bolt
|
16
16
|
class Applicator
|
17
|
-
def initialize(inventory, executor, modulepath, plugin_dirs,
|
17
|
+
def initialize(inventory, executor, modulepath, plugin_dirs, project,
|
18
|
+
pdb_client, hiera_config, max_compiles, apply_settings)
|
18
19
|
# lazy-load expensive gem code
|
19
20
|
require 'concurrent'
|
20
21
|
|
@@ -22,6 +23,7 @@ module Bolt
|
|
22
23
|
@executor = executor
|
23
24
|
@modulepath = modulepath
|
24
25
|
@plugin_dirs = plugin_dirs
|
26
|
+
@project = project
|
25
27
|
@pdb_client = pdb_client
|
26
28
|
@hiera_config = hiera_config ? validate_hiera_config(hiera_config) : nil
|
27
29
|
@apply_settings = apply_settings || {}
|
@@ -34,6 +36,8 @@ module Bolt
|
|
34
36
|
search_dirs << mod.plugins if mod.plugins?
|
35
37
|
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
36
38
|
search_dirs << mod.files if mod.files?
|
39
|
+
type_files = "#{mod.path}/types"
|
40
|
+
search_dirs << type_files if File.exist?(type_files)
|
37
41
|
search_dirs
|
38
42
|
end
|
39
43
|
end
|
@@ -181,11 +185,12 @@ module Bolt
|
|
181
185
|
rich_data: true,
|
182
186
|
symbol_as_string: true,
|
183
187
|
type_by_reference: true,
|
184
|
-
local_reference:
|
188
|
+
local_reference: true)
|
185
189
|
|
186
190
|
scope = {
|
187
191
|
code_ast: ast,
|
188
192
|
modulepath: @modulepath,
|
193
|
+
project: @project.to_h,
|
189
194
|
pdb_config: @pdb_client.config.to_hash,
|
190
195
|
hiera_config: @hiera_config,
|
191
196
|
plan_vars: plan_vars,
|
data/lib/bolt/apply_result.rb
CHANGED
@@ -57,7 +57,7 @@ module Bolt
|
|
57
57
|
msg = "Report result contains an '_output' key. Catalog application may have printed extraneous output to stdout: #{result['_output']}"
|
58
58
|
# rubocop:enable Layout/LineLength
|
59
59
|
else
|
60
|
-
msg = "Report did not contain all expected keys missing: #{missing_keys.join('
|
60
|
+
msg = "Report did not contain all expected keys missing: #{missing_keys.join(', ')}"
|
61
61
|
end
|
62
62
|
|
63
63
|
{ 'msg' => msg,
|
data/lib/bolt/apply_target.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module Bolt
|
4
4
|
class ApplyTarget
|
5
5
|
ATTRIBUTES = %i[uri name target_alias config vars facts features
|
6
|
-
plugin_hooks safe_name].freeze
|
6
|
+
plugin_hooks resources safe_name].freeze
|
7
7
|
COMPUTED = %i[host password port protocol user].freeze
|
8
8
|
|
9
9
|
attr_reader(*ATTRIBUTES)
|
@@ -24,7 +24,8 @@ module Bolt
|
|
24
24
|
facts = nil,
|
25
25
|
vars = nil,
|
26
26
|
features = nil,
|
27
|
-
plugin_hooks = nil
|
27
|
+
plugin_hooks = nil,
|
28
|
+
resources = nil)
|
28
29
|
raise Bolt::Error.new("Target objects cannot be instantiated inside apply blocks", 'bolt/apply-error')
|
29
30
|
end
|
30
31
|
# rubocop:enable Lint/UnusedMethodArgument
|
@@ -11,7 +11,7 @@ module Bolt
|
|
11
11
|
escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
|
12
12
|
run_context: %w[concurrency inventoryfile save-rerun cleanup],
|
13
13
|
global_config_setters: %w[modulepath boltdir configfile],
|
14
|
-
transports: %w[transport connect-timeout tty],
|
14
|
+
transports: %w[transport connect-timeout tty ssh-command copy-command],
|
15
15
|
display: %w[format color verbose trace],
|
16
16
|
global: %w[help version debug] }.freeze
|
17
17
|
|
@@ -20,7 +20,7 @@ module Bolt
|
|
20
20
|
def get_help_text(subcommand, action = nil)
|
21
21
|
case subcommand
|
22
22
|
when 'apply'
|
23
|
-
{ flags: ACTION_OPTS + %w[noop execute compile-concurrency],
|
23
|
+
{ flags: ACTION_OPTS + %w[noop execute compile-concurrency hiera-config],
|
24
24
|
banner: APPLY_HELP }
|
25
25
|
when 'command'
|
26
26
|
case action
|
@@ -172,13 +172,14 @@ module Bolt
|
|
172
172
|
apply
|
173
173
|
|
174
174
|
USAGE
|
175
|
-
bolt apply
|
175
|
+
bolt apply [manifest.pp] [options]
|
176
176
|
|
177
177
|
DESCRIPTION
|
178
178
|
Apply Puppet manifest code on the specified targets.
|
179
179
|
|
180
180
|
EXAMPLES
|
181
|
-
bolt apply manifest.pp
|
181
|
+
bolt apply manifest.pp -t target
|
182
|
+
bolt apply -e "file { '/etc/puppetlabs': ensure => present }" -t target
|
182
183
|
HELP
|
183
184
|
|
184
185
|
COMMAND_HELP = <<~HELP
|
@@ -594,6 +595,7 @@ module Bolt
|
|
594
595
|
HELP
|
595
596
|
|
596
597
|
attr_reader :warnings
|
598
|
+
|
597
599
|
def initialize(options)
|
598
600
|
super()
|
599
601
|
|
@@ -655,8 +657,8 @@ module Bolt
|
|
655
657
|
@options[:password] = STDIN.noecho(&:gets).chomp
|
656
658
|
STDERR.puts
|
657
659
|
end
|
658
|
-
define('--private-key KEY', '
|
659
|
-
@options[:'private-key'] = key
|
660
|
+
define('--private-key KEY', 'Path to private ssh key to authenticate with') do |key|
|
661
|
+
@options[:'private-key'] = File.expand_path(key)
|
660
662
|
end
|
661
663
|
define('--[no-]host-key-check', 'Check host keys with SSH') do |host_key_check|
|
662
664
|
@options[:'host-key-check'] = host_key_check
|
@@ -688,7 +690,7 @@ module Bolt
|
|
688
690
|
|
689
691
|
separator "\nRUN CONTEXT OPTIONS"
|
690
692
|
define('-c', '--concurrency CONCURRENCY', Integer,
|
691
|
-
'Maximum number of simultaneous connections
|
693
|
+
'Maximum number of simultaneous connections') do |concurrency|
|
692
694
|
@options[:concurrency] = concurrency
|
693
695
|
end
|
694
696
|
define('--compile-concurrency CONCURRENCY', Integer,
|
@@ -718,7 +720,7 @@ module Bolt
|
|
718
720
|
end
|
719
721
|
define('--hiera-config FILEPATH',
|
720
722
|
'Specify where to load Hiera config from (default: ~/.puppetlabs/bolt/hiera.yaml)') do |path|
|
721
|
-
@options[:'hiera-config'] = path
|
723
|
+
@options[:'hiera-config'] = File.expand_path(path)
|
722
724
|
end
|
723
725
|
define('-i', '--inventoryfile FILEPATH',
|
724
726
|
'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
|
@@ -741,6 +743,14 @@ module Bolt
|
|
741
743
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
742
744
|
@options[:transport] = t
|
743
745
|
end
|
746
|
+
define('--ssh-command EXEC', "Executable to use instead of the net-ssh ruby library. ",
|
747
|
+
"This option is experimental.") do |exec|
|
748
|
+
@options[:'ssh-command'] = exec
|
749
|
+
end
|
750
|
+
define('--copy-command EXEC', "Command to copy files to remote hosts if using external SSH. ",
|
751
|
+
"This option is experimental.") do |exec|
|
752
|
+
@options[:'copy-command'] = exec
|
753
|
+
end
|
744
754
|
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
745
755
|
@options[:'connect-timeout'] = timeout
|
746
756
|
end
|
data/lib/bolt/catalog.rb
CHANGED
@@ -58,6 +58,8 @@ module Bolt
|
|
58
58
|
target = request['target']
|
59
59
|
pdb_client = Bolt::PuppetDB::Client.new(Bolt::PuppetDB::Config.new(request['pdb_config']))
|
60
60
|
options = request['puppet_config'] || {}
|
61
|
+
project = request['project'] || {}
|
62
|
+
bolt_project = Struct.new(:name, :path).new(project['name'], project['path']) unless project.empty?
|
61
63
|
with_puppet_settings(request['hiera_config']) do
|
62
64
|
Puppet[:rich_data] = true
|
63
65
|
Puppet[:node_name_value] = target['name']
|
@@ -67,7 +69,8 @@ module Bolt
|
|
67
69
|
Puppet::Pal.in_tmp_environment('bolt_catalog', env_conf) do |pal|
|
68
70
|
inv = Bolt::ApplyInventory.new(request['config'])
|
69
71
|
Puppet.override(bolt_pdb_client: pdb_client,
|
70
|
-
bolt_inventory: inv
|
72
|
+
bolt_inventory: inv,
|
73
|
+
bolt_project: bolt_project) do
|
71
74
|
Puppet.lookup(:pal_current_node).trusted_data = target['trusted']
|
72
75
|
pal.with_catalog_compiler do |compiler|
|
73
76
|
# Deserializing needs to happen inside the catalog compiler so
|
data/lib/bolt/cli.rb
CHANGED
@@ -124,8 +124,9 @@ module Bolt
|
|
124
124
|
|
125
125
|
Bolt::Logger.configure(config.log, config.color)
|
126
126
|
|
127
|
-
# Logger must be configured before checking path case, otherwise warnings will not display
|
127
|
+
# Logger must be configured before checking path case and project file, otherwise warnings will not display
|
128
128
|
@config.check_path_case('modulepath', @config.modulepath)
|
129
|
+
@config.project.check_deprecated_file
|
129
130
|
|
130
131
|
# Log the file paths for loaded config files
|
131
132
|
config_loaded
|
@@ -257,13 +258,11 @@ module Bolt
|
|
257
258
|
end
|
258
259
|
|
259
260
|
def puppetdb_client
|
260
|
-
|
261
|
-
puppetdb_config = Bolt::PuppetDB::Config.load_config(nil, config.puppetdb, config.project.path)
|
262
|
-
@puppetdb_client = Bolt::PuppetDB::Client.new(puppetdb_config)
|
261
|
+
plugins.puppetdb_client
|
263
262
|
end
|
264
263
|
|
265
264
|
def plugins
|
266
|
-
@plugins ||= Bolt::Plugin.setup(config, pal,
|
265
|
+
@plugins ||= Bolt::Plugin.setup(config, pal, analytics)
|
267
266
|
end
|
268
267
|
|
269
268
|
def query_puppetdb_nodes(query)
|
@@ -538,6 +537,17 @@ module Bolt
|
|
538
537
|
Puppet[:tasks] = false
|
539
538
|
ast = pal.parse_manifest(code, filename)
|
540
539
|
|
540
|
+
if defined?(ast.body) &&
|
541
|
+
(ast.body.is_a?(Puppet::Pops::Model::HostClassDefinition) ||
|
542
|
+
ast.body.is_a?(Puppet::Pops::Model::ResourceTypeDefinition))
|
543
|
+
message = "Manifest only contains definitions and will result in no changes on the targets. "\
|
544
|
+
"Definitions must be declared for their resources to be applied. You can read more "\
|
545
|
+
"about defining and declaring classes and types in the Puppet documentation at "\
|
546
|
+
"https://puppet.com/docs/puppet/latest/lang_classes.html and "\
|
547
|
+
"https://puppet.com/docs/puppet/latest/lang_defined_types.html"
|
548
|
+
@logger.warn(message)
|
549
|
+
end
|
550
|
+
|
541
551
|
executor = Bolt::Executor.new(config.concurrency, analytics, noop)
|
542
552
|
executor.subscribe(outputter) if options.fetch(:format, 'human') == 'human'
|
543
553
|
executor.subscribe(log_outputter)
|
data/lib/bolt/config.rb
CHANGED
@@ -47,9 +47,7 @@ module Bolt
|
|
47
47
|
"targets on the command line and from plans.",
|
48
48
|
"log" => "The configuration of the logfile output. Configuration can be set for "\
|
49
49
|
"`console` and the path to a log file, such as `~/.puppetlabs/bolt/debug.log`.",
|
50
|
-
"modulepath" => "
|
51
|
-
"of directories or a string containing a list of directories separated by the "\
|
52
|
-
"OS-specific PATH separator.",
|
50
|
+
"modulepath" => "An array of directories that Bolt loads content (e.g. plans and tasks) from.",
|
53
51
|
"plugin_hooks" => "Which plugins a specific hook should use.",
|
54
52
|
"plugins" => "A map of plugins and their configuration data.",
|
55
53
|
"puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
|
@@ -66,8 +64,8 @@ module Bolt
|
|
66
64
|
|
67
65
|
DEFAULT_OPTIONS = {
|
68
66
|
"color" => true,
|
69
|
-
"concurrency" => 100,
|
70
67
|
"compile-concurrency" => "Number of cores",
|
68
|
+
"concurrency" => "100 or one-third of the ulimit, whichever is lower",
|
71
69
|
"format" => "human",
|
72
70
|
"hiera-config" => "Boltdir/hiera.yaml",
|
73
71
|
"inventoryfile" => "Boltdir/inventory.yaml",
|
@@ -103,6 +101,8 @@ module Bolt
|
|
103
101
|
"show_diff" => false
|
104
102
|
}.freeze
|
105
103
|
|
104
|
+
DEFAULT_DEFAULT_CONCURRENCY = 100
|
105
|
+
|
106
106
|
def self.default
|
107
107
|
new(Bolt::Project.new('.'), {})
|
108
108
|
end
|
@@ -113,7 +113,7 @@ module Bolt
|
|
113
113
|
data: Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
|
114
114
|
}
|
115
115
|
|
116
|
-
data = load_defaults.push(data).select { |config| config[:data]&.any? }
|
116
|
+
data = load_defaults(project).push(data).select { |config| config[:data]&.any? }
|
117
117
|
|
118
118
|
new(project, data, overrides)
|
119
119
|
end
|
@@ -125,27 +125,29 @@ module Bolt
|
|
125
125
|
filepath: project.config_file,
|
126
126
|
data: Bolt::Util.read_yaml_hash(configfile, 'config')
|
127
127
|
}
|
128
|
-
data = load_defaults.push(data).select { |config| config[:data]&.any? }
|
128
|
+
data = load_defaults(project).push(data).select { |config| config[:data]&.any? }
|
129
129
|
|
130
130
|
new(project, data, overrides)
|
131
131
|
end
|
132
132
|
|
133
|
-
def self.load_defaults
|
133
|
+
def self.load_defaults(project)
|
134
134
|
# Lazy-load expensive gem code
|
135
135
|
require 'win32/dir' if Bolt::Util.windows?
|
136
136
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
137
|
+
# Don't load /etc/puppetlabs/bolt/bolt.yaml twice
|
138
|
+
confs = if project.path == Bolt::Project.system_path
|
139
|
+
[]
|
140
|
+
else
|
141
|
+
system_path = Pathname.new(File.join(Bolt::Project.system_path, 'bolt.yaml'))
|
142
|
+
[{ filepath: system_path, data: Bolt::Util.read_optional_yaml_hash(system_path, 'config') }]
|
143
|
+
end
|
144
|
+
|
142
145
|
user_path = begin
|
143
146
|
Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt', 'bolt.yaml')))
|
144
147
|
rescue ArgumentError
|
145
148
|
nil
|
146
149
|
end
|
147
150
|
|
148
|
-
confs = [{ filepath: system_path, data: Bolt::Util.read_optional_yaml_hash(system_path, 'config') }]
|
149
151
|
confs << { filepath: user_path, data: Bolt::Util.read_optional_yaml_hash(user_path, 'config') } if user_path
|
150
152
|
confs
|
151
153
|
end
|
@@ -165,7 +167,7 @@ module Bolt
|
|
165
167
|
'apply_settings' => {},
|
166
168
|
'color' => true,
|
167
169
|
'compile-concurrency' => Etc.nprocessors,
|
168
|
-
'concurrency' =>
|
170
|
+
'concurrency' => default_concurrency,
|
169
171
|
'format' => 'human',
|
170
172
|
'log' => { 'console' => {} },
|
171
173
|
'plugin_hooks' => {},
|
@@ -183,6 +185,18 @@ module Bolt
|
|
183
185
|
|
184
186
|
override_data = normalize_overrides(overrides)
|
185
187
|
|
188
|
+
# If we need to lower concurrency and concurrency is not configured
|
189
|
+
ld_concurrency = loaded_data.map(&:keys).flatten.include?('concurrency')
|
190
|
+
if default_concurrency != DEFAULT_DEFAULT_CONCURRENCY &&
|
191
|
+
!ld_concurrency &&
|
192
|
+
!override_data.key?('concurrency')
|
193
|
+
concurrency_warning = { option: 'concurrency',
|
194
|
+
msg: "Concurrency will default to #{default_concurrency} because ulimit "\
|
195
|
+
"is low: #{Etc.sysconf(Etc::SC_OPEN_MAX)}. Set concurrency with "\
|
196
|
+
"'--concurrency', or set your ulimit with 'ulimit -n <limit>'" }
|
197
|
+
@warnings << concurrency_warning
|
198
|
+
end
|
199
|
+
|
186
200
|
@data = merge_config_layers(default_data, *loaded_data, override_data)
|
187
201
|
|
188
202
|
TRANSPORT_CONFIG.each do |transport, config|
|
@@ -312,10 +326,10 @@ module Bolt
|
|
312
326
|
@warnings << { option: 'future', msg: msg }
|
313
327
|
end
|
314
328
|
|
315
|
-
keys = OPTIONS.keys - %w[plugins plugin_hooks]
|
329
|
+
keys = OPTIONS.keys - %w[plugins plugin_hooks puppetdb]
|
316
330
|
keys.each do |key|
|
317
331
|
next unless Bolt::Util.references?(@data[key])
|
318
|
-
valid_keys = TRANSPORT_CONFIG.keys + %w[plugins plugin_hooks]
|
332
|
+
valid_keys = TRANSPORT_CONFIG.keys + %w[plugins plugin_hooks puppetdb]
|
319
333
|
raise Bolt::ValidationError,
|
320
334
|
"Found unsupported key _plugin in config setting #{key}. Plugins are only available in "\
|
321
335
|
"#{valid_keys.join(', ')}."
|
@@ -458,5 +472,19 @@ module Bolt
|
|
458
472
|
l =~ /[A-Za-z]/ ? "[#{l.upcase}#{l.downcase}]" : l
|
459
473
|
end.join
|
460
474
|
end
|
475
|
+
|
476
|
+
# Etc::SC_OPEN_MAX is meaningless on windows, not defined in PE Jruby and not available
|
477
|
+
# on some platforms. This method holds the logic to decide whether or not to even consider it.
|
478
|
+
def sc_open_max_available?
|
479
|
+
!Bolt::Util.windows? && defined?(Etc::SC_OPEN_MAX) && Etc.sysconf(Etc::SC_OPEN_MAX)
|
480
|
+
end
|
481
|
+
|
482
|
+
def default_concurrency
|
483
|
+
@default_concurrency ||= if !sc_open_max_available? || Etc.sysconf(Etc::SC_OPEN_MAX) >= 300
|
484
|
+
DEFAULT_DEFAULT_CONCURRENCY
|
485
|
+
else
|
486
|
+
Etc.sysconf(Etc::SC_OPEN_MAX) / 3
|
487
|
+
end
|
488
|
+
end
|
461
489
|
end
|
462
490
|
end
|
@@ -13,14 +13,20 @@ module Bolt
|
|
13
13
|
# in schemas/bolt-transport-definitions.json
|
14
14
|
OPTIONS = {
|
15
15
|
"cleanup" => { type: TrueClass,
|
16
|
+
external: true,
|
16
17
|
desc: "Whether to clean up temporary files created on targets." },
|
17
18
|
"connect-timeout" => { type: Integer,
|
18
19
|
desc: "How long to wait when establishing connections." },
|
20
|
+
"copy-command" => { external: true,
|
21
|
+
desc: "Command to use when copying files using ssh-command. "\
|
22
|
+
"Bolt runs `<copy-command> <src> <dest>`. **This option is experimental.**" },
|
19
23
|
"disconnect-timeout" => { type: Integer,
|
20
24
|
desc: "How long to wait before force-closing a connection." },
|
21
25
|
"host" => { type: String,
|
26
|
+
external: true,
|
22
27
|
desc: "Host name." },
|
23
28
|
"host-key-check" => { type: TrueClass,
|
29
|
+
external: true,
|
24
30
|
desc: "Whether to perform host key validation when connecting." },
|
25
31
|
"extensions" => { type: Array,
|
26
32
|
desc: "List of file extensions that are accepted for scripts or tasks on Windows. "\
|
@@ -29,6 +35,7 @@ module Bolt
|
|
29
35
|
"a `.py` script runs with `python.exe`. The extensions `.ps1`, `.rb`, and "\
|
30
36
|
"`.pp` are always allowed and run via hard-coded executables." },
|
31
37
|
"interpreters" => { type: Hash,
|
38
|
+
external: true,
|
32
39
|
desc: "A map of an extension name to the absolute path of an executable, "\
|
33
40
|
"enabling you to override the shebang defined in a task executable. The "\
|
34
41
|
"extension can optionally be specified with the `.` character (`.py` and "\
|
@@ -44,26 +51,36 @@ module Bolt
|
|
44
51
|
"password" => { type: String,
|
45
52
|
desc: "Login password." },
|
46
53
|
"port" => { type: Integer,
|
54
|
+
external: true,
|
47
55
|
desc: "Connection port." },
|
48
|
-
"private-key" => {
|
56
|
+
"private-key" => { external: true,
|
57
|
+
desc: "Either the path to the private key file to use for authentication, or a "\
|
49
58
|
"hash with the key `key-data` and the contents of the private key." },
|
50
59
|
"proxyjump" => { type: String,
|
51
60
|
desc: "A jump host to proxy connections through, and an optional user to "\
|
52
61
|
"connect with." },
|
53
62
|
"run-as" => { type: String,
|
63
|
+
external: true,
|
54
64
|
desc: "A different user to run commands as after login." },
|
55
65
|
"run-as-command" => { type: Array,
|
66
|
+
external: true,
|
56
67
|
desc: "The command to elevate permissions. Bolt appends the user and command "\
|
57
68
|
"strings to the configured `run-as-command` before running it on the "\
|
58
69
|
"target. This command must not require an interactive password prompt, "\
|
59
70
|
"and the `sudo-password` option is ignored when `run-as-command` is "\
|
60
71
|
"specified. The `run-as-command` must be specified as an array." },
|
61
72
|
"script-dir" => { type: String,
|
73
|
+
external: true,
|
62
74
|
desc: "The subdirectory of the tmpdir to use in place of a randomized "\
|
63
75
|
"subdirectory for uploading and executing temporary files on the "\
|
64
76
|
"target. It's expected that this directory already exists as a subdir "\
|
65
77
|
"of tmpdir, which is either configured or defaults to `/tmp`." },
|
78
|
+
"ssh-command" => { external: true,
|
79
|
+
desc: "Command and flags to use when SSHing. This enables the external "\
|
80
|
+
"SSH transport which shells out to the specified command. "\
|
81
|
+
"**This option is experimental.**" },
|
66
82
|
"sudo-executable" => { type: String,
|
83
|
+
external: true,
|
67
84
|
desc: "The executable to use when escalating to the configured `run-as` "\
|
68
85
|
"user. This is useful when you want to escalate using the configured "\
|
69
86
|
"`sudo-password`, since `run-as-command` does not use `sudo-password` "\
|
@@ -71,14 +88,17 @@ module Bolt
|
|
71
88
|
"`<sudo-executable> -S -u <user> -p custom_bolt_prompt <command>`. "\
|
72
89
|
"**This option is experimental.**" },
|
73
90
|
"sudo-password" => { type: String,
|
91
|
+
external: true,
|
74
92
|
desc: "Password to use when changing users via `run-as`." },
|
75
93
|
"tmpdir" => { type: String,
|
94
|
+
external: true,
|
76
95
|
desc: "The directory to upload and execute temporary files on the target." },
|
77
96
|
"tty" => { type: TrueClass,
|
78
97
|
desc: "Request a pseudo tty for the session. This option is generally "\
|
79
98
|
"only used in conjunction with the `run-as` option when the sudoers "\
|
80
99
|
"policy requires a `tty`." },
|
81
100
|
"user" => { type: String,
|
101
|
+
external: true,
|
82
102
|
desc: "Login user." }
|
83
103
|
}.freeze
|
84
104
|
|
@@ -103,6 +123,13 @@ module Bolt
|
|
103
123
|
|
104
124
|
if key_opt.instance_of?(String)
|
105
125
|
@config['private-key'] = File.expand_path(key_opt, @project)
|
126
|
+
|
127
|
+
# We have an explicit test for this to only warn if using net-ssh transport
|
128
|
+
Bolt::Util.validate_file('ssh key', @config['private-key']) if @config['ssh-command']
|
129
|
+
end
|
130
|
+
|
131
|
+
if key_opt.instance_of?(Hash) && @config['ssh-command']
|
132
|
+
raise Bolt::ValidationError, 'private-key must be a filepath when using ssh-command'
|
106
133
|
end
|
107
134
|
end
|
108
135
|
|
@@ -130,6 +157,25 @@ module Bolt
|
|
130
157
|
end
|
131
158
|
end
|
132
159
|
end
|
160
|
+
|
161
|
+
if @config['ssh-command'] && !@config['load-config']
|
162
|
+
msg = 'Cannot use external SSH transport with load-config set to false'
|
163
|
+
raise Bolt::ValidationError, msg
|
164
|
+
end
|
165
|
+
|
166
|
+
if (ssh_cmd = @config['ssh-command'])
|
167
|
+
unless ssh_cmd.is_a?(String) || ssh_cmd.is_a?(Array)
|
168
|
+
raise Bolt::ValidationError,
|
169
|
+
"ssh-command must be a String or Array, received #{ssh_cmd.class} #{ssh_cmd.inspect}"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
if (copy_cmd = @config['copy-command'])
|
174
|
+
unless copy_cmd.is_a?(String) || copy_cmd.is_a?(Array)
|
175
|
+
raise Bolt::ValidationError,
|
176
|
+
"copy-command must be a String or Array, received #{copy_cmd.class} #{copy_cmd.inspect}"
|
177
|
+
end
|
178
|
+
end
|
133
179
|
end
|
134
180
|
end
|
135
181
|
end
|