bolt 0.16.4 → 0.17.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/fail_plan.rb +2 -3
- data/bolt-modules/boltlib/lib/puppet/functions/file_upload.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +2 -3
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +29 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +27 -0
- data/lib/bolt/cli.rb +220 -184
- data/lib/bolt/config.rb +95 -13
- data/lib/bolt/executor.rb +12 -5
- data/lib/bolt/inventory.rb +48 -11
- data/lib/bolt/inventory/group.rb +22 -2
- data/lib/bolt/logger.rb +56 -8
- data/lib/bolt/outputter/human.rb +18 -1
- data/lib/bolt/pal.rb +6 -1
- data/lib/bolt/target.rb +1 -1
- data/lib/bolt/transport/base.rb +3 -0
- data/lib/bolt/transport/local.rb +90 -0
- data/lib/bolt/transport/local/shell.rb +29 -0
- data/lib/bolt/transport/orch.rb +2 -2
- data/lib/bolt/transport/ssh.rb +0 -3
- data/lib/bolt/transport/winrm.rb +0 -3
- data/lib/bolt/util.rb +31 -4
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_ext/puppetdb_inventory.rb +9 -2
- data/modules/aggregate/lib/puppet/functions/aggregate/count.rb +19 -0
- data/modules/aggregate/lib/puppet/functions/aggregate/nodes.rb +19 -0
- data/modules/aggregate/plans/count.pp +35 -0
- data/modules/aggregate/plans/nodes.pp +35 -0
- data/modules/canary/lib/puppet/functions/canary/merge.rb +11 -0
- data/modules/canary/lib/puppet/functions/canary/random_split.rb +20 -0
- data/modules/canary/lib/puppet/functions/canary/skip.rb +23 -0
- data/modules/canary/plans/init.pp +52 -0
- metadata +14 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91e33f8c654cb910a35ab5584efb6ac4fe2b3005
|
4
|
+
data.tar.gz: 5c2e652d4d607538aa57bf932d43822dbba32e3c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d78fdf3b548aeaa383afb25d72a9e0df827e3929419e8d745efc1fc2d2fa98015e107c2fdb47ae5315002069a4da2bd7bdc7eaa3dd4adc9cfff5226b80e5e415
|
7
|
+
data.tar.gz: b1ea22fe91aabd4860cbdebb08dd50be7cdd43d18b312f044c5c00e64e0e2ca0de4c09835655e2b153217b756a570991195c6042d9c6e843e60932d89efdcc9f
|
@@ -1,10 +1,9 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
1
3
|
# Raises a Bolt::PlanFailure exception to signal to callers that the plan failed
|
2
4
|
#
|
3
5
|
# Plan authors should call this function when their plan is not successful. The
|
4
6
|
# error may then be caught by another plans run_plan function or in bolt itself
|
5
|
-
|
6
|
-
require 'bolt/error'
|
7
|
-
|
8
7
|
Puppet::Functions.create_function(:fail_plan) do
|
9
8
|
dispatch :from_args do
|
10
9
|
param 'String[1]', :msg
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
1
3
|
# Uploads the given file or directory to the given set of targets and returns the result from each upload.
|
2
4
|
#
|
3
5
|
# * This function does nothing if the list of targets is empty.
|
@@ -5,8 +7,6 @@
|
|
5
7
|
# * A target is a String with a targets's hostname or a Target.
|
6
8
|
# * The returned value contains information about the result per target.
|
7
9
|
#
|
8
|
-
require 'bolt/error'
|
9
|
-
|
10
10
|
Puppet::Functions.create_function(:file_upload, Puppet::Functions::InternalFunction) do
|
11
11
|
dispatch :file_upload do
|
12
12
|
scope_param
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
1
3
|
# Parses common ways of referring to targets and returns an array of Targets.
|
2
4
|
#
|
3
5
|
# Accepts input consisting of
|
@@ -12,9 +14,6 @@
|
|
12
14
|
# - ['host1', 'group1', 'winrm://host2:54321']
|
13
15
|
#
|
14
16
|
# Returns an array of unique Targets resolved from any target URIs and groups.
|
15
|
-
|
16
|
-
require 'bolt/error'
|
17
|
-
|
18
17
|
Puppet::Functions.create_function(:get_targets) do
|
19
18
|
dispatch :get_targets do
|
20
19
|
param 'Boltlib::TargetSpec', :names
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
1
3
|
# Runs a command on the given set of targets and returns the result from each command execution.
|
2
4
|
#
|
3
5
|
# * This function does nothing if the list of targets is empty.
|
@@ -5,8 +7,6 @@
|
|
5
7
|
# * A target is a String with a targets's hostname or a Target.
|
6
8
|
# * The returned value contains information about the result per target.
|
7
9
|
#
|
8
|
-
require 'bolt/error'
|
9
|
-
|
10
10
|
Puppet::Functions.create_function(:run_command) do
|
11
11
|
dispatch :run_command do
|
12
12
|
param 'String[1]', :command
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
1
3
|
# Runs the `plan` referenced by its name passing giving arguments to it given as a hash of name to value mappings.
|
2
4
|
# A plan is autoloaded from under <root>/plans if not already defined.
|
3
5
|
#
|
@@ -8,8 +10,6 @@
|
|
8
10
|
# }
|
9
11
|
# run_plan('myplan', { x => 'testing' })
|
10
12
|
#
|
11
|
-
require 'bolt/error'
|
12
|
-
|
13
13
|
Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction) do
|
14
14
|
dispatch :run_plan do
|
15
15
|
scope_param
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
1
3
|
# Runs a given instance of a `Task` on the given set of targets and returns the result from each.
|
2
4
|
#
|
3
5
|
# * This function does nothing if the list of targets is empty.
|
@@ -5,8 +7,6 @@
|
|
5
7
|
# * A target is a String with a targets's hostname or a Target.
|
6
8
|
# * The returned value contains information about the result per target.
|
7
9
|
#
|
8
|
-
require 'bolt/error'
|
9
|
-
|
10
10
|
Puppet::Functions.create_function(:run_task) do
|
11
11
|
dispatch :run_task do
|
12
12
|
param 'String[1]', :task_name
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
3
|
+
# Sets a variable { key => value } for a target.
|
4
|
+
#
|
5
|
+
# This function takes 3 parameters:
|
6
|
+
# * A Target object to set the variable for
|
7
|
+
# * The key for the variable (String)
|
8
|
+
# * The value of the variable (Data)
|
9
|
+
#
|
10
|
+
# Returns undef.
|
11
|
+
Puppet::Functions.create_function(:set_var) do
|
12
|
+
dispatch :set_var do
|
13
|
+
param 'Target', :target
|
14
|
+
param 'String', :key
|
15
|
+
param 'Data', :value
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_var(target, key, value)
|
19
|
+
unless Puppet[:tasks]
|
20
|
+
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
21
|
+
Puppet::Pops::Issues::TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, operation: 'set_var'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
inventory = Puppet.lookup(:bolt_inventory) { nil }
|
26
|
+
|
27
|
+
inventory.set_var(target, key, value)
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'bolt/error'
|
2
|
+
|
3
|
+
# Returns a hash of the 'vars' (variables) assigned to a target through the
|
4
|
+
# inventory file or `set_var` function.
|
5
|
+
#
|
6
|
+
# Accepts no parameters.
|
7
|
+
#
|
8
|
+
# Plan authors can call this function on a target to get the variable hash
|
9
|
+
# for that target.
|
10
|
+
Puppet::Functions.create_function(:vars) do
|
11
|
+
dispatch :vars do
|
12
|
+
param 'Target', :target
|
13
|
+
return_type 'Hash[String, Data]'
|
14
|
+
end
|
15
|
+
|
16
|
+
def vars(target)
|
17
|
+
unless Puppet[:tasks]
|
18
|
+
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
19
|
+
Puppet::Pops::Issues::TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, operation: 'set_var'
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
inventory = Puppet.lookup(:bolt_inventory) { nil }
|
24
|
+
|
25
|
+
inventory.vars(target)
|
26
|
+
end
|
27
|
+
end
|
data/lib/bolt/cli.rb
CHANGED
@@ -24,7 +24,8 @@ module Bolt
|
|
24
24
|
class CLIExit < StandardError; end
|
25
25
|
|
26
26
|
class CLI
|
27
|
-
|
27
|
+
class BoltOptionParser < OptionParser
|
28
|
+
BANNER = <<-HELP.freeze
|
28
29
|
Usage: bolt <subcommand> <action> [options]
|
29
30
|
|
30
31
|
Available subcommands:
|
@@ -39,9 +40,9 @@ Available subcommands:
|
|
39
40
|
bolt file upload <src> <dest> Upload a local file
|
40
41
|
|
41
42
|
where [options] are:
|
42
|
-
HELP
|
43
|
+
HELP
|
43
44
|
|
44
|
-
|
45
|
+
TASK_HELP = <<-HELP.freeze
|
45
46
|
Usage: bolt task <action> <task> [options] [parameters]
|
46
47
|
|
47
48
|
Available actions are:
|
@@ -52,27 +53,27 @@ Available actions are:
|
|
52
53
|
Parameters are of the form <parameter>=<value>.
|
53
54
|
|
54
55
|
Available options are:
|
55
|
-
HELP
|
56
|
+
HELP
|
56
57
|
|
57
|
-
|
58
|
+
COMMAND_HELP = <<-HELP.freeze
|
58
59
|
Usage: bolt command <action> <command> [options]
|
59
60
|
|
60
61
|
Available actions are:
|
61
62
|
run Run a command remotely
|
62
63
|
|
63
64
|
Available options are:
|
64
|
-
HELP
|
65
|
+
HELP
|
65
66
|
|
66
|
-
|
67
|
+
SCRIPT_HELP = <<-HELP.freeze
|
67
68
|
Usage: bolt script <action> <script> [[arg1] ... [argN]] [options]
|
68
69
|
|
69
70
|
Available actions are:
|
70
71
|
run Upload a local script and run it remotely
|
71
72
|
|
72
73
|
Available options are:
|
73
|
-
HELP
|
74
|
+
HELP
|
74
75
|
|
75
|
-
|
76
|
+
PLAN_HELP = <<-HELP.freeze
|
76
77
|
Usage: bolt plan <action> <plan> [options] [parameters]
|
77
78
|
|
78
79
|
Available actions are:
|
@@ -83,214 +84,266 @@ Available actions are:
|
|
83
84
|
Parameters are of the form <parameter>=<value>.
|
84
85
|
|
85
86
|
Available options are:
|
86
|
-
HELP
|
87
|
+
HELP
|
87
88
|
|
88
|
-
|
89
|
+
FILE_HELP = <<-HELP.freeze
|
89
90
|
Usage: bolt file <action> [options]
|
90
91
|
|
91
92
|
Available actions are:
|
92
93
|
upload <src> <dest> Upload local file <src> to <dest> on each node
|
93
94
|
|
94
95
|
Available options are:
|
95
|
-
HELP
|
96
|
+
HELP
|
96
97
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
TRANSPORTS = %w[ssh winrm pcp].freeze
|
98
|
+
# A helper mixin for OptionParser::Switch instances which will allow
|
99
|
+
# us to show/hide particular switch in the help message produced by
|
100
|
+
# the OptionParser#help method on demand.
|
101
|
+
module SwitchHider
|
102
|
+
attr_accessor :hide
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
def initialize(argv)
|
108
|
-
Bolt::Logger.initialize_logging
|
109
|
-
@argv = argv
|
110
|
-
@options = {
|
111
|
-
nodes: []
|
112
|
-
}
|
113
|
-
@config = Bolt::Config.new
|
114
|
-
|
115
|
-
# parse mode and object, use COMMANDS as a whitelist
|
116
|
-
@options[:mode] = argv[0] if COMMANDS.keys.any? { |mode| argv[0] == mode }
|
117
|
-
@options[:object] = argv[1] if COMMANDS.values.flatten.uniq.any? { |object| argv[1] == object }
|
118
|
-
@parser = create_option_parser(@options)
|
119
|
-
@logger = Logging.logger[self]
|
120
|
-
end
|
121
|
-
|
122
|
-
def create_option_parser(results)
|
123
|
-
parser = OptionParser.new('') do |opts|
|
124
|
-
unless results[:mode] == 'plan'
|
125
|
-
opts.on('-n', '--nodes NODES',
|
126
|
-
'Node(s) to connect to in URI format [protocol://]host[:port] (Optional)',
|
127
|
-
'Eg. --nodes bolt.puppet.com',
|
128
|
-
'Eg. --nodes localhost,ssh://nix.com:2222,winrm://windows.puppet.com',
|
129
|
-
"\n",
|
130
|
-
'* NODES can either be comma-separated, \'@<file>\' to read',
|
131
|
-
'* nodes from a file, or \'-\' to read from stdin',
|
132
|
-
'* Windows nodes must specify protocol with winrm://',
|
133
|
-
'* protocol is `ssh` by default, may be `ssh` or `winrm`',
|
134
|
-
'* port defaults to `22` for SSH',
|
135
|
-
'* port defaults to `5985` or `5986` for WinRM, based on the --[no-]ssl setting') do |nodes|
|
136
|
-
results[:nodes] << get_arg_input(nodes)
|
137
|
-
end
|
104
|
+
def summarize(*args)
|
105
|
+
return self if hide
|
106
|
+
super
|
138
107
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize(options)
|
111
|
+
super()
|
112
|
+
|
113
|
+
@options = options
|
114
|
+
|
115
|
+
@nodes = define('-n', '--nodes NODES',
|
116
|
+
'Node(s) to connect to in URI format [protocol://]host[:port] (Optional)',
|
117
|
+
'Eg. --nodes bolt.puppet.com',
|
118
|
+
'Eg. --nodes localhost,ssh://nix.com:2222,winrm://windows.puppet.com',
|
119
|
+
# An empty line in a switch description causes the OptionParser#help
|
120
|
+
# method to raise an exception. Specifying a line containing only a
|
121
|
+
# newline is the closest one can get to the empty line without
|
122
|
+
# triggering that exception.
|
123
|
+
"\n",
|
124
|
+
'* NODES can either be comma-separated, \'@<file>\' to read',
|
125
|
+
'* nodes from a file, or \'-\' to read from stdin',
|
126
|
+
'* Windows nodes must specify protocol with winrm://',
|
127
|
+
'* protocol is `ssh` by default, may be `ssh` or `winrm`',
|
128
|
+
'* port defaults to `22` for SSH',
|
129
|
+
'* port defaults to `5985` or `5986` for WinRM, based on the --[no-]ssl setting') do |nodes|
|
130
|
+
@options[:nodes] << get_arg_input(nodes)
|
131
|
+
end.extend(SwitchHider)
|
132
|
+
define('-u', '--user USER',
|
133
|
+
'User to authenticate as (Optional)') do |user|
|
134
|
+
@options[:user] = user
|
142
135
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
136
|
+
define('-p', '--password [PASSWORD]',
|
137
|
+
'Password to authenticate with (Optional).',
|
138
|
+
'Omit the value to prompt for the password.') do |password|
|
146
139
|
if password.nil?
|
147
140
|
STDOUT.print "Please enter your password: "
|
148
|
-
|
141
|
+
@options[:password] = STDIN.noecho(&:gets).chomp
|
149
142
|
STDOUT.puts
|
150
143
|
else
|
151
|
-
|
144
|
+
@options[:password] = password
|
152
145
|
end
|
153
146
|
end
|
154
|
-
|
155
|
-
|
156
|
-
|
147
|
+
define('--private-key KEY',
|
148
|
+
'Private ssh key to authenticate with (Optional)') do |key|
|
149
|
+
@options[:key] = key
|
157
150
|
end
|
158
|
-
|
159
|
-
|
160
|
-
|
151
|
+
define('--tmpdir DIR',
|
152
|
+
'The directory to upload and execute temporary files on the target (Optional)') do |tmpdir|
|
153
|
+
@options[:tmpdir] = tmpdir
|
161
154
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
155
|
+
define('-c', '--concurrency CONCURRENCY', Integer,
|
156
|
+
'Maximum number of simultaneous connections ' \
|
157
|
+
'(Optional, defaults to 100)') do |concurrency|
|
158
|
+
@options[:concurrency] = concurrency
|
166
159
|
end
|
167
|
-
|
168
|
-
|
169
|
-
|
160
|
+
define('--connect-timeout TIMEOUT', Integer,
|
161
|
+
'Connection timeout (Optional)') do |timeout|
|
162
|
+
@options[:connect_timeout] = timeout
|
170
163
|
end
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
164
|
+
define('--modulepath MODULES',
|
165
|
+
'List of directories containing modules, ' \
|
166
|
+
'separated by ' << File::PATH_SEPARATOR) do |modulepath|
|
167
|
+
@options[:modulepath] = modulepath.split(File::PATH_SEPARATOR)
|
175
168
|
end
|
176
|
-
|
177
|
-
|
178
|
-
|
169
|
+
define('--params PARAMETERS',
|
170
|
+
'Parameters to a task or plan') do |params|
|
171
|
+
@options[:task_options] = parse_params(params)
|
179
172
|
end
|
180
173
|
|
181
|
-
|
182
|
-
|
183
|
-
|
174
|
+
define('--format FORMAT',
|
175
|
+
'Output format to use: human or json') do |format|
|
176
|
+
@options[:format] = format
|
184
177
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
178
|
+
define('--[no-]host-key-check',
|
179
|
+
'Check host keys with SSH') do |host_key_check|
|
180
|
+
@options[:host_key_check] = host_key_check
|
188
181
|
end
|
189
|
-
|
190
|
-
|
191
|
-
|
182
|
+
define('--[no-]ssl',
|
183
|
+
'Use SSL with WinRM') do |ssl|
|
184
|
+
@options[:ssl] = ssl
|
192
185
|
end
|
193
|
-
|
194
|
-
|
195
|
-
|
186
|
+
define('--transport TRANSPORT', TRANSPORTS.map(&:to_s),
|
187
|
+
'Specify a default transport: ' << TRANSPORTS.join(', ')) do |t|
|
188
|
+
@options[:transport] = t
|
196
189
|
end
|
197
|
-
|
198
|
-
|
199
|
-
|
190
|
+
define('--run-as USER',
|
191
|
+
'User to run as using privilege escalation') do |user|
|
192
|
+
@options[:run_as] = user
|
200
193
|
end
|
201
|
-
|
202
|
-
|
194
|
+
define('--sudo-password [PASSWORD]',
|
195
|
+
'Password for privilege escalation') do |password|
|
203
196
|
if password.nil?
|
204
197
|
STDOUT.print "Please enter your privilege escalation password: "
|
205
|
-
|
198
|
+
@options[:sudo_password] = STDIN.noecho(&:gets).chomp
|
206
199
|
STDOUT.puts
|
207
200
|
else
|
208
|
-
|
201
|
+
@options[:sudo_password] = password
|
209
202
|
end
|
210
203
|
end
|
211
|
-
|
212
|
-
|
213
|
-
|
204
|
+
define('--configfile CONFIG_PATH',
|
205
|
+
'Specify where to load the config file from') do |path|
|
206
|
+
@options[:configfile] = path
|
214
207
|
end
|
215
|
-
|
216
|
-
|
217
|
-
|
208
|
+
define('--inventoryfile INVENTORY_PATH',
|
209
|
+
'Specify where to load the inventory file from') do |path|
|
210
|
+
@options[:inventoryfile] = path
|
218
211
|
end
|
219
|
-
|
220
|
-
|
221
|
-
|
212
|
+
define_tail('--[no-]tty',
|
213
|
+
'Request a pseudo TTY on nodes that support it') do |tty|
|
214
|
+
@options[:tty] = tty
|
222
215
|
end
|
223
|
-
|
224
|
-
|
225
|
-
|
216
|
+
define_tail('--noop',
|
217
|
+
'Execute a task that supports it in noop mode') do |_|
|
218
|
+
@options[:noop] = true
|
226
219
|
end
|
227
|
-
|
228
|
-
|
220
|
+
define_tail('-h', '--help', 'Display help') do |_|
|
221
|
+
@options[:help] = true
|
229
222
|
end
|
230
|
-
|
231
|
-
|
223
|
+
define_tail('--verbose', 'Display verbose logging') do |_|
|
224
|
+
@options[:verbose] = true
|
232
225
|
end
|
233
|
-
|
234
|
-
|
226
|
+
define_tail('--debug', 'Display debug logging') do |_|
|
227
|
+
@options[:debug] = true
|
235
228
|
end
|
236
|
-
|
229
|
+
define_tail('--version', 'Display the version') do |_|
|
237
230
|
puts Bolt::VERSION
|
238
231
|
raise Bolt::CLIExit
|
239
232
|
end
|
233
|
+
|
234
|
+
update
|
240
235
|
end
|
241
236
|
|
242
|
-
|
243
|
-
|
237
|
+
def update
|
238
|
+
# Update the banner according to the mode
|
239
|
+
self.banner = case @options[:mode]
|
240
|
+
when 'plan'
|
244
241
|
PLAN_HELP
|
245
|
-
when
|
242
|
+
when 'command'
|
246
243
|
COMMAND_HELP
|
247
|
-
when
|
244
|
+
when 'script'
|
248
245
|
SCRIPT_HELP
|
249
|
-
when
|
246
|
+
when 'task'
|
250
247
|
TASK_HELP
|
251
|
-
when
|
248
|
+
when 'file'
|
252
249
|
FILE_HELP
|
253
250
|
else
|
254
251
|
BANNER
|
255
252
|
end
|
256
|
-
parser
|
257
|
-
end
|
258
253
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
private :inventory
|
254
|
+
# Only show the --nodes switch in the help message produced by
|
255
|
+
# the #help method when not dealing with plans
|
256
|
+
@nodes.hide = (@options[:mode] == 'plan')
|
257
|
+
end
|
264
258
|
|
265
|
-
|
266
|
-
|
267
|
-
|
259
|
+
def parse_params(params)
|
260
|
+
json = get_arg_input(params)
|
261
|
+
JSON.parse(json)
|
262
|
+
rescue JSON::ParserError => err
|
263
|
+
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{err}"
|
264
|
+
end
|
265
|
+
|
266
|
+
def get_arg_input(value)
|
267
|
+
if value.start_with?('@')
|
268
|
+
file = value.sub(/^@/, '')
|
269
|
+
read_arg_file(file)
|
270
|
+
elsif value == '-'
|
271
|
+
STDIN.read
|
272
|
+
else
|
273
|
+
value
|
274
|
+
end
|
268
275
|
end
|
269
276
|
|
270
|
-
|
271
|
-
|
277
|
+
def read_arg_file(file)
|
278
|
+
File.read(file)
|
279
|
+
rescue StandardError => err
|
280
|
+
raise Bolt::CLIError, "Error attempting to read #{file}: #{err}"
|
272
281
|
end
|
282
|
+
end
|
283
|
+
|
284
|
+
COMMANDS = { 'command' => %w[run],
|
285
|
+
'script' => %w[run],
|
286
|
+
'task' => %w[show run],
|
287
|
+
'plan' => %w[show run],
|
288
|
+
'file' => %w[upload] }.freeze
|
273
289
|
|
274
|
-
|
275
|
-
options[:mode] = remaining.shift
|
290
|
+
attr_reader :config, :options
|
276
291
|
|
277
|
-
|
278
|
-
|
292
|
+
def initialize(argv)
|
293
|
+
Bolt::Logger.initialize_logging
|
294
|
+
@logger = Logging.logger[self]
|
279
295
|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
296
|
+
@config = Bolt::Config.new
|
297
|
+
|
298
|
+
@argv = argv
|
299
|
+
|
300
|
+
@options = {
|
301
|
+
nodes: []
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
# Only call after @config has been initialized.
|
306
|
+
def inventory
|
307
|
+
@inventory ||= Bolt::Inventory.from_config(config)
|
308
|
+
end
|
309
|
+
private :inventory
|
284
310
|
|
285
|
-
|
311
|
+
def parse
|
312
|
+
parser = BoltOptionParser.new(options)
|
313
|
+
|
314
|
+
if @argv.empty? ||
|
315
|
+
begin
|
316
|
+
# RuboCop apparently doesn't realize this is a block and issues
|
317
|
+
# the Lint/AssignmentInCondition warning for every assignment in
|
318
|
+
# it, so we disable that warning here.
|
319
|
+
# rubocop:disable Lint/AssignmentInCondition
|
320
|
+
remaining = handle_parser_errors do
|
321
|
+
parser.permute(@argv)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Set the mode
|
325
|
+
options[:mode] = remaining.shift
|
326
|
+
|
327
|
+
if options[:mode] == 'help'
|
328
|
+
options[:help] = true
|
329
|
+
options[:mode] = remaining.shift
|
330
|
+
end
|
331
|
+
|
332
|
+
# Update the parser for the new mode
|
333
|
+
parser.update
|
334
|
+
|
335
|
+
options[:help]
|
336
|
+
# rubocop:enable Lint/AssignmentInCondition
|
337
|
+
end
|
338
|
+
then # rubocop:disable Style/MultilineIfThen
|
286
339
|
puts parser.help
|
287
340
|
raise Bolt::CLIExit
|
288
341
|
end
|
289
342
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
343
|
+
config.load_file(options[:configfile])
|
344
|
+
config.update_from_cli(options)
|
345
|
+
config.validate
|
346
|
+
Bolt::Logger.configure(config)
|
294
347
|
|
295
348
|
# This section handles parsing non-flag options which are
|
296
349
|
# mode specific rather then part of the config
|
@@ -313,7 +366,9 @@ HELP
|
|
313
366
|
validate(options)
|
314
367
|
|
315
368
|
# After validation, initialize inventory and targets. Errors here are better to catch early.
|
316
|
-
options[:
|
369
|
+
unless options[:action] == 'show' || options[:mode] == 'plan'
|
370
|
+
options[:targets] = inventory.get_targets(options[:nodes]) if options[:nodes]
|
371
|
+
end
|
317
372
|
|
318
373
|
options
|
319
374
|
rescue Bolt::Error => e
|
@@ -321,30 +376,6 @@ HELP
|
|
321
376
|
raise e
|
322
377
|
end
|
323
378
|
|
324
|
-
def parse_params(params)
|
325
|
-
json = get_arg_input(params)
|
326
|
-
JSON.parse(json)
|
327
|
-
rescue JSON::ParserError => err
|
328
|
-
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{err}"
|
329
|
-
end
|
330
|
-
|
331
|
-
def get_arg_input(value)
|
332
|
-
if value.start_with?('@')
|
333
|
-
file = value.sub(/^@/, '')
|
334
|
-
read_arg_file(file)
|
335
|
-
elsif value == '-'
|
336
|
-
STDIN.read
|
337
|
-
else
|
338
|
-
value
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
def read_arg_file(file)
|
343
|
-
File.read(file)
|
344
|
-
rescue StandardError => err
|
345
|
-
raise Bolt::CLIError, "Error attempting to read #{file}: #{err}"
|
346
|
-
end
|
347
|
-
|
348
379
|
def validate(options)
|
349
380
|
unless COMMANDS.include?(options[:mode])
|
350
381
|
raise Bolt::CLIError,
|
@@ -385,12 +416,6 @@ HELP
|
|
385
416
|
raise Bolt::CLIError, "Option '--nodes' must be specified"
|
386
417
|
end
|
387
418
|
|
388
|
-
if %w[task plan].include?(options[:mode]) && @config[:modulepath].nil?
|
389
|
-
raise Bolt::CLIError,
|
390
|
-
"Option '--modulepath' must be specified when using" \
|
391
|
-
" a task or plan"
|
392
|
-
end
|
393
|
-
|
394
419
|
if options[:noop] && (options[:mode] != 'task' || options[:action] != 'run')
|
395
420
|
raise Bolt::CLIError,
|
396
421
|
"Option '--noop' may only be specified when running a task"
|
@@ -401,6 +426,8 @@ HELP
|
|
401
426
|
yield
|
402
427
|
rescue OptionParser::MissingArgument => e
|
403
428
|
raise Bolt::CLIError, "Option '#{e.args.first}' needs a parameter"
|
429
|
+
rescue OptionParser::InvalidArgument => e
|
430
|
+
raise Bolt::CLIError, "Invalid parameter specified for option '#{e.args.first}': #{e.args[1]}"
|
404
431
|
rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption => e
|
405
432
|
raise Bolt::CLIError, "Unknown argument '#{e.args.first}'"
|
406
433
|
end
|
@@ -416,7 +443,7 @@ HELP
|
|
416
443
|
end
|
417
444
|
|
418
445
|
if options[:mode] == 'plan' || options[:mode] == 'task'
|
419
|
-
pal = Bolt::PAL.new(
|
446
|
+
pal = Bolt::PAL.new(config)
|
420
447
|
end
|
421
448
|
|
422
449
|
if options[:action] == 'show'
|
@@ -443,13 +470,22 @@ HELP
|
|
443
470
|
message = 'There may be processes left executing on some nodes.'
|
444
471
|
|
445
472
|
if options[:mode] == 'plan'
|
446
|
-
|
473
|
+
unless options[:nodes].empty?
|
474
|
+
if options[:task_options]['nodes']
|
475
|
+
raise Bolt::CLIError,
|
476
|
+
"A plan's 'nodes' parameter may be specified using the --nodes option, but in that " \
|
477
|
+
"case it must not be specified as a separate nodes=<value> parameter nor included " \
|
478
|
+
"in the JSON data passed in the --params option"
|
479
|
+
end
|
480
|
+
options[:task_options]['nodes'] = options[:nodes].join(',')
|
481
|
+
end
|
482
|
+
executor = Bolt::Executor.new(config, options[:noop], true)
|
447
483
|
result = pal.run_plan(options[:object], options[:task_options], executor, inventory)
|
448
484
|
outputter.print_plan_result(result)
|
449
485
|
# An exception would have been raised if the plan failed
|
450
486
|
code = 0
|
451
487
|
else
|
452
|
-
executor = Bolt::Executor.new(
|
488
|
+
executor = Bolt::Executor.new(config, options[:noop])
|
453
489
|
targets = options[:targets]
|
454
490
|
|
455
491
|
results = nil
|
@@ -525,7 +561,7 @@ HELP
|
|
525
561
|
end
|
526
562
|
|
527
563
|
def outputter
|
528
|
-
@outputter ||= Bolt::Outputter.for_format(
|
564
|
+
@outputter ||= Bolt::Outputter.for_format(config[:format])
|
529
565
|
end
|
530
566
|
end
|
531
567
|
end
|