bolt 0.20.7 → 0.21.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/analytics.rb +2 -1
- data/lib/bolt/bolt_option_parser.rb +292 -0
- data/lib/bolt/cli.rb +3 -295
- data/lib/bolt/config.rb +73 -8
- data/lib/bolt/error.rb +6 -0
- data/lib/bolt/inventory.rb +1 -5
- data/lib/bolt/outputter/human.rb +1 -1
- data/lib/bolt/transport/orch.rb +6 -6
- data/lib/bolt/transport/orch/connection.rb +1 -1
- data/lib/bolt/util.rb +8 -3
- data/lib/bolt/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 563c0acfed40d50d62d19a1c3d8acc4ea7ff04ed
|
4
|
+
data.tar.gz: 2233c30b95d4cf03e68a131cfe4e83ea527f720f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94354f851af225ecd5ecc5123fee0aca0844d947685596d605e33f81543a8c5b071e30b2bcf4e872ab188e194dbec13b69dc8850de3761b02604ef2c1f49b997
|
7
|
+
data.tar.gz: 3d9e1463227a2c4ac4d0e80d6c1475e65b299f16d6371bef210f2dcbd090054a78d1590f54358fea416fdf812a7889890fb37b02ad835da81cc6d67b76046de4
|
data/lib/bolt/analytics.rb
CHANGED
@@ -0,0 +1,292 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class BoltOptionParser < OptionParser
|
7
|
+
def self.examples(cmd, desc)
|
8
|
+
<<-EXAMP
|
9
|
+
#{desc} a Windows host via WinRM, providing for the password
|
10
|
+
bolt #{cmd} -n winrm://winhost -u Administrator -p
|
11
|
+
#{desc} the local machine, a Linux host via SSH, and hosts from a group specified in an inventory file
|
12
|
+
bolt #{cmd} -n localhost,nixhost,node_group
|
13
|
+
#{desc} Windows hosts queried from PuppetDB via WinRM as a domain user, prompting for the password
|
14
|
+
bolt #{cmd} -q 'inventory[certname] { facts.os.family = "windows" }' --transport winrm -u 'domain\\Administrator' -p
|
15
|
+
EXAMP
|
16
|
+
end
|
17
|
+
|
18
|
+
BANNER = <<-HELP
|
19
|
+
Usage: bolt <subcommand> <action> [options]
|
20
|
+
|
21
|
+
Available subcommands:
|
22
|
+
bolt command run <command> Run a command remotely
|
23
|
+
bolt file upload <src> <dest> Upload a local file
|
24
|
+
bolt script run <script> Upload a local script and run it remotely
|
25
|
+
bolt task show Show list of available tasks
|
26
|
+
bolt task show <task> Show documentation for task
|
27
|
+
bolt task run <task> [params] Run a Puppet task
|
28
|
+
bolt plan show Show list of available plans
|
29
|
+
bolt plan show <plan> Show details for plan
|
30
|
+
bolt plan run <plan> [params] Run a Puppet task plan
|
31
|
+
|
32
|
+
Run `bolt <subcommand> --help` to view specific examples.
|
33
|
+
|
34
|
+
where [options] are:
|
35
|
+
HELP
|
36
|
+
|
37
|
+
TASK_HELP = <<-HELP
|
38
|
+
Usage: bolt task <action> <task> [options] [parameters]
|
39
|
+
|
40
|
+
Available actions are:
|
41
|
+
show Show list of available tasks
|
42
|
+
show <task> Show documentation for task
|
43
|
+
run Run a Puppet task
|
44
|
+
|
45
|
+
Parameters are of the form <parameter>=<value>.
|
46
|
+
|
47
|
+
#{examples('task run facts', 'run facter on')}
|
48
|
+
Available options are:
|
49
|
+
HELP
|
50
|
+
|
51
|
+
COMMAND_HELP = <<-HELP
|
52
|
+
Usage: bolt command <action> <command> [options]
|
53
|
+
|
54
|
+
Available actions are:
|
55
|
+
run Run a command remotely
|
56
|
+
|
57
|
+
#{examples('command run hostname', 'run hostname on')}
|
58
|
+
Available options are:
|
59
|
+
HELP
|
60
|
+
|
61
|
+
SCRIPT_HELP = <<-HELP
|
62
|
+
Usage: bolt script <action> <script> [[arg1] ... [argN]] [options]
|
63
|
+
|
64
|
+
Available actions are:
|
65
|
+
run Upload a local script and run it remotely
|
66
|
+
|
67
|
+
#{examples('script run my_script.ps1 some args', 'run a script on')}
|
68
|
+
Available options are:
|
69
|
+
HELP
|
70
|
+
|
71
|
+
PLAN_HELP = <<-HELP
|
72
|
+
Usage: bolt plan <action> <plan> [options] [parameters]
|
73
|
+
|
74
|
+
Available actions are:
|
75
|
+
show Show list of available plans
|
76
|
+
show <plan> Show details for plan
|
77
|
+
run Run a Puppet task plan
|
78
|
+
|
79
|
+
Parameters are of the form <parameter>=<value>.
|
80
|
+
|
81
|
+
#{examples('plan run canary command=hostname', 'run the canary plan on')}
|
82
|
+
Available options are:
|
83
|
+
HELP
|
84
|
+
|
85
|
+
FILE_HELP = <<-HELP
|
86
|
+
Usage: bolt file <action> [options]
|
87
|
+
|
88
|
+
Available actions are:
|
89
|
+
upload <src> <dest> Upload local file <src> to <dest> on each node
|
90
|
+
|
91
|
+
#{examples('file upload /tmp/source /etc/profile.d/login.sh', 'upload a file to')}
|
92
|
+
Available options are:
|
93
|
+
HELP
|
94
|
+
|
95
|
+
# A helper mixin for OptionParser::Switch instances which will allow
|
96
|
+
# us to show/hide particular switch in the help message produced by
|
97
|
+
# the OptionParser#help method on demand.
|
98
|
+
module SwitchHider
|
99
|
+
attr_accessor :hide
|
100
|
+
|
101
|
+
def summarize(*args)
|
102
|
+
return self if hide
|
103
|
+
super
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def initialize(options)
|
108
|
+
super()
|
109
|
+
|
110
|
+
@options = options
|
111
|
+
|
112
|
+
@nodes = define('-n', '--nodes NODES',
|
113
|
+
'Identifies the nodes to target.',
|
114
|
+
'Enter a comma-separated list of node URIs or group names.',
|
115
|
+
"Or read a node list from an input file '@<file>' or stdin '-'.",
|
116
|
+
'Example: --nodes localhost,node_group,ssh://nix.com:23,winrm://windows.puppet.com',
|
117
|
+
'URI format is [protocol://]host[:port]',
|
118
|
+
"SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}",
|
119
|
+
'For Windows nodes, specify the winrm:// protocol if it has not be configured',
|
120
|
+
'For SSH, port defaults to `22`',
|
121
|
+
'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |nodes|
|
122
|
+
@options[:nodes] << get_arg_input(nodes)
|
123
|
+
end.extend(SwitchHider)
|
124
|
+
@query = define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
|
125
|
+
@options[:query] = query
|
126
|
+
end.extend(SwitchHider)
|
127
|
+
define('--noop', 'Execute a task that supports it in noop mode') do |_|
|
128
|
+
@options[:noop] = true
|
129
|
+
end
|
130
|
+
define('--description DESCRIPTION',
|
131
|
+
'Description to use for the job') do |description|
|
132
|
+
@options[:description] = description
|
133
|
+
end
|
134
|
+
define('--params PARAMETERS',
|
135
|
+
"Parameters to a task or plan as json, a json file '@<file>', or on stdin '-'") do |params|
|
136
|
+
@options[:task_options] = parse_params(params)
|
137
|
+
end
|
138
|
+
|
139
|
+
separator 'Authentication:'
|
140
|
+
define('-u', '--user USER', 'User to authenticate as') do |user|
|
141
|
+
@options[:user] = user
|
142
|
+
end
|
143
|
+
define('-p', '--password [PASSWORD]',
|
144
|
+
'Password to authenticate with. Omit the value to prompt for the password.') do |password|
|
145
|
+
if password.nil?
|
146
|
+
STDOUT.print "Please enter your password: "
|
147
|
+
@options[:password] = STDIN.noecho(&:gets).chomp
|
148
|
+
STDOUT.puts
|
149
|
+
else
|
150
|
+
@options[:password] = password
|
151
|
+
end
|
152
|
+
end
|
153
|
+
define('--private-key KEY', 'Private ssh key to authenticate with') do |key|
|
154
|
+
@options[:'private-key'] = key
|
155
|
+
end
|
156
|
+
define('--[no-]host-key-check', 'Check host keys with SSH') do |host_key_check|
|
157
|
+
@options[:'host-key-check'] = host_key_check
|
158
|
+
end
|
159
|
+
define('--[no-]ssl', 'Use SSL with WinRM') do |ssl|
|
160
|
+
@options[:ssl] = ssl
|
161
|
+
end
|
162
|
+
define('--[no-]ssl-verify', 'Verify remote host SSL certificate with WinRM') do |ssl_verify|
|
163
|
+
@options[:'ssl-verify'] = ssl_verify
|
164
|
+
end
|
165
|
+
|
166
|
+
separator 'Escalation:'
|
167
|
+
define('--run-as USER', 'User to run as using privilege escalation') do |user|
|
168
|
+
@options[:'run-as'] = user
|
169
|
+
end
|
170
|
+
define('--sudo-password [PASSWORD]',
|
171
|
+
'Password for privilege escalation. Omit the value to prompt for the password.') do |password|
|
172
|
+
if password.nil?
|
173
|
+
STDOUT.print "Please enter your privilege escalation password: "
|
174
|
+
@options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
|
175
|
+
STDOUT.puts
|
176
|
+
else
|
177
|
+
@options[:'sudo-password'] = password
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
separator 'Run context:'
|
182
|
+
define('-c', '--concurrency CONCURRENCY', Integer,
|
183
|
+
'Maximum number of simultaneous connections (default: 100)') do |concurrency|
|
184
|
+
@options[:concurrency] = concurrency
|
185
|
+
end
|
186
|
+
define('--modulepath MODULES',
|
187
|
+
"List of directories containing modules, separated by '#{File::PATH_SEPARATOR}'") do |modulepath|
|
188
|
+
@options[:modulepath] = modulepath.split(File::PATH_SEPARATOR)
|
189
|
+
end
|
190
|
+
define('--boltdir FILEPATH',
|
191
|
+
'Specify what Boltdir to load config from (default: autodiscovered from current working dir)') do |path|
|
192
|
+
@options[:configfile] = path
|
193
|
+
end
|
194
|
+
define('--configfile FILEPATH',
|
195
|
+
'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml)') do |path|
|
196
|
+
@options[:configfile] = path
|
197
|
+
end
|
198
|
+
define('--inventoryfile FILEPATH',
|
199
|
+
'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
|
200
|
+
if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
|
201
|
+
raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
|
202
|
+
end
|
203
|
+
@options[:inventoryfile] = path
|
204
|
+
end
|
205
|
+
|
206
|
+
separator 'Transports:'
|
207
|
+
define('--transport TRANSPORT', TRANSPORTS.keys.map(&:to_s),
|
208
|
+
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
209
|
+
@options[:transport] = t
|
210
|
+
end
|
211
|
+
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
212
|
+
@options[:'connect-timeout'] = timeout
|
213
|
+
end
|
214
|
+
define('--[no-]tty', 'Request a pseudo TTY on nodes that support it') do |tty|
|
215
|
+
@options[:tty] = tty
|
216
|
+
end
|
217
|
+
define('--tmpdir DIR', 'The directory to upload and execute temporary files on the target') do |tmpdir|
|
218
|
+
@options[:tmpdir] = tmpdir
|
219
|
+
end
|
220
|
+
|
221
|
+
separator 'Display:'
|
222
|
+
define('--format FORMAT', 'Output format to use: human or json') do |format|
|
223
|
+
@options[:format] = format
|
224
|
+
end
|
225
|
+
define('--[no-]color', 'Whether to show output in color') do |color|
|
226
|
+
@options[:color] = color
|
227
|
+
end
|
228
|
+
define('-h', '--help', 'Display help') do |_|
|
229
|
+
@options[:help] = true
|
230
|
+
end
|
231
|
+
define('--verbose', 'Display verbose logging') do |_|
|
232
|
+
@options[:verbose] = true
|
233
|
+
end
|
234
|
+
define('--debug', 'Display debug logging') do |_|
|
235
|
+
@options[:debug] = true
|
236
|
+
end
|
237
|
+
define('--version', 'Display the version') do |_|
|
238
|
+
puts Bolt::VERSION
|
239
|
+
raise Bolt::CLIExit
|
240
|
+
end
|
241
|
+
|
242
|
+
update
|
243
|
+
end
|
244
|
+
|
245
|
+
def update
|
246
|
+
# show the --nodes and --query switches by default
|
247
|
+
@nodes.hide = @query.hide = false
|
248
|
+
|
249
|
+
# Update the banner according to the mode
|
250
|
+
self.banner = case @options[:mode]
|
251
|
+
when 'plan'
|
252
|
+
# don't show the --nodes and --query switches in the plan help
|
253
|
+
@nodes.hide = @query.hide = true
|
254
|
+
PLAN_HELP
|
255
|
+
when 'command'
|
256
|
+
COMMAND_HELP
|
257
|
+
when 'script'
|
258
|
+
SCRIPT_HELP
|
259
|
+
when 'task'
|
260
|
+
TASK_HELP
|
261
|
+
when 'file'
|
262
|
+
FILE_HELP
|
263
|
+
else
|
264
|
+
BANNER
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def parse_params(params)
|
269
|
+
json = get_arg_input(params)
|
270
|
+
JSON.parse(json)
|
271
|
+
rescue JSON::ParserError => err
|
272
|
+
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{err}"
|
273
|
+
end
|
274
|
+
|
275
|
+
def get_arg_input(value)
|
276
|
+
if value.start_with?('@')
|
277
|
+
file = value.sub(/^@/, '')
|
278
|
+
read_arg_file(file)
|
279
|
+
elsif value == '-'
|
280
|
+
STDIN.read
|
281
|
+
else
|
282
|
+
value
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def read_arg_file(file)
|
287
|
+
File.read(file)
|
288
|
+
rescue StandardError => err
|
289
|
+
raise Bolt::FileError.new("Error attempting to read #{file}: #{err}", file)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
data/lib/bolt/cli.rb
CHANGED
@@ -7,6 +7,7 @@ require 'io/console'
|
|
7
7
|
require 'logging'
|
8
8
|
require 'optparse'
|
9
9
|
require 'bolt/analytics'
|
10
|
+
require 'bolt/bolt_option_parser'
|
10
11
|
require 'bolt/config'
|
11
12
|
require 'bolt/error'
|
12
13
|
require 'bolt/executor'
|
@@ -20,298 +21,8 @@ require 'bolt/version'
|
|
20
21
|
require 'bolt/util/on_access'
|
21
22
|
|
22
23
|
module Bolt
|
23
|
-
class CLIError < Bolt::Error
|
24
|
-
def initialize(msg)
|
25
|
-
super(msg, "bolt/cli-error")
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
24
|
class CLIExit < StandardError; end
|
30
|
-
|
31
25
|
class CLI
|
32
|
-
class BoltOptionParser < OptionParser
|
33
|
-
def self.examples(cmd, desc)
|
34
|
-
<<-EXAMP
|
35
|
-
#{desc} a Windows host via WinRM, providing for the password
|
36
|
-
bolt #{cmd} -n winrm://winhost -u Administrator -p
|
37
|
-
#{desc} the local machine, a Linux host via SSH, and hosts from a group specified in an inventory file
|
38
|
-
bolt #{cmd} -n localhost,nixhost,node_group
|
39
|
-
#{desc} Windows hosts queried from PuppetDB via WinRM as a domain user, prompting for the password
|
40
|
-
bolt #{cmd} -q 'inventory[certname] { facts.os.family = "windows" }' --transport winrm -u 'domain\\Administrator' -p
|
41
|
-
EXAMP
|
42
|
-
end
|
43
|
-
|
44
|
-
BANNER = <<-HELP
|
45
|
-
Usage: bolt <subcommand> <action> [options]
|
46
|
-
|
47
|
-
Available subcommands:
|
48
|
-
bolt command run <command> Run a command remotely
|
49
|
-
bolt file upload <src> <dest> Upload a local file
|
50
|
-
bolt script run <script> Upload a local script and run it remotely
|
51
|
-
bolt task show Show list of available tasks
|
52
|
-
bolt task show <task> Show documentation for task
|
53
|
-
bolt task run <task> [params] Run a Puppet task
|
54
|
-
bolt plan show Show list of available plans
|
55
|
-
bolt plan show <plan> Show details for plan
|
56
|
-
bolt plan run <plan> [params] Run a Puppet task plan
|
57
|
-
|
58
|
-
Run `bolt <subcommand> --help` to view specific examples.
|
59
|
-
|
60
|
-
where [options] are:
|
61
|
-
HELP
|
62
|
-
|
63
|
-
TASK_HELP = <<-HELP
|
64
|
-
Usage: bolt task <action> <task> [options] [parameters]
|
65
|
-
|
66
|
-
Available actions are:
|
67
|
-
show Show list of available tasks
|
68
|
-
show <task> Show documentation for task
|
69
|
-
run Run a Puppet task
|
70
|
-
|
71
|
-
Parameters are of the form <parameter>=<value>.
|
72
|
-
|
73
|
-
#{examples('task run facts', 'run facter on')}
|
74
|
-
Available options are:
|
75
|
-
HELP
|
76
|
-
|
77
|
-
COMMAND_HELP = <<-HELP
|
78
|
-
Usage: bolt command <action> <command> [options]
|
79
|
-
|
80
|
-
Available actions are:
|
81
|
-
run Run a command remotely
|
82
|
-
|
83
|
-
#{examples('command run hostname', 'run hostname on')}
|
84
|
-
Available options are:
|
85
|
-
HELP
|
86
|
-
|
87
|
-
SCRIPT_HELP = <<-HELP
|
88
|
-
Usage: bolt script <action> <script> [[arg1] ... [argN]] [options]
|
89
|
-
|
90
|
-
Available actions are:
|
91
|
-
run Upload a local script and run it remotely
|
92
|
-
|
93
|
-
#{examples('script run my_script.ps1 some args', 'run a script on')}
|
94
|
-
Available options are:
|
95
|
-
HELP
|
96
|
-
|
97
|
-
PLAN_HELP = <<-HELP
|
98
|
-
Usage: bolt plan <action> <plan> [options] [parameters]
|
99
|
-
|
100
|
-
Available actions are:
|
101
|
-
show Show list of available plans
|
102
|
-
show <plan> Show details for plan
|
103
|
-
run Run a Puppet task plan
|
104
|
-
|
105
|
-
Parameters are of the form <parameter>=<value>.
|
106
|
-
|
107
|
-
#{examples('plan run canary command=hostname', 'run the canary plan on')}
|
108
|
-
Available options are:
|
109
|
-
HELP
|
110
|
-
|
111
|
-
FILE_HELP = <<-HELP
|
112
|
-
Usage: bolt file <action> [options]
|
113
|
-
|
114
|
-
Available actions are:
|
115
|
-
upload <src> <dest> Upload local file <src> to <dest> on each node
|
116
|
-
|
117
|
-
#{examples('file upload /tmp/source /etc/profile.d/login.sh', 'upload a file to')}
|
118
|
-
Available options are:
|
119
|
-
HELP
|
120
|
-
|
121
|
-
# A helper mixin for OptionParser::Switch instances which will allow
|
122
|
-
# us to show/hide particular switch in the help message produced by
|
123
|
-
# the OptionParser#help method on demand.
|
124
|
-
module SwitchHider
|
125
|
-
attr_accessor :hide
|
126
|
-
|
127
|
-
def summarize(*args)
|
128
|
-
return self if hide
|
129
|
-
super
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def initialize(options)
|
134
|
-
super()
|
135
|
-
|
136
|
-
@options = options
|
137
|
-
|
138
|
-
@nodes = define('-n', '--nodes NODES',
|
139
|
-
'Identifies the nodes to target.',
|
140
|
-
'Enter a comma-separated list of node URIs or group names.',
|
141
|
-
"Or read a node list from an input file '@<file>' or stdin '-'.",
|
142
|
-
'Example: --nodes localhost,node_group,ssh://nix.com:23,winrm://windows.puppet.com',
|
143
|
-
'URI format is [protocol://]host[:port]',
|
144
|
-
"SSH is the default protocol; may be #{TRANSPORTS.keys.join(', ')}",
|
145
|
-
'For Windows nodes, specify the winrm:// protocol if it has not be configured',
|
146
|
-
'For SSH, port defaults to `22`',
|
147
|
-
'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |nodes|
|
148
|
-
@options[:nodes] << get_arg_input(nodes)
|
149
|
-
end.extend(SwitchHider)
|
150
|
-
@query = define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
|
151
|
-
@options[:query] = query
|
152
|
-
end.extend(SwitchHider)
|
153
|
-
define('--noop', 'Execute a task that supports it in noop mode') do |_|
|
154
|
-
@options[:noop] = true
|
155
|
-
end
|
156
|
-
define('--description DESCRIPTION',
|
157
|
-
'Description to use for the job') do |description|
|
158
|
-
@options[:description] = description
|
159
|
-
end
|
160
|
-
define('--params PARAMETERS',
|
161
|
-
"Parameters to a task or plan as json, a json file '@<file>', or on stdin '-'") do |params|
|
162
|
-
@options[:task_options] = parse_params(params)
|
163
|
-
end
|
164
|
-
|
165
|
-
separator 'Authentication:'
|
166
|
-
define('-u', '--user USER', 'User to authenticate as') do |user|
|
167
|
-
@options[:user] = user
|
168
|
-
end
|
169
|
-
define('-p', '--password [PASSWORD]',
|
170
|
-
'Password to authenticate with. Omit the value to prompt for the password.') do |password|
|
171
|
-
if password.nil?
|
172
|
-
STDOUT.print "Please enter your password: "
|
173
|
-
@options[:password] = STDIN.noecho(&:gets).chomp
|
174
|
-
STDOUT.puts
|
175
|
-
else
|
176
|
-
@options[:password] = password
|
177
|
-
end
|
178
|
-
end
|
179
|
-
define('--private-key KEY', 'Private ssh key to authenticate with') do |key|
|
180
|
-
@options[:'private-key'] = key
|
181
|
-
end
|
182
|
-
define('--[no-]host-key-check', 'Check host keys with SSH') do |host_key_check|
|
183
|
-
@options[:'host-key-check'] = host_key_check
|
184
|
-
end
|
185
|
-
define('--[no-]ssl', 'Use SSL with WinRM') do |ssl|
|
186
|
-
@options[:ssl] = ssl
|
187
|
-
end
|
188
|
-
define('--[no-]ssl-verify', 'Verify remote host SSL certificate with WinRM') do |ssl_verify|
|
189
|
-
@options[:'ssl-verify'] = ssl_verify
|
190
|
-
end
|
191
|
-
|
192
|
-
separator 'Escalation:'
|
193
|
-
define('--run-as USER', 'User to run as using privilege escalation') do |user|
|
194
|
-
@options[:'run-as'] = user
|
195
|
-
end
|
196
|
-
define('--sudo-password [PASSWORD]',
|
197
|
-
'Password for privilege escalation. Omit the value to prompt for the password.') do |password|
|
198
|
-
if password.nil?
|
199
|
-
STDOUT.print "Please enter your privilege escalation password: "
|
200
|
-
@options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
|
201
|
-
STDOUT.puts
|
202
|
-
else
|
203
|
-
@options[:'sudo-password'] = password
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
separator 'Run context:'
|
208
|
-
define('-c', '--concurrency CONCURRENCY', Integer,
|
209
|
-
'Maximum number of simultaneous connections (default: 100)') do |concurrency|
|
210
|
-
@options[:concurrency] = concurrency
|
211
|
-
end
|
212
|
-
define('--modulepath MODULES',
|
213
|
-
"List of directories containing modules, separated by '#{File::PATH_SEPARATOR}'") do |modulepath|
|
214
|
-
@options[:modulepath] = modulepath.split(File::PATH_SEPARATOR)
|
215
|
-
end
|
216
|
-
define('--configfile FILEPATH',
|
217
|
-
'Specify where to load config from (default: ~/.puppetlabs/bolt.yaml)') do |path|
|
218
|
-
@options[:configfile] = path
|
219
|
-
end
|
220
|
-
define('--inventoryfile FILEPATH',
|
221
|
-
'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
|
222
|
-
if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
|
223
|
-
raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
|
224
|
-
end
|
225
|
-
@options[:inventoryfile] = path
|
226
|
-
end
|
227
|
-
|
228
|
-
separator 'Transports:'
|
229
|
-
define('--transport TRANSPORT', TRANSPORTS.keys.map(&:to_s),
|
230
|
-
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
231
|
-
@options[:transport] = t
|
232
|
-
end
|
233
|
-
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
234
|
-
@options[:'connect-timeout'] = timeout
|
235
|
-
end
|
236
|
-
define('--[no-]tty', 'Request a pseudo TTY on nodes that support it') do |tty|
|
237
|
-
@options[:tty] = tty
|
238
|
-
end
|
239
|
-
define('--tmpdir DIR', 'The directory to upload and execute temporary files on the target') do |tmpdir|
|
240
|
-
@options[:tmpdir] = tmpdir
|
241
|
-
end
|
242
|
-
|
243
|
-
separator 'Display:'
|
244
|
-
define('--format FORMAT', 'Output format to use: human or json') do |format|
|
245
|
-
@options[:format] = format
|
246
|
-
end
|
247
|
-
define('--[no-]color', 'Whether to show output in color') do |color|
|
248
|
-
@options[:color] = color
|
249
|
-
end
|
250
|
-
define('-h', '--help', 'Display help') do |_|
|
251
|
-
@options[:help] = true
|
252
|
-
end
|
253
|
-
define('--verbose', 'Display verbose logging') do |_|
|
254
|
-
@options[:verbose] = true
|
255
|
-
end
|
256
|
-
define('--debug', 'Display debug logging') do |_|
|
257
|
-
@options[:debug] = true
|
258
|
-
end
|
259
|
-
define('--version', 'Display the version') do |_|
|
260
|
-
puts Bolt::VERSION
|
261
|
-
raise Bolt::CLIExit
|
262
|
-
end
|
263
|
-
|
264
|
-
update
|
265
|
-
end
|
266
|
-
|
267
|
-
def update
|
268
|
-
# show the --nodes and --query switches by default
|
269
|
-
@nodes.hide = @query.hide = false
|
270
|
-
|
271
|
-
# Update the banner according to the mode
|
272
|
-
self.banner = case @options[:mode]
|
273
|
-
when 'plan'
|
274
|
-
# don't show the --nodes and --query switches in the plan help
|
275
|
-
@nodes.hide = @query.hide = true
|
276
|
-
PLAN_HELP
|
277
|
-
when 'command'
|
278
|
-
COMMAND_HELP
|
279
|
-
when 'script'
|
280
|
-
SCRIPT_HELP
|
281
|
-
when 'task'
|
282
|
-
TASK_HELP
|
283
|
-
when 'file'
|
284
|
-
FILE_HELP
|
285
|
-
else
|
286
|
-
BANNER
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
def parse_params(params)
|
291
|
-
json = get_arg_input(params)
|
292
|
-
JSON.parse(json)
|
293
|
-
rescue JSON::ParserError => err
|
294
|
-
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{err}"
|
295
|
-
end
|
296
|
-
|
297
|
-
def get_arg_input(value)
|
298
|
-
if value.start_with?('@')
|
299
|
-
file = value.sub(/^@/, '')
|
300
|
-
read_arg_file(file)
|
301
|
-
elsif value == '-'
|
302
|
-
STDIN.read
|
303
|
-
else
|
304
|
-
value
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
def read_arg_file(file)
|
309
|
-
File.read(file)
|
310
|
-
rescue StandardError => err
|
311
|
-
raise Bolt::FileError.new("Error attempting to read #{file}: #{err}", file)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
26
|
COMMANDS = { 'command' => %w[run],
|
316
27
|
'script' => %w[run],
|
317
28
|
'task' => %w[show run],
|
@@ -323,11 +34,8 @@ Available options are:
|
|
323
34
|
def initialize(argv)
|
324
35
|
Bolt::Logger.initialize_logging
|
325
36
|
@logger = Logging.logger[self]
|
326
|
-
|
327
37
|
@config = Bolt::Config.new
|
328
|
-
|
329
38
|
@argv = argv
|
330
|
-
|
331
39
|
@options = {
|
332
40
|
nodes: []
|
333
41
|
}
|
@@ -365,8 +73,7 @@ Available options are:
|
|
365
73
|
raise Bolt::CLIExit
|
366
74
|
end
|
367
75
|
|
368
|
-
config.
|
369
|
-
config.update_from_cli(options)
|
76
|
+
config.update(options)
|
370
77
|
config.validate
|
371
78
|
Bolt::Logger.configure(config)
|
372
79
|
|
@@ -503,6 +210,7 @@ Available options are:
|
|
503
210
|
end
|
504
211
|
|
505
212
|
@analytics.screen_view(screen,
|
213
|
+
output_format: config[:format],
|
506
214
|
target_nodes: options.fetch(:targets, []).count,
|
507
215
|
inventory_nodes: inventory.node_names.count,
|
508
216
|
inventory_groups: inventory.group_names.count)
|
data/lib/bolt/config.rb
CHANGED
@@ -68,9 +68,13 @@ module Bolt
|
|
68
68
|
local: {}
|
69
69
|
}.freeze
|
70
70
|
|
71
|
+
BOLTDIR_NAME = 'Boltdir'
|
72
|
+
|
71
73
|
def initialize(**kwargs)
|
72
74
|
super()
|
73
75
|
@logger = Logging.logger[self]
|
76
|
+
@pwd = kwargs.delete(:pwd)
|
77
|
+
|
74
78
|
DEFAULTS.merge(kwargs).each { |k, v| self[k] = v }
|
75
79
|
|
76
80
|
# add an entry for the default console logger
|
@@ -99,11 +103,6 @@ module Bolt
|
|
99
103
|
Bolt::Util.deep_clone(self)
|
100
104
|
end
|
101
105
|
|
102
|
-
def default_paths
|
103
|
-
root_path = File.expand_path(File.join('~', '.puppetlabs'))
|
104
|
-
[File.join(root_path, 'bolt.yaml'), File.join(root_path, 'bolt.yml')]
|
105
|
-
end
|
106
|
-
|
107
106
|
def normalize_log(target)
|
108
107
|
return target if target == 'console'
|
109
108
|
target = target[5..-1] if target.start_with?('file:')
|
@@ -146,9 +145,55 @@ module Bolt
|
|
146
145
|
end
|
147
146
|
private :update_from_file
|
148
147
|
|
149
|
-
def
|
150
|
-
|
151
|
-
|
148
|
+
def find_boltdir(dir)
|
149
|
+
path = dir
|
150
|
+
boltdir = nil
|
151
|
+
while boltdir.nil? && path && path != File.dirname(path)
|
152
|
+
maybe_boltdir = File.join(path, BOLTDIR_NAME)
|
153
|
+
boltdir = maybe_boltdir if File.directory?(maybe_boltdir)
|
154
|
+
path = File.dirname(path)
|
155
|
+
end
|
156
|
+
boltdir
|
157
|
+
end
|
158
|
+
|
159
|
+
def pwd
|
160
|
+
@pwd ||= Dir.pwd
|
161
|
+
end
|
162
|
+
|
163
|
+
def boltdir
|
164
|
+
@boltdir ||= find_boltdir(pwd) || default_boltdir
|
165
|
+
end
|
166
|
+
|
167
|
+
def default_boltdir
|
168
|
+
File.expand_path(File.join('~', '.puppetlabs', 'bolt'))
|
169
|
+
end
|
170
|
+
|
171
|
+
def default_modulepath
|
172
|
+
[File.join(boltdir, "modules")]
|
173
|
+
end
|
174
|
+
|
175
|
+
# TODO: This is deprecated in 0.21.0 and can be removed in release 0.22.0.
|
176
|
+
def legacy_conf
|
177
|
+
return @legacy_conf if defined?(@legacy_conf)
|
178
|
+
root_path = File.expand_path(File.join('~', '.puppetlabs'))
|
179
|
+
legacy_paths = [File.join(root_path, 'bolt.yaml'), File.join(root_path, 'bolt.yml')]
|
180
|
+
@legacy_conf = legacy_paths.find { |path| File.exist?(path) }
|
181
|
+
@legacy_conf ||= legacy_paths[0]
|
182
|
+
if @legacy_conf
|
183
|
+
correct_path = File.join(default_boltdir, 'bolt.yaml')
|
184
|
+
msg = "Found configfile at deprecated location #{@legacy_conf}. Global config should be in #{correct_path}"
|
185
|
+
@logger.warn(msg)
|
186
|
+
end
|
187
|
+
@legacy_conf
|
188
|
+
end
|
189
|
+
|
190
|
+
def default_config
|
191
|
+
path = File.join(boltdir, 'bolt.yaml')
|
192
|
+
File.exist?(path) ? path : legacy_conf
|
193
|
+
end
|
194
|
+
|
195
|
+
def default_inventory
|
196
|
+
File.join(boltdir, 'inventory.yaml')
|
152
197
|
end
|
153
198
|
|
154
199
|
def update_from_cli(options)
|
@@ -184,6 +229,26 @@ module Bolt
|
|
184
229
|
end
|
185
230
|
end
|
186
231
|
|
232
|
+
# Defaults that do not vary based on boltdir should not be included here.
|
233
|
+
#
|
234
|
+
# Defaults which are treated differently from specified values like
|
235
|
+
# 'inventoryfile' cannot be included here or they will not be handled correctly.
|
236
|
+
def update_from_defaults
|
237
|
+
self[:modulepath] = default_modulepath
|
238
|
+
end
|
239
|
+
|
240
|
+
# The order in which config is processed is important
|
241
|
+
def update(options)
|
242
|
+
update_from_defaults
|
243
|
+
load_file(options[:configfile])
|
244
|
+
update_from_cli(options)
|
245
|
+
end
|
246
|
+
|
247
|
+
def load_file(path)
|
248
|
+
data = Bolt::Util.read_config_file(path, [default_config], 'config')
|
249
|
+
update_from_file(data) if data
|
250
|
+
end
|
251
|
+
|
187
252
|
def update_from_inventory(data)
|
188
253
|
update_from_file(data)
|
189
254
|
|
data/lib/bolt/error.rb
CHANGED
data/lib/bolt/inventory.rb
CHANGED
@@ -36,10 +36,6 @@ module Bolt
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
def self.default_paths
|
40
|
-
[File.expand_path(File.join('~', '.puppetlabs', 'bolt', 'inventory.yaml'))]
|
41
|
-
end
|
42
|
-
|
43
39
|
def self.from_config(config)
|
44
40
|
if ENV.include?(ENVIRONMENT_VAR)
|
45
41
|
begin
|
@@ -48,7 +44,7 @@ module Bolt
|
|
48
44
|
raise Bolt::Error.new("Could not parse inventory from $#{ENVIRONMENT_VAR}", 'bolt/parse-error')
|
49
45
|
end
|
50
46
|
else
|
51
|
-
data = Bolt::Util.read_config_file(config[:inventoryfile],
|
47
|
+
data = Bolt::Util.read_config_file(config[:inventoryfile], [config.default_inventory], 'inventory')
|
52
48
|
end
|
53
49
|
|
54
50
|
inventory = new(data, config)
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -117,7 +117,7 @@ module Bolt
|
|
117
117
|
# Building lots of strings...
|
118
118
|
pretty_params = +""
|
119
119
|
task_info = +""
|
120
|
-
usage = +"bolt task run --nodes
|
120
|
+
usage = +"bolt task run --nodes <node-name> #{task['name']}"
|
121
121
|
|
122
122
|
if task['parameters']
|
123
123
|
replace_data_type(task['parameters'])
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -87,7 +87,7 @@ module Bolt
|
|
87
87
|
|
88
88
|
def batch_command(targets, command, options = {}, &callback)
|
89
89
|
params = {
|
90
|
-
command
|
90
|
+
'command' => command
|
91
91
|
}
|
92
92
|
results = run_task_job(targets,
|
93
93
|
BOLT_COMMAND_TASK,
|
@@ -105,8 +105,8 @@ module Bolt
|
|
105
105
|
content = File.open(script, &:read)
|
106
106
|
content = Base64.encode64(content)
|
107
107
|
params = {
|
108
|
-
content
|
109
|
-
arguments
|
108
|
+
'content' => content,
|
109
|
+
'arguments' => arguments
|
110
110
|
}
|
111
111
|
callback ||= proc {}
|
112
112
|
results = run_task_job(targets, BOLT_SCRIPT_TASK, params, options, &callback)
|
@@ -121,9 +121,9 @@ module Bolt
|
|
121
121
|
content = Base64.encode64(content)
|
122
122
|
mode = File.stat(source).mode
|
123
123
|
params = {
|
124
|
-
path
|
125
|
-
content
|
126
|
-
mode
|
124
|
+
'path' => destination,
|
125
|
+
'content' => content,
|
126
|
+
'mode' => mode
|
127
127
|
}
|
128
128
|
callback ||= proc {}
|
129
129
|
results = run_task_job(targets, BOLT_UPLOAD_TASK, params, options, &callback)
|
@@ -62,7 +62,7 @@ module Bolt
|
|
62
62
|
body = { task: task.name,
|
63
63
|
environment: @environment,
|
64
64
|
noop: arguments['_noop'],
|
65
|
-
params: arguments.reject { |k, _| k
|
65
|
+
params: arguments.reject { |k, _| k.start_with?('_') },
|
66
66
|
scope: {
|
67
67
|
nodes: targets.map(&:host)
|
68
68
|
} }
|
data/lib/bolt/util.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module Bolt
|
4
4
|
module Util
|
5
|
-
# CODEREVIEW I hate mixing in random modules
|
6
5
|
class << self
|
7
6
|
def read_config_file(path, default_paths = nil, file_name = 'file')
|
8
7
|
logger = Logging.logger[self]
|
@@ -17,10 +16,16 @@ module Bolt
|
|
17
16
|
end
|
18
17
|
|
19
18
|
path = File.expand_path(path)
|
20
|
-
File.open(path, "r:UTF-8") { |f| YAML.safe_load(f.read) }
|
19
|
+
content = File.open(path, "r:UTF-8") { |f| YAML.safe_load(f.read) }
|
20
|
+
logger.debug("Loaded #{file_name} from #{path}")
|
21
|
+
content
|
21
22
|
rescue Errno::ENOENT
|
23
|
+
msg = "Could not read #{file_name} file: #{path}"
|
22
24
|
if path_passed
|
23
|
-
raise Bolt::FileError.new(
|
25
|
+
raise Bolt::FileError.new(msg, path)
|
26
|
+
else
|
27
|
+
logger.debug(msg)
|
28
|
+
nil
|
24
29
|
end
|
25
30
|
rescue Psych::Exception
|
26
31
|
raise Bolt::FileError.new("Could not parse #{file_name} file: #{path}", path)
|
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: 0.
|
4
|
+
version: 0.21.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-06-
|
11
|
+
date: 2018-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -351,6 +351,7 @@ files:
|
|
351
351
|
- exe/bolt-inventory-pdb
|
352
352
|
- lib/bolt.rb
|
353
353
|
- lib/bolt/analytics.rb
|
354
|
+
- lib/bolt/bolt_option_parser.rb
|
354
355
|
- lib/bolt/cli.rb
|
355
356
|
- lib/bolt/config.rb
|
356
357
|
- lib/bolt/error.rb
|
@@ -1634,7 +1635,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1634
1635
|
version: '0'
|
1635
1636
|
requirements: []
|
1636
1637
|
rubyforge_project:
|
1637
|
-
rubygems_version: 2.
|
1638
|
+
rubygems_version: 2.6.14
|
1638
1639
|
signing_key:
|
1639
1640
|
specification_version: 4
|
1640
1641
|
summary: Execute commands remotely over SSH and WinRM
|