bolt 2.17.0 → 2.18.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/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/lib/bolt/applicator.rb +19 -14
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +7 -3
- data/lib/bolt/cli.rb +67 -55
- data/lib/bolt/config.rb +16 -3
- data/lib/bolt/config/options.rb +2 -1
- data/lib/bolt/config/transport/options.rb +16 -10
- data/lib/bolt/config/transport/ssh.rb +24 -10
- data/lib/bolt/logger.rb +12 -0
- data/lib/bolt/project.rb +11 -0
- data/lib/bolt/shell/powershell/snippets.rb +15 -6
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
- data/lib/bolt/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87eb513700afc2d9fefa9f2497e8718f0e14e511ea2004c59eecfcb19e41bdbc
|
4
|
+
data.tar.gz: 499d4c8a1a3c0d4b883d83f6478019467a44999beba83712ce49a30ee2ff1fc8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6046f666fe9bccc4df2a70b191582b14b834063caf31987c1131e7e7c57ac9280e0bf76d719f9b82f27f5de7205b744a2dd3d485f53e9aa867661b824a5b0945
|
7
|
+
data.tar.gz: fc77aa2acd93b4aab739f468a2aa6a6a2da2c9c10bbc4927005f60aeb20148ecb29884d6f2c6cf86bf84d76af276a55f1a8588734157444ab5844c0b9d9878c9
|
@@ -13,10 +13,13 @@ require 'bolt/task'
|
|
13
13
|
# > **Note:** Not available in apply block
|
14
14
|
Puppet::Functions.create_function(:apply_prep) do
|
15
15
|
# @param targets A pattern or array of patterns identifying a set of targets.
|
16
|
+
# @param options Options hash.
|
17
|
+
# @option options [Array] _required_modules An array of modules to sync to the target.
|
16
18
|
# @example Prepare targets by name.
|
17
19
|
# apply_prep('target1,target2')
|
18
20
|
dispatch :apply_prep do
|
19
21
|
param 'Boltlib::TargetSpec', :targets
|
22
|
+
optional_param 'Hash[String, Data]', :options
|
20
23
|
end
|
21
24
|
|
22
25
|
def script_compiler
|
@@ -60,18 +63,34 @@ Puppet::Functions.create_function(:apply_prep) do
|
|
60
63
|
@executor ||= Puppet.lookup(:bolt_executor)
|
61
64
|
end
|
62
65
|
|
63
|
-
def apply_prep(target_spec)
|
66
|
+
def apply_prep(target_spec, options = {})
|
64
67
|
unless Puppet[:tasks]
|
65
68
|
raise Puppet::ParseErrorWithIssue
|
66
69
|
.from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'apply_prep')
|
67
70
|
end
|
68
71
|
|
72
|
+
options = options.transform_keys { |k| k.sub(/^_/, '').to_sym }
|
73
|
+
|
69
74
|
applicator = Puppet.lookup(:apply_executor)
|
70
75
|
|
71
76
|
executor.report_function_call(self.class.name)
|
72
77
|
|
73
78
|
targets = inventory.get_targets(target_spec)
|
74
79
|
|
80
|
+
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
81
|
+
if required_modules&.any?
|
82
|
+
Puppet.debug("Syncing only required modules: #{required_modules.join(',')}.")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Gather facts, including custom facts
|
86
|
+
plugins = applicator.build_plugin_tarball do |mod|
|
87
|
+
next unless required_modules.nil? || required_modules.include?(mod.name)
|
88
|
+
search_dirs = []
|
89
|
+
search_dirs << mod.plugins if mod.plugins?
|
90
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
91
|
+
search_dirs
|
92
|
+
end
|
93
|
+
|
75
94
|
executor.log_action('install puppet and gather facts', targets) do
|
76
95
|
executor.without_default_logging do
|
77
96
|
# Skip targets that include the puppet-agent feature, as we know an agent will be available.
|
@@ -109,14 +128,6 @@ Puppet::Functions.create_function(:apply_prep) do
|
|
109
128
|
need_install_targets.each { |target| set_agent_feature(target) }
|
110
129
|
end
|
111
130
|
|
112
|
-
# Gather facts, including custom facts
|
113
|
-
plugins = applicator.build_plugin_tarball do |mod|
|
114
|
-
search_dirs = []
|
115
|
-
search_dirs << mod.plugins if mod.plugins?
|
116
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
117
|
-
search_dirs
|
118
|
-
end
|
119
|
-
|
120
131
|
task = applicator.custom_facts_task
|
121
132
|
arguments = { 'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins) }
|
122
133
|
results = executor.run_task(targets, task, arguments)
|
data/lib/bolt/applicator.rb
CHANGED
@@ -18,7 +18,6 @@ module Bolt
|
|
18
18
|
pdb_client, hiera_config, max_compiles, apply_settings)
|
19
19
|
# lazy-load expensive gem code
|
20
20
|
require 'concurrent'
|
21
|
-
|
22
21
|
@inventory = inventory
|
23
22
|
@executor = executor
|
24
23
|
@modulepath = modulepath || []
|
@@ -30,17 +29,6 @@ module Bolt
|
|
30
29
|
|
31
30
|
@pool = Concurrent::ThreadPoolExecutor.new(max_threads: max_compiles)
|
32
31
|
@logger = Logging.logger[self]
|
33
|
-
@plugin_tarball = Concurrent::Delay.new do
|
34
|
-
build_plugin_tarball do |mod|
|
35
|
-
search_dirs = []
|
36
|
-
search_dirs << mod.plugins if mod.plugins?
|
37
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
38
|
-
search_dirs << mod.files if mod.files?
|
39
|
-
type_files = "#{mod.path}/types"
|
40
|
-
search_dirs << type_files if File.exist?(type_files)
|
41
|
-
search_dirs
|
42
|
-
end
|
43
|
-
end
|
44
32
|
end
|
45
33
|
|
46
34
|
private def libexec
|
@@ -188,7 +176,6 @@ module Bolt
|
|
188
176
|
|
189
177
|
def apply_ast(raw_ast, targets, options, plan_vars = {})
|
190
178
|
ast = Puppet::Pops::Serialization::ToDataConverter.convert(raw_ast, rich_data: true, symbol_to_string: true)
|
191
|
-
|
192
179
|
# Serialize as pcore for *Result* objects
|
193
180
|
plan_vars = Puppet::Pops::Serialization::ToDataConverter.convert(plan_vars,
|
194
181
|
rich_data: true,
|
@@ -207,9 +194,26 @@ module Bolt
|
|
207
194
|
# This data isn't available on the target config hash
|
208
195
|
config: @inventory.transport_data_get
|
209
196
|
}
|
210
|
-
|
211
197
|
description = options[:description] || 'apply catalog'
|
212
198
|
|
199
|
+
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
200
|
+
if required_modules&.any?
|
201
|
+
@logger.debug("Syncing only required modules: #{required_modules.join(',')}.")
|
202
|
+
end
|
203
|
+
|
204
|
+
@plugin_tarball = Concurrent::Delay.new do
|
205
|
+
build_plugin_tarball do |mod|
|
206
|
+
next unless required_modules.nil? || required_modules.include?(mod.name)
|
207
|
+
search_dirs = []
|
208
|
+
search_dirs << mod.plugins if mod.plugins?
|
209
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
210
|
+
search_dirs << mod.files if mod.files?
|
211
|
+
type_files = "#{mod.path}/types"
|
212
|
+
search_dirs << type_files if File.exist?(type_files)
|
213
|
+
search_dirs
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
213
217
|
r = @executor.log_action(description, targets) do
|
214
218
|
futures = targets.map do |target|
|
215
219
|
Concurrent::Future.execute(executor: @pool) do
|
@@ -236,6 +240,7 @@ module Bolt
|
|
236
240
|
result
|
237
241
|
end
|
238
242
|
else
|
243
|
+
|
239
244
|
arguments = {
|
240
245
|
'catalog' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(catalog),
|
241
246
|
'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins),
|
data/lib/bolt/apply_result.rb
CHANGED
@@ -20,7 +20,7 @@ module Bolt
|
|
20
20
|
error_hash['msg'] =~ /The term 'ruby.exe' is not recognized as the name of a cmdlet/)
|
21
21
|
# Windows does not have Ruby present
|
22
22
|
{
|
23
|
-
'msg' => "Puppet
|
23
|
+
'msg' => "Puppet was not found on the target or in $env:ProgramFiles, please install it to enable 'apply'",
|
24
24
|
'kind' => 'bolt/apply-error'
|
25
25
|
}
|
26
26
|
elsif exit_code == 1 && error_hash['msg'] =~ /cannot load such file -- puppet \(LoadError\)/
|
@@ -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 project configfile],
|
14
|
-
transports: %w[transport connect-timeout tty ssh-command copy-command],
|
14
|
+
transports: %w[transport connect-timeout tty native-ssh ssh-command copy-command],
|
15
15
|
display: %w[format color verbose trace],
|
16
16
|
global: %w[help version debug log-level] }.freeze
|
17
17
|
|
@@ -743,11 +743,15 @@ module Bolt
|
|
743
743
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
744
744
|
@options[:transport] = t
|
745
745
|
end
|
746
|
-
define('--ssh
|
746
|
+
define('--[no-]native-ssh', 'Whether to shell out to native SSH or use the net-ssh Ruby library.',
|
747
|
+
'This option is experimental') do |bool|
|
748
|
+
@options[:'native-ssh'] = bool
|
749
|
+
end
|
750
|
+
define('--ssh-command EXEC', "Executable to use instead of the net-ssh Ruby library. ",
|
747
751
|
"This option is experimental.") do |exec|
|
748
752
|
@options[:'ssh-command'] = exec
|
749
753
|
end
|
750
|
-
define('--copy-command EXEC', "Command to copy files to remote hosts if using
|
754
|
+
define('--copy-command EXEC', "Command to copy files to remote hosts if using native SSH. ",
|
751
755
|
"This option is experimental.") do |exec|
|
752
756
|
@options[:'copy-command'] = exec
|
753
757
|
end
|
data/lib/bolt/cli.rb
CHANGED
@@ -46,7 +46,6 @@ module Bolt
|
|
46
46
|
Bolt::Logger.initialize_logging
|
47
47
|
@logger = Logging.logger[self]
|
48
48
|
@argv = argv
|
49
|
-
@config = Bolt::Config.default
|
50
49
|
@options = {}
|
51
50
|
end
|
52
51
|
|
@@ -77,61 +76,74 @@ module Bolt
|
|
77
76
|
private :help?
|
78
77
|
|
79
78
|
def parse
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
options[:object] = remaining.shift
|
91
|
-
|
92
|
-
# Only parse task_options for task or plan
|
93
|
-
if %w[task plan].include?(options[:subcommand])
|
94
|
-
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
95
|
-
if options[:task_options]
|
96
|
-
unless task_options.empty?
|
97
|
-
raise Bolt::CLIError,
|
98
|
-
"Parameters must be specified through either the --params " \
|
99
|
-
"option or param=value pairs, not both"
|
100
|
-
end
|
101
|
-
options[:params_parsed] = true
|
102
|
-
elsif task_options.any?
|
103
|
-
options[:params_parsed] = false
|
104
|
-
options[:task_options] = Hash[task_options.map { |a| a.split('=', 2) }]
|
105
|
-
else
|
106
|
-
options[:params_parsed] = true
|
107
|
-
options[:task_options] = {}
|
79
|
+
begin
|
80
|
+
parser = BoltOptionParser.new(options)
|
81
|
+
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
82
|
+
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
83
|
+
if @argv.empty? || help?(remaining)
|
84
|
+
# Update the parser for the subcommand (or lack thereof)
|
85
|
+
parser.update
|
86
|
+
puts parser.help
|
87
|
+
raise Bolt::CLIExit
|
108
88
|
end
|
109
|
-
end
|
110
|
-
options[:leftovers] = remaining
|
111
89
|
|
112
|
-
|
90
|
+
options[:object] = remaining.shift
|
113
91
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
92
|
+
# Only parse task_options for task or plan
|
93
|
+
if %w[task plan].include?(options[:subcommand])
|
94
|
+
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
95
|
+
if options[:task_options]
|
96
|
+
unless task_options.empty?
|
97
|
+
raise Bolt::CLIError,
|
98
|
+
"Parameters must be specified through either the --params " \
|
99
|
+
"option or param=value pairs, not both"
|
100
|
+
end
|
101
|
+
options[:params_parsed] = true
|
102
|
+
elsif task_options.any?
|
103
|
+
options[:params_parsed] = false
|
104
|
+
options[:task_options] = Hash[task_options.map { |a| a.split('=', 2) }]
|
105
|
+
else
|
106
|
+
options[:params_parsed] = true
|
107
|
+
options[:task_options] = {}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
options[:leftovers] = remaining
|
111
|
+
|
112
|
+
validate(options)
|
113
|
+
|
114
|
+
@config = if ENV['BOLT_PROJECT']
|
115
|
+
project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
|
116
|
+
Bolt::Config.from_project(project, options)
|
117
|
+
elsif options[:configfile]
|
118
|
+
Bolt::Config.from_file(options[:configfile], options)
|
119
|
+
else
|
120
|
+
project = if options[:boltdir]
|
121
|
+
dir = Pathname.new(options[:boltdir])
|
122
|
+
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
123
|
+
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
124
|
+
else
|
125
|
+
Bolt::Project.create_project(dir)
|
126
|
+
end
|
121
127
|
else
|
122
|
-
Bolt::Project.
|
128
|
+
Bolt::Project.find_boltdir(Dir.pwd)
|
123
129
|
end
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
130
|
+
Bolt::Config.from_project(project, options)
|
131
|
+
end
|
132
|
+
|
133
|
+
Bolt::Logger.configure(config.log, config.color)
|
134
|
+
rescue Bolt::Error => e
|
135
|
+
if $stdout.isatty
|
136
|
+
# Print the error message in red, mimicking outputter.fatal_error
|
137
|
+
$stdout.puts("\033[31m#{e.message}\033[0m")
|
138
|
+
else
|
139
|
+
$stdout.puts(e.message)
|
140
|
+
end
|
141
|
+
raise e
|
142
|
+
end
|
131
143
|
|
132
144
|
# Logger must be configured before checking path case and project file, otherwise warnings will not display
|
133
|
-
|
134
|
-
|
145
|
+
config.check_path_case('modulepath', config.modulepath)
|
146
|
+
config.project.check_deprecated_file
|
135
147
|
|
136
148
|
# Log the file paths for loaded config files
|
137
149
|
config_loaded
|
@@ -281,12 +293,12 @@ module Bolt
|
|
281
293
|
def warn_inventory_overrides_cli(opts)
|
282
294
|
inventory_source = if ENV[Bolt::Inventory::ENVIRONMENT_VAR]
|
283
295
|
Bolt::Inventory::ENVIRONMENT_VAR
|
284
|
-
elsif
|
285
|
-
|
296
|
+
elsif config.inventoryfile && Bolt::Util.file_stat(config.inventoryfile)
|
297
|
+
config.inventoryfile
|
286
298
|
else
|
287
299
|
begin
|
288
|
-
Bolt::Util.file_stat(
|
289
|
-
|
300
|
+
Bolt::Util.file_stat(config.default_inventoryfile)
|
301
|
+
config.default_inventoryfile
|
290
302
|
rescue Errno::ENOENT
|
291
303
|
nil
|
292
304
|
end
|
@@ -390,7 +402,7 @@ module Bolt
|
|
390
402
|
if options[:action] == 'generate-types'
|
391
403
|
code = generate_types
|
392
404
|
elsif options[:action] == 'install'
|
393
|
-
code = install_puppetfile(
|
405
|
+
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath)
|
394
406
|
end
|
395
407
|
when 'secret'
|
396
408
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -802,7 +814,7 @@ module Bolt
|
|
802
814
|
end
|
803
815
|
|
804
816
|
def rerun
|
805
|
-
@rerun ||= Bolt::Rerun.new(
|
817
|
+
@rerun ||= Bolt::Rerun.new(config.rerunfile, config.save_rerun)
|
806
818
|
end
|
807
819
|
|
808
820
|
def outputter
|
data/lib/bolt/config.rb
CHANGED
@@ -28,7 +28,7 @@ module Bolt
|
|
28
28
|
DEFAULT_DEFAULT_CONCURRENCY = 100
|
29
29
|
|
30
30
|
def self.default
|
31
|
-
new(Bolt::Project.
|
31
|
+
new(Bolt::Project.default_project, {})
|
32
32
|
end
|
33
33
|
|
34
34
|
def self.from_project(project, overrides = {})
|
@@ -121,9 +121,22 @@ module Bolt
|
|
121
121
|
)
|
122
122
|
end
|
123
123
|
|
124
|
-
# Move data under
|
125
|
-
# config from other sources.
|
124
|
+
# Move data under inventory-config to top-level so it can be easily merged with
|
125
|
+
# config from other sources. Error early if inventory-config is not a hash or
|
126
|
+
# has a plugin reference.
|
126
127
|
if data.key?('inventory-config')
|
128
|
+
unless data['inventory-config'].is_a?(Hash)
|
129
|
+
raise Bolt::ValidationError,
|
130
|
+
"Option 'inventory-config' must be of type Hash, received #{data['inventory-config']} "\
|
131
|
+
"#{data['inventory-config']} (file: #{filepath})"
|
132
|
+
end
|
133
|
+
|
134
|
+
if data['inventory-config'].key?('_plugin')
|
135
|
+
raise Bolt::ValidationError,
|
136
|
+
"Found unsupported key '_plugin' for option 'inventory-config'; supported keys are "\
|
137
|
+
"'#{INVENTORY_OPTIONS.keys.join("', '")}' (file: #{filepath})"
|
138
|
+
end
|
139
|
+
|
127
140
|
data = data.merge(data.delete('inventory-config'))
|
128
141
|
end
|
129
142
|
|
data/lib/bolt/config/options.rb
CHANGED
@@ -175,7 +175,7 @@ module Bolt
|
|
175
175
|
"log" => {
|
176
176
|
description: "A map of configuration for the logfile output. Under `log`, you can configure log options "\
|
177
177
|
"for `console` and add configuration for individual log files, such as "\
|
178
|
-
"
|
178
|
+
"`~/.puppetlabs/bolt/debug.log`. Individual log files must be valid filepaths. If the log "\
|
179
179
|
"file does not exist, then Bolt will create it before logging information.",
|
180
180
|
type: Hash,
|
181
181
|
properties: {
|
@@ -440,6 +440,7 @@ module Bolt
|
|
440
440
|
concurrency
|
441
441
|
format
|
442
442
|
inventory-config
|
443
|
+
log
|
443
444
|
plugin_hooks
|
444
445
|
plugins
|
445
446
|
puppetdb
|
@@ -108,17 +108,16 @@ module Bolt
|
|
108
108
|
},
|
109
109
|
"copy-command" => {
|
110
110
|
type: [Array, String],
|
111
|
-
description: "The command to use when copying files using
|
111
|
+
description: "The command to use when copying files using native SSH. Bolt runs `<copy-command> <src> "\
|
112
112
|
"<dest>`. This option is used when you need support for features or algorithms that are not "\
|
113
113
|
"supported by the net-ssh Ruby library. **This option is experimental.** You can read more "\
|
114
|
-
"about this option in [
|
115
|
-
"transport](experimental_features.md#external-ssh-transport).",
|
114
|
+
"about this option in [Native SSH transport](experimental_features.md#native-ssh-transport).",
|
116
115
|
items: {
|
117
116
|
type: String
|
118
117
|
},
|
119
118
|
_plugin: true,
|
120
|
-
_default:
|
121
|
-
_example:
|
119
|
+
_default: %w[scp -r],
|
120
|
+
_example: %w[scp -r -F ~/ssh-config/myconf]
|
122
121
|
},
|
123
122
|
"disconnect-timeout" => {
|
124
123
|
type: Integer,
|
@@ -270,6 +269,13 @@ module Bolt
|
|
270
269
|
_plugin: true,
|
271
270
|
_example: %w[defaults hmac-md5]
|
272
271
|
},
|
272
|
+
"native-ssh" => {
|
273
|
+
type: [TrueClass, FalseClass],
|
274
|
+
description: "This enables the native SSH transport, which shells out to SSH instead of using the "\
|
275
|
+
"net-ssh Ruby library",
|
276
|
+
_default: false,
|
277
|
+
_example: true
|
278
|
+
},
|
273
279
|
"password" => {
|
274
280
|
type: String,
|
275
281
|
description: "The password to use to login.",
|
@@ -368,16 +374,16 @@ module Bolt
|
|
368
374
|
},
|
369
375
|
"ssh-command" => {
|
370
376
|
type: [Array, String],
|
371
|
-
description: "The command and flags to use when SSHing. This
|
372
|
-
"shells out to the specified command. This option is used when you need support for "\
|
377
|
+
description: "The command and flags to use when SSHing. This option is used when you need support for "\
|
373
378
|
"features or algorithms that are not supported by the net-ssh Ruby library. **This option "\
|
374
|
-
"is experimental.** You can read more about this option in [
|
375
|
-
"transport](experimental_features.md#
|
379
|
+
"is experimental.** You can read more about this option in [Native SSH "\
|
380
|
+
"transport](experimental_features.md#native-ssh-transport).",
|
376
381
|
items: {
|
377
382
|
type: String
|
378
383
|
},
|
379
384
|
_plugin: true,
|
380
|
-
|
385
|
+
_default: 'ssh',
|
386
|
+
_example: %w[ssh -o Ciphers=chacha20-poly1305@openssh.com]
|
381
387
|
},
|
382
388
|
"ssl" => {
|
383
389
|
type: [TrueClass, FalseClass],
|
@@ -32,13 +32,14 @@ module Bolt
|
|
32
32
|
user
|
33
33
|
].concat(RUN_AS_OPTIONS).sort.freeze
|
34
34
|
|
35
|
-
# Options available when using the
|
36
|
-
|
35
|
+
# Options available when using the native ssh transport
|
36
|
+
NATIVE_OPTIONS = %w[
|
37
37
|
cleanup
|
38
38
|
copy-command
|
39
39
|
host
|
40
40
|
host-key-check
|
41
41
|
interpreters
|
42
|
+
native-ssh
|
42
43
|
port
|
43
44
|
private-key
|
44
45
|
script-dir
|
@@ -56,17 +57,30 @@ module Bolt
|
|
56
57
|
"tty" => false
|
57
58
|
}.freeze
|
58
59
|
|
59
|
-
# The set of options available for the ssh and
|
60
|
+
# The set of options available for the ssh and native ssh transports overlap, so we
|
60
61
|
# need to check which transport is used before fully initializing, otherwise options
|
61
62
|
# may not be filtered correctly.
|
62
63
|
def initialize(data = {}, project = nil)
|
63
64
|
assert_hash_or_config(data)
|
64
|
-
@
|
65
|
+
@native = true if data['native-ssh']
|
65
66
|
super(data, project)
|
66
67
|
end
|
67
68
|
|
69
|
+
# This method is used to filter CLI options in the Config class. This
|
70
|
+
# should include `ssh-command` so that we can later warn if the option
|
71
|
+
# is present without `native-ssh`
|
72
|
+
def self.options
|
73
|
+
%w[ssh-command native-ssh].concat(OPTIONS)
|
74
|
+
end
|
75
|
+
|
68
76
|
private def filter(unfiltered)
|
69
|
-
|
77
|
+
# Because we filter before merging config together it's impossible to
|
78
|
+
# know whether both ssh-command *and* native-ssh will be specified
|
79
|
+
# unless they are both in the filter. However, we can't add
|
80
|
+
# ssh-command to OPTIONS since that's used for documenting available
|
81
|
+
# options. This makes it so that ssh-command is preserved so we can
|
82
|
+
# warn once all config is resolved if native-ssh isn't set.
|
83
|
+
@native ? unfiltered.slice(*NATIVE_OPTIONS) : unfiltered.slice(*self.class.options)
|
70
84
|
end
|
71
85
|
|
72
86
|
private def validate
|
@@ -83,11 +97,11 @@ module Bolt
|
|
83
97
|
@config['private-key'] = File.expand_path(key_opt, @project)
|
84
98
|
|
85
99
|
# We have an explicit test for this to only warn if using net-ssh transport
|
86
|
-
Bolt::Util.validate_file('ssh key', @config['private-key']) if @config['ssh
|
100
|
+
Bolt::Util.validate_file('ssh key', @config['private-key']) if @config['native-ssh']
|
87
101
|
end
|
88
102
|
|
89
|
-
if key_opt.instance_of?(Hash) && @config['ssh
|
90
|
-
raise Bolt::ValidationError, 'private-key must be a filepath when using ssh
|
103
|
+
if key_opt.instance_of?(Hash) && @config['native-ssh']
|
104
|
+
raise Bolt::ValidationError, 'private-key must be a filepath when using native-ssh'
|
91
105
|
end
|
92
106
|
end
|
93
107
|
|
@@ -117,8 +131,8 @@ module Bolt
|
|
117
131
|
end
|
118
132
|
end
|
119
133
|
|
120
|
-
if @config['ssh
|
121
|
-
msg = 'Cannot use
|
134
|
+
if @config['native-ssh'] && !@config['load-config']
|
135
|
+
msg = 'Cannot use native SSH transport with load-config set to false'
|
122
136
|
raise Bolt::ValidationError, msg
|
123
137
|
end
|
124
138
|
end
|
data/lib/bolt/logger.rb
CHANGED
@@ -15,6 +15,7 @@ module Bolt
|
|
15
15
|
return if Logging.initialized?
|
16
16
|
|
17
17
|
Logging.init :debug, :info, :notice, :warn, :error, :fatal, :any
|
18
|
+
@mutex = Mutex.new
|
18
19
|
|
19
20
|
Logging.color_scheme(
|
20
21
|
'bolt',
|
@@ -102,5 +103,16 @@ module Bolt
|
|
102
103
|
def self.reset_logging
|
103
104
|
Logging.reset
|
104
105
|
end
|
106
|
+
|
107
|
+
def self.warn_once(type, msg)
|
108
|
+
@mutex.synchronize {
|
109
|
+
@warnings ||= []
|
110
|
+
@logger ||= Logging.logger[self]
|
111
|
+
unless @warnings.include?(type)
|
112
|
+
@logger.warn(msg)
|
113
|
+
@warnings << type
|
114
|
+
end
|
115
|
+
}
|
116
|
+
end
|
105
117
|
end
|
106
118
|
end
|
data/lib/bolt/project.rb
CHANGED
@@ -31,6 +31,7 @@ module Bolt
|
|
31
31
|
# hierarchy, falling back to the default if we reach the root.
|
32
32
|
def self.find_boltdir(dir)
|
33
33
|
dir = Pathname.new(dir)
|
34
|
+
|
34
35
|
if (dir + BOLTDIR_NAME).directory?
|
35
36
|
create_project(dir + BOLTDIR_NAME, 'embedded')
|
36
37
|
elsif (dir + 'bolt.yaml').file? || (dir + 'bolt-project.yaml').file?
|
@@ -44,6 +45,15 @@ module Bolt
|
|
44
45
|
|
45
46
|
def self.create_project(path, type = 'option')
|
46
47
|
fullpath = Pathname.new(path).expand_path
|
48
|
+
|
49
|
+
if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
|
50
|
+
raise Bolt::Error.new(
|
51
|
+
"Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
|
52
|
+
"BOLT_PROJECT='#{fullpath}' to force the use of this project directory.",
|
53
|
+
"bolt/world-writable-error"
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
47
57
|
project_file = File.join(fullpath, 'bolt-project.yaml')
|
48
58
|
data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
|
49
59
|
new(data, path, type)
|
@@ -51,6 +61,7 @@ module Bolt
|
|
51
61
|
|
52
62
|
def initialize(raw_data, path, type = 'option')
|
53
63
|
@path = Pathname.new(path).expand_path
|
64
|
+
|
54
65
|
@project_file = @path + 'bolt-project.yaml'
|
55
66
|
|
56
67
|
@warnings = []
|
@@ -85,12 +85,21 @@ module Bolt
|
|
85
85
|
|
86
86
|
def shell_init
|
87
87
|
<<~PS
|
88
|
-
$
|
89
|
-
|
90
|
-
|
91
|
-
$
|
92
|
-
|
93
|
-
|
88
|
+
$installRegKey = Get-ItemProperty -Path "HKLM:\\Software\\Puppet Labs\\Puppet" -ErrorAction 0
|
89
|
+
if(![string]::IsNullOrEmpty($installRegKey.RememberedInstallDir64)){
|
90
|
+
$boltBaseDir = $installRegKey.RememberedInstallDir64
|
91
|
+
}elseif(![string]::IsNullOrEmpty($installRegKey.RememberedInstallDir)){
|
92
|
+
$boltBaseDir = $installRegKey.RememberedInstallDir
|
93
|
+
}else{
|
94
|
+
$boltBaseDir = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet"
|
95
|
+
}
|
96
|
+
|
97
|
+
$ENV:PATH += ";${boltBaseDir}\\bin\\;" +
|
98
|
+
"${boltBaseDir}\\puppet\\bin;" +
|
99
|
+
"${boltBaseDir}\\sys\\ruby\\bin\\"
|
100
|
+
$ENV:RUBYLIB = "${boltBaseDir}\\puppet\\lib;" +
|
101
|
+
"${boltBaseDir}\\facter\\lib;" +
|
102
|
+
"${boltBaseDir}\\hiera\\lib;" +
|
94
103
|
$ENV:RUBYLIB
|
95
104
|
|
96
105
|
Add-Type -AssemblyName System.ServiceModel.Web, System.Runtime.Serialization
|
data/lib/bolt/transport/ssh.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bolt/logger'
|
3
4
|
require 'bolt/node/errors'
|
4
5
|
require 'bolt/transport/simple'
|
5
6
|
|
@@ -21,7 +22,12 @@ module Bolt
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def with_connection(target)
|
24
|
-
|
25
|
+
if target.transport_config['ssh-command'] && !target.transport_config['native-ssh']
|
26
|
+
Bolt::Logger.warn_once("ssh-command and native-ssh conflict",
|
27
|
+
"native-ssh must be true to use ssh-command")
|
28
|
+
end
|
29
|
+
|
30
|
+
conn = if target.transport_config['native-ssh']
|
25
31
|
ExecConnection.new(target)
|
26
32
|
else
|
27
33
|
Connection.new(target, @transport_logger)
|
data/lib/bolt/version.rb
CHANGED
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.18.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-07-
|
11
|
+
date: 2020-07-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|