bolt 0.14.0 → 0.15.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/exe/bolt +3 -2
- data/lib/bolt/cli.rb +99 -92
- data/lib/bolt/config.rb +7 -8
- data/lib/bolt/error.rb +20 -3
- data/lib/bolt/executor.rb +44 -45
- data/lib/bolt/logger.rb +28 -52
- data/lib/bolt/node.rb +12 -41
- data/lib/bolt/node/orch.rb +28 -34
- data/lib/bolt/node/ssh.rb +26 -21
- data/lib/bolt/node/winrm.rb +41 -19
- data/lib/bolt/notifier.rb +2 -2
- data/lib/bolt/outputter/human.rb +34 -29
- data/lib/bolt/outputter/json.rb +4 -10
- data/lib/bolt/pal.rb +106 -0
- data/lib/bolt/result.rb +90 -87
- data/lib/bolt/result_set.rb +94 -0
- data/lib/bolt/target.rb +61 -13
- data/lib/bolt/version.rb +1 -1
- data/modules/boltlib/lib/puppet/datatypes/result.rb +18 -0
- data/modules/boltlib/lib/puppet/datatypes/resultset.rb +22 -0
- data/modules/boltlib/lib/puppet/datatypes/target.rb +4 -1
- data/modules/boltlib/lib/puppet/functions/file_upload.rb +15 -10
- data/modules/boltlib/lib/puppet/functions/run_command.rb +15 -8
- data/modules/boltlib/lib/puppet/functions/run_script.rb +15 -26
- data/modules/boltlib/lib/puppet/functions/run_task.rb +17 -15
- metadata +23 -9
- data/lib/bolt/execution_result.rb +0 -109
- data/lib/bolt/formatter.rb +0 -9
- data/lib/bolt/node_uri.rb +0 -42
- data/modules/boltlib/lib/puppet/datatypes/executionresult.rb +0 -30
data/lib/bolt/executor.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
require 'bolt/logger'
|
2
1
|
require 'json'
|
3
2
|
require 'concurrent'
|
3
|
+
require 'logging'
|
4
4
|
require 'bolt/result'
|
5
5
|
require 'bolt/config'
|
6
6
|
require 'bolt/notifier'
|
7
|
+
require 'bolt/node'
|
8
|
+
require 'bolt/result_set'
|
7
9
|
|
8
10
|
module Bolt
|
9
11
|
class Executor
|
@@ -11,20 +13,26 @@ module Bolt
|
|
11
13
|
|
12
14
|
def initialize(config = Bolt::Config.new, noop = nil, plan_logging = false)
|
13
15
|
@config = config
|
14
|
-
@logger =
|
16
|
+
@logger = Logging.logger[self]
|
17
|
+
|
18
|
+
# If a specific elevated log level has been requested, honor that.
|
19
|
+
# Otherwise, escalate the log level to "info" if running in plan mode, so
|
20
|
+
# that certain progress messages will be visible.
|
21
|
+
default_log_level = plan_logging ? :info : :notice
|
22
|
+
@logger.level = @config[:log_level] || default_log_level
|
15
23
|
@noop = noop
|
16
24
|
@notifier = Bolt::Notifier.new
|
17
|
-
@plan_logging = plan_logging
|
18
25
|
end
|
19
26
|
|
20
|
-
def
|
21
|
-
|
22
|
-
Bolt::Node.
|
27
|
+
def from_targets(targets)
|
28
|
+
targets.map do |target|
|
29
|
+
Bolt::Node.from_target(target, config: @config)
|
23
30
|
end
|
24
31
|
end
|
32
|
+
private :from_targets
|
25
33
|
|
26
34
|
def on(nodes, callback = nil)
|
27
|
-
results = Concurrent::
|
35
|
+
results = Concurrent::Array.new
|
28
36
|
|
29
37
|
poolsize = [nodes.length, @config[:concurrency]].min
|
30
38
|
pool = Concurrent::FixedThreadPool.new(poolsize)
|
@@ -38,11 +46,11 @@ module Bolt
|
|
38
46
|
pool.post do
|
39
47
|
result =
|
40
48
|
begin
|
41
|
-
@notifier.notify(callback,
|
49
|
+
@notifier.notify(callback, type: :node_start, target: node.target) if callback
|
42
50
|
node.connect
|
43
51
|
yield node
|
44
52
|
rescue StandardError => ex
|
45
|
-
Bolt::Result.from_exception(ex)
|
53
|
+
Bolt::Result.from_exception(node.target, ex)
|
46
54
|
ensure
|
47
55
|
begin
|
48
56
|
node.disconnect
|
@@ -50,11 +58,8 @@ module Bolt
|
|
50
58
|
@logger.info("Failed to close connection to #{node.uri} : #{ex.message}")
|
51
59
|
end
|
52
60
|
end
|
53
|
-
results[
|
54
|
-
if callback
|
55
|
-
@notifier.notify(callback, node, type: :node_result, result: result)
|
56
|
-
end
|
57
|
-
result
|
61
|
+
results.concat([result])
|
62
|
+
@notifier.notify(callback, type: :node_result, result: result) if callback
|
58
63
|
end
|
59
64
|
}
|
60
65
|
pool.shutdown
|
@@ -62,84 +67,78 @@ module Bolt
|
|
62
67
|
|
63
68
|
@notifier.shutdown
|
64
69
|
|
65
|
-
|
70
|
+
Bolt::ResultSet.new(results)
|
66
71
|
end
|
72
|
+
private :on
|
67
73
|
|
68
74
|
def summary(action, object, result)
|
69
|
-
fc = result.
|
75
|
+
fc = result.error_set.length
|
70
76
|
npl = result.length == 1 ? '' : 's'
|
71
77
|
fpl = fc == 1 ? '' : 's'
|
72
78
|
"Ran #{action} '#{object}' on #{result.length} node#{npl} with #{fc} failure#{fpl}"
|
73
79
|
end
|
80
|
+
private :summary
|
74
81
|
|
75
|
-
def run_command(
|
76
|
-
|
77
|
-
@logger.
|
82
|
+
def run_command(targets, command)
|
83
|
+
nodes = from_targets(targets)
|
84
|
+
@logger.info("Starting command run '#{command}' on #{nodes.map(&:uri)}")
|
78
85
|
callback = block_given? ? Proc.new : nil
|
79
86
|
|
80
87
|
r = on(nodes, callback) do |node|
|
81
88
|
@logger.debug("Running command '#{command}' on #{node.uri}")
|
82
89
|
node_result = node.run_command(command)
|
83
|
-
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.
|
90
|
+
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.value)}")
|
84
91
|
node_result
|
85
92
|
end
|
86
|
-
@logger.
|
93
|
+
@logger.info(summary('command', command, r))
|
87
94
|
r
|
88
95
|
end
|
89
96
|
|
90
|
-
def run_script(
|
91
|
-
|
92
|
-
@logger.
|
97
|
+
def run_script(targets, script, arguments)
|
98
|
+
nodes = from_targets(targets)
|
99
|
+
@logger.info("Starting script run #{script} on #{nodes.map(&:uri)}")
|
93
100
|
@logger.debug("Arguments: #{arguments}")
|
94
101
|
callback = block_given? ? Proc.new : nil
|
95
102
|
|
96
103
|
r = on(nodes, callback) do |node|
|
97
104
|
@logger.debug { "Running script '#{script}' on #{node.uri}" }
|
98
105
|
node_result = node.run_script(script, arguments)
|
99
|
-
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.
|
106
|
+
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.value)}")
|
100
107
|
node_result
|
101
108
|
end
|
102
|
-
@logger.
|
109
|
+
@logger.info(summary('script', script, r))
|
103
110
|
r
|
104
111
|
end
|
105
112
|
|
106
|
-
def run_task(
|
107
|
-
|
108
|
-
@logger.
|
113
|
+
def run_task(targets, task, input_method, arguments)
|
114
|
+
nodes = from_targets(targets)
|
115
|
+
@logger.info("Starting task #{task} on #{nodes.map(&:uri)}")
|
109
116
|
@logger.debug("Arguments: #{arguments} Input method: #{input_method}")
|
110
117
|
callback = block_given? ? Proc.new : nil
|
111
118
|
|
112
119
|
r = on(nodes, callback) do |node|
|
113
120
|
@logger.debug { "Running task run '#{task}' on #{node.uri}" }
|
114
121
|
node_result = node.run_task(task, input_method, arguments)
|
115
|
-
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.
|
122
|
+
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.value)}")
|
116
123
|
node_result
|
117
124
|
end
|
118
|
-
@logger.
|
125
|
+
@logger.info(summary('task', task, r))
|
119
126
|
r
|
120
127
|
end
|
121
128
|
|
122
|
-
def file_upload(
|
123
|
-
|
124
|
-
@logger.
|
129
|
+
def file_upload(targets, source, destination)
|
130
|
+
nodes = from_targets(targets)
|
131
|
+
@logger.info("Starting file upload from #{source} to #{destination} on #{nodes.map(&:uri)}")
|
125
132
|
callback = block_given? ? Proc.new : nil
|
126
133
|
|
127
134
|
r = on(nodes, callback) do |node|
|
128
|
-
@logger.debug { "Uploading: '#{source}' to #{node.uri}" }
|
135
|
+
@logger.debug { "Uploading: '#{source}' to #{destination} on #{node.uri}" }
|
129
136
|
node_result = node.upload(source, destination)
|
130
|
-
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.
|
137
|
+
@logger.debug("Result on #{node.uri}: #{JSON.dump(node_result.value)}")
|
131
138
|
node_result
|
132
139
|
end
|
133
|
-
@logger.
|
140
|
+
@logger.info(summary('upload', source, r))
|
134
141
|
r
|
135
142
|
end
|
136
|
-
|
137
|
-
private
|
138
|
-
|
139
|
-
def results_to_hash(results)
|
140
|
-
result_hash = {}
|
141
|
-
results.each_pair { |k, v| result_hash[k] = v }
|
142
|
-
result_hash
|
143
|
-
end
|
144
143
|
end
|
145
144
|
end
|
data/lib/bolt/logger.rb
CHANGED
@@ -1,56 +1,32 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
1
|
+
require 'logging'
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
module Logger
|
5
|
+
# This method provides a single point-of-entry to setup logging for both
|
6
|
+
# the CLI and for tests. This is necessary because we define custom log
|
7
|
+
# levels which create corresponding methods on the logger instances;
|
8
|
+
# without first initializing the Logging system, calls to those methods
|
9
|
+
# will fail.
|
10
|
+
def self.initialize_logging
|
11
|
+
# Initialization isn't idempotent and will result in warnings about const
|
12
|
+
# redefs, so skip it if it's already been initialized
|
13
|
+
return if Logging.initialized?
|
14
|
+
|
15
|
+
Logging.init :debug, :info, :notice, :warn, :error, :fatal, :any
|
16
|
+
Logging.appenders.stderr(
|
17
|
+
'stderr',
|
18
|
+
layout: Logging.layouts.pattern(
|
19
|
+
pattern: '%d %-6l %c: %m\n',
|
20
|
+
date_pattern: '%Y-%m-%dT%H:%M:%S.%6N'
|
21
|
+
)
|
22
|
+
)
|
23
|
+
root_logger = Logging.logger[:root]
|
24
|
+
root_logger.add_appenders :stderr
|
25
|
+
root_logger.level = :notice
|
21
26
|
end
|
22
|
-
NOTICE = 2
|
23
|
-
WARN = 3
|
24
|
-
ERROR = 4
|
25
|
-
FATAL = 5
|
26
|
-
ANY = 6
|
27
|
-
end
|
28
|
-
|
29
|
-
def notice(progname = nil, &block)
|
30
|
-
add(NOTICE, nil, progname, &block)
|
31
|
-
end
|
32
27
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# rubocop:disable Style/ClassVars
|
38
|
-
@@config = {
|
39
|
-
log_destination: STDERR,
|
40
|
-
log_level: NOTICE
|
41
|
-
}
|
42
|
-
|
43
|
-
# Not thread safe call only during startup
|
44
|
-
def self.configure(config)
|
45
|
-
@@config[:log_level] = config[:log_level] if config[:log_level]
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.get_logger(**conf)
|
49
|
-
conf = @@config.merge(conf)
|
50
|
-
logger = new(conf[:log_destination])
|
51
|
-
logger.level = conf[:log_level]
|
52
|
-
logger.progname = conf[:progname] if conf[:progname]
|
53
|
-
logger.formatter = Bolt::Formatter.new
|
54
|
-
logger
|
28
|
+
def self.reset_logging
|
29
|
+
Logging.reset
|
30
|
+
end
|
55
31
|
end
|
56
32
|
end
|
data/lib/bolt/node.rb
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
-
require 'bolt/logger'
|
2
|
-
require 'bolt/node_uri'
|
3
1
|
require 'bolt/result'
|
4
2
|
require 'bolt/config'
|
5
3
|
require 'bolt/target'
|
4
|
+
require 'logging'
|
6
5
|
|
7
6
|
module Bolt
|
8
7
|
class Node
|
9
8
|
STDIN_METHODS = %w[both stdin].freeze
|
10
9
|
ENVIRONMENT_METHODS = %w[both environment].freeze
|
11
10
|
|
12
|
-
def self.
|
13
|
-
|
14
|
-
klass = case uri.scheme
|
11
|
+
def self.from_target(target, **kwargs)
|
12
|
+
klass = case target.protocol || kwargs[:config][:transport]
|
15
13
|
when 'winrm'
|
16
14
|
Bolt::WinRM
|
17
15
|
when 'pcp'
|
@@ -19,27 +17,19 @@ module Bolt
|
|
19
17
|
else
|
20
18
|
Bolt::SSH
|
21
19
|
end
|
22
|
-
klass.new(
|
23
|
-
uri.port,
|
24
|
-
uri.user,
|
25
|
-
uri.password,
|
26
|
-
uri: uri_string,
|
27
|
-
**kwargs)
|
20
|
+
klass.new(target, **kwargs)
|
28
21
|
end
|
29
22
|
|
30
23
|
def self.initialize_transport(_logger); end
|
31
24
|
|
32
|
-
attr_reader :logger, :
|
25
|
+
attr_reader :logger, :user, :password, :connect_timeout, :target
|
33
26
|
|
34
|
-
def initialize(
|
35
|
-
|
36
|
-
@host = host
|
37
|
-
@port = port
|
38
|
-
@uri = uri
|
27
|
+
def initialize(target, config: Bolt::Config.new)
|
28
|
+
@target = target
|
39
29
|
|
40
30
|
transport_conf = config[:transports][protocol.to_sym]
|
41
|
-
@user = user || transport_conf[:user]
|
42
|
-
@password = password || transport_conf[:password]
|
31
|
+
@user = @target.user || transport_conf[:user]
|
32
|
+
@password = @target.password || transport_conf[:password]
|
43
33
|
@key = transport_conf[:key]
|
44
34
|
@cacert = transport_conf[:cacert]
|
45
35
|
@tty = transport_conf[:tty]
|
@@ -53,30 +43,11 @@ module Bolt
|
|
53
43
|
@orch_task_environment = transport_conf[:orch_task_environment]
|
54
44
|
@extensions = transport_conf[:extensions]
|
55
45
|
|
56
|
-
@logger =
|
57
|
-
@transport_logger = Logger.get_logger(progname: @host, log_level: Logger::WARN)
|
46
|
+
@logger = Logging.logger[@target.host]
|
58
47
|
end
|
59
48
|
|
60
|
-
def
|
61
|
-
@
|
62
|
-
result = _upload(source, destination)
|
63
|
-
if result.success?
|
64
|
-
Bolt::Result.new(nil, "Uploaded '#{source}' to '#{host}:#{destination}'")
|
65
|
-
else
|
66
|
-
result
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def run_command(command)
|
71
|
-
_run_command(command)
|
72
|
-
end
|
73
|
-
|
74
|
-
def run_script(script, arguments)
|
75
|
-
_run_script(script, arguments)
|
76
|
-
end
|
77
|
-
|
78
|
-
def run_task(task, input_method, arguments)
|
79
|
-
_run_task(task, input_method, arguments)
|
49
|
+
def uri
|
50
|
+
@target.uri
|
80
51
|
end
|
81
52
|
end
|
82
53
|
end
|
data/lib/bolt/node/orch.rb
CHANGED
@@ -43,13 +43,13 @@ module Bolt
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
46
|
+
def run_task(task, _input_method, arguments)
|
47
47
|
body = { task: task_name_from_path(task),
|
48
48
|
environment: @orch_task_environment,
|
49
49
|
noop: arguments['_noop'],
|
50
50
|
params: arguments.reject { |k, _| k == '_noop' },
|
51
51
|
scope: {
|
52
|
-
nodes: [@host]
|
52
|
+
nodes: [@target.host]
|
53
53
|
} }
|
54
54
|
# Should we handle errors here or let them propagate?
|
55
55
|
results = client.run_task(body)
|
@@ -57,53 +57,47 @@ module Bolt
|
|
57
57
|
state = node_result['state']
|
58
58
|
result = node_result['result']
|
59
59
|
|
60
|
-
|
61
|
-
|
60
|
+
# If it's finished or already has a proper error simply pass it to the
|
61
|
+
# the result otherwise make sure an error is generated
|
62
|
+
if state == 'finished' || result['_error']
|
63
|
+
Bolt::Result.new(@target, value: result)
|
62
64
|
elsif state == 'skipped'
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
),
|
71
|
-
nil,
|
72
|
-
nil
|
65
|
+
Bolt::Result.new(
|
66
|
+
@target,
|
67
|
+
value: { '_error' => {
|
68
|
+
'kind' => 'puppetlabs.tasks/skipped-node',
|
69
|
+
'msg' => "Node #{@target.host} was skipped",
|
70
|
+
'details' => {}
|
71
|
+
} }
|
73
72
|
)
|
74
73
|
else
|
75
|
-
#
|
76
|
-
|
77
|
-
exit_code = result['_error']['details']['exit_code'] || 'unknown'
|
78
|
-
rescue NoMethodError
|
79
|
-
exit_code = 'unknown'
|
80
|
-
end
|
74
|
+
# Make a generic error with a unkown exit_code
|
75
|
+
Bolt::Result.for_task(@target, result.to_json, '', 'unknown')
|
81
76
|
end
|
82
|
-
Bolt::TaskResult.new(result.to_json, "", exit_code)
|
83
77
|
end
|
84
78
|
|
85
79
|
# run_task generates a result that makes sense for a generic task which
|
86
80
|
# needs to be unwrapped to extract stdout/stderr/exitcode.
|
87
81
|
#
|
88
82
|
def unwrap_bolt_result(result)
|
89
|
-
if result.
|
83
|
+
if result.error_hash
|
90
84
|
# something went wrong return the failure
|
91
85
|
return result
|
92
86
|
end
|
93
87
|
|
94
|
-
Bolt::
|
88
|
+
Bolt::Result.for_command(@target, result.value['stdout'], result.value['stderr'], result.value['exit_code'])
|
95
89
|
end
|
96
90
|
|
97
|
-
def
|
98
|
-
result =
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
91
|
+
def run_command(command, options = {})
|
92
|
+
result = run_task(BOLT_MOCK_FILE,
|
93
|
+
'stdin',
|
94
|
+
action: 'command',
|
95
|
+
command: command,
|
96
|
+
options: options)
|
103
97
|
unwrap_bolt_result(result)
|
104
98
|
end
|
105
99
|
|
106
|
-
def
|
100
|
+
def upload(source, destination)
|
107
101
|
content = File.open(source, &:read)
|
108
102
|
content = Base64.encode64(content)
|
109
103
|
mode = File.stat(source).mode
|
@@ -113,12 +107,12 @@ module Bolt
|
|
113
107
|
content: content,
|
114
108
|
mode: mode
|
115
109
|
}
|
116
|
-
result =
|
117
|
-
result = Bolt::Result.
|
110
|
+
result = run_task(BOLT_MOCK_FILE, 'stdin', params)
|
111
|
+
result = Bolt::Result.for_upload(@target, source, destination) unless result.error_hash
|
118
112
|
result
|
119
113
|
end
|
120
114
|
|
121
|
-
def
|
115
|
+
def run_script(script, arguments)
|
122
116
|
content = File.open(script, &:read)
|
123
117
|
content = Base64.encode64(content)
|
124
118
|
params = {
|
@@ -126,7 +120,7 @@ module Bolt
|
|
126
120
|
content: content,
|
127
121
|
arguments: arguments
|
128
122
|
}
|
129
|
-
unwrap_bolt_result(
|
123
|
+
unwrap_bolt_result(run_task(BOLT_MOCK_FILE, 'stdin', params))
|
130
124
|
end
|
131
125
|
end
|
132
126
|
end
|