bolt 1.8.1 → 1.9.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 +3 -2
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +1 -1
- data/lib/bolt/applicator.rb +1 -0
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/puppetdb.rb +2 -0
- data/lib/bolt/puppetdb/client.rb +36 -2
- data/lib/bolt/puppetdb/config.rb +31 -7
- data/lib/bolt/target.rb +2 -0
- data/lib/bolt/transport/api.rb +28 -0
- data/lib/bolt/transport/api/connection.rb +70 -0
- data/lib/bolt/transport/local.rb +9 -5
- data/lib/bolt/transport/local/shell.rb +1 -1
- data/lib/bolt/version.rb +1 -1
- data/lib/plan_executor/app.rb +2 -1
- data/lib/plan_executor/executor.rb +194 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5818403d3dac52071b37dc23c99dc29a74c790493ea192b1f1855ca93cda721a
|
4
|
+
data.tar.gz: '019a929cfd4a7bfd2ce2bd51cbf4fa4e48cea6bb577213f68ae33dabaa704aa4'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 352f34c5cdef73ccde6a84df1fbf722a562b251801a9d9d49c7b221237de7a03758ba57139559fb07f8124ce3f19d4723b7313725bcdde0fa88f9cb1eeaf64e6
|
7
|
+
data.tar.gz: 35a9b64a4c97465b4f72e0ad7e7b98e2fdf657b08b89653a024e46d5ef78fa0fd44f5e9db66f97f1f9b1fed399d29a277461e9b8e63c9601307b9811f138a823
|
@@ -63,18 +63,19 @@ Puppet::Functions.create_function(:apply_prep) do
|
|
63
63
|
need_install, installed = versions.partition { |r| r['version'].nil? }
|
64
64
|
installed.each do |r|
|
65
65
|
Puppet.debug "Puppet Agent #{r['version']} installed on #{r.target.name}"
|
66
|
+
inventory.set_feature(r.target, 'puppet-agent')
|
66
67
|
end
|
67
68
|
|
68
69
|
unless need_install.empty?
|
69
70
|
need_install_targets = need_install.map(&:target)
|
70
71
|
run_task(executor, need_install_targets, 'puppet_agent::install')
|
71
|
-
|
72
|
+
# Service task works best when targets have puppet-agent feature
|
73
|
+
need_install_targets.each { |target| inventory.set_feature(target, 'puppet-agent') }
|
72
74
|
# Ensure the Puppet service is stopped after new install
|
73
75
|
run_task(executor, need_install_targets, 'service', 'action' => 'stop', 'name' => 'puppet')
|
74
76
|
run_task(executor, need_install_targets, 'service', 'action' => 'disable', 'name' => 'puppet')
|
75
77
|
end
|
76
78
|
end
|
77
|
-
targets.each { |target| inventory.set_feature(target, 'puppet-agent') }
|
78
79
|
|
79
80
|
# Gather facts, including custom facts
|
80
81
|
plugins = applicator.build_plugin_tarball do |mod|
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Repeat the block until it returns a truthy value. Returns the value.
|
4
4
|
Puppet::Functions.create_function(:'ctrl::do_until') do
|
5
5
|
# @example Run a task until it succeeds
|
6
|
-
# ctrl::do_until || {
|
6
|
+
# ctrl::do_until() || {
|
7
7
|
# run_task('test', $target, _catch_errors => true).ok?
|
8
8
|
# }
|
9
9
|
dispatch :do_until do
|
data/lib/bolt/applicator.rb
CHANGED
data/lib/bolt/inventory/group.rb
CHANGED
@@ -125,7 +125,7 @@ module Bolt
|
|
125
125
|
# Require nodes to be parseable as a Target.
|
126
126
|
begin
|
127
127
|
Target.new(n)
|
128
|
-
rescue
|
128
|
+
rescue Bolt::ParseError => e
|
129
129
|
@logger.debug(e)
|
130
130
|
raise ValidationError.new("Invalid node name #{n}", @name)
|
131
131
|
end
|
data/lib/bolt/puppetdb.rb
CHANGED
data/lib/bolt/puppetdb/client.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'logging'
|
4
5
|
require 'uri'
|
5
6
|
require 'httpclient'
|
6
7
|
|
@@ -11,6 +12,9 @@ module Bolt
|
|
11
12
|
|
12
13
|
def initialize(config)
|
13
14
|
@config = config
|
15
|
+
@bad_urls = []
|
16
|
+
@current_url = nil
|
17
|
+
@logger = Logging.logger[self]
|
14
18
|
end
|
15
19
|
|
16
20
|
def query_certnames(query)
|
@@ -41,22 +45,35 @@ module Bolt
|
|
41
45
|
|
42
46
|
def make_query(query, path = nil)
|
43
47
|
body = JSON.generate(query: query)
|
44
|
-
url = "#{
|
48
|
+
url = "#{uri}/pdb/query/v4"
|
45
49
|
url += "/#{path}" if path
|
46
50
|
|
47
51
|
begin
|
48
52
|
response = http_client.post(url, body: body, header: headers)
|
53
|
+
rescue SocketError, OpenSSL::SSL::SSLError, SystemCallError, Net::ProtocolError, IOError => err
|
54
|
+
raise Bolt::PuppetDBFailoverError, "Failed to query PuppetDB: #{err}"
|
49
55
|
rescue StandardError => err
|
50
56
|
raise Bolt::PuppetDBError, "Failed to query PuppetDB: #{err}"
|
51
57
|
end
|
58
|
+
|
52
59
|
if response.code != 200
|
53
|
-
|
60
|
+
msg = "Failed to query PuppetDB: #{response.body}"
|
61
|
+
if response.code == 400
|
62
|
+
raise Bolt::PuppetDBError, msg
|
63
|
+
else
|
64
|
+
raise Bolt::PuppetDBFailoverError, msg
|
65
|
+
end
|
54
66
|
end
|
67
|
+
|
55
68
|
begin
|
56
69
|
JSON.parse(response.body)
|
57
70
|
rescue JSON::ParserError
|
58
71
|
raise Bolt::PuppetDBError, "Unable to parse response as JSON: #{response.body}"
|
59
72
|
end
|
73
|
+
rescue Bolt::PuppetDBFailoverError => err
|
74
|
+
@logger.error("Request to puppetdb at #{@current_url} failed with #{err}.")
|
75
|
+
reject_url
|
76
|
+
make_query(query, path)
|
60
77
|
end
|
61
78
|
|
62
79
|
def http_client
|
@@ -68,6 +85,23 @@ module Bolt
|
|
68
85
|
@http
|
69
86
|
end
|
70
87
|
|
88
|
+
def reject_url
|
89
|
+
@bad_urls << @current_url if @current_url
|
90
|
+
@current_url = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def uri
|
94
|
+
@current_url ||= (@config.server_urls - @bad_urls).first
|
95
|
+
unless @current_url
|
96
|
+
msg = "Failed to connect to all PuppetDB server_urls: #{@config.server_urls.to_a.join(', ')}."
|
97
|
+
raise Bolt::PuppetDBError, msg
|
98
|
+
end
|
99
|
+
|
100
|
+
uri = URI.parse(@current_url)
|
101
|
+
uri.port ||= 8081
|
102
|
+
uri
|
103
|
+
end
|
104
|
+
|
71
105
|
def headers
|
72
106
|
headers = { 'Content-Type' => 'application/json' }
|
73
107
|
headers['X-Authentication'] = @config.token if @config.token
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -20,6 +20,7 @@ module Bolt
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def self.load_config(filename, options)
|
23
|
+
config = {}
|
23
24
|
global_path = Bolt::Util.windows? ? DEFAULT_CONFIG[:win_global] : DEFAULT_CONFIG[:global]
|
24
25
|
if filename
|
25
26
|
if File.exist?(filename)
|
@@ -27,13 +28,20 @@ module Bolt
|
|
27
28
|
else
|
28
29
|
raise Bolt::PuppetDBError, "config file #{filename} does not exist"
|
29
30
|
end
|
30
|
-
elsif File.exist?(DEFAULT_CONFIG[:user])
|
31
|
-
config = JSON.parse(File.read(DEFAULT_CONFIG[:user]))
|
32
|
-
elsif File.exist?(global_path)
|
33
|
-
config = JSON.parse(File.read(global_path))
|
34
31
|
else
|
35
|
-
|
32
|
+
if File.exist?(DEFAULT_CONFIG[:user])
|
33
|
+
filepath = DEFAULT_CONFIG[:user]
|
34
|
+
elsif File.exist?(global_path)
|
35
|
+
filepath = global_path
|
36
|
+
end
|
37
|
+
|
38
|
+
begin
|
39
|
+
config = JSON.parse(File.read(filepath)) if filepath
|
40
|
+
rescue StandardError => e
|
41
|
+
Logging.logger[self].error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
|
42
|
+
end
|
36
43
|
end
|
44
|
+
|
37
45
|
config = config.fetch('puppetdb', {})
|
38
46
|
new(config.merge(options))
|
39
47
|
end
|
@@ -45,8 +53,11 @@ module Bolt
|
|
45
53
|
|
46
54
|
def token
|
47
55
|
return @token if @token
|
48
|
-
|
49
|
-
|
56
|
+
# Allow nil in config to skip loading a token
|
57
|
+
if @settings.include?('token')
|
58
|
+
if @settings['token']
|
59
|
+
@token = File.read(@settings['token'])
|
60
|
+
end
|
50
61
|
elsif File.exist?(DEFAULT_TOKEN)
|
51
62
|
@token = File.read(DEFAULT_TOKEN)
|
52
63
|
end
|
@@ -66,6 +77,19 @@ module Bolt
|
|
66
77
|
true
|
67
78
|
end
|
68
79
|
|
80
|
+
def server_urls
|
81
|
+
case @settings['server_urls']
|
82
|
+
when String
|
83
|
+
[@settings['server_urls']]
|
84
|
+
when Array
|
85
|
+
@settings['server_urls']
|
86
|
+
when nil
|
87
|
+
raise Bolt::PuppetDBError, "server_urls must be specified"
|
88
|
+
else
|
89
|
+
raise Bolt::PuppetDBError, "server_urls must be a string or array"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
69
93
|
def uri
|
70
94
|
return @uri if @uri
|
71
95
|
uri = case @settings['server_urls']
|
data/lib/bolt/target.rb
CHANGED
@@ -62,6 +62,8 @@ module Bolt
|
|
62
62
|
# Initialize with an empty scheme to ensure we parse the hostname correctly
|
63
63
|
Addressable::URI.parse("//#{string}")
|
64
64
|
end
|
65
|
+
rescue Addressable::URI::InvalidURIError => e
|
66
|
+
raise Bolt::ParseError, "Could not parse target URI: #{e.message}"
|
65
67
|
end
|
66
68
|
private :parse
|
67
69
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/transport/api/connection'
|
4
|
+
|
5
|
+
# A copy of the orchestrator transport which uses the api connection class
|
6
|
+
# in order to bypass calling 'start_plan'
|
7
|
+
module Bolt
|
8
|
+
module Transport
|
9
|
+
class Api < Orch
|
10
|
+
def initialize(*args)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_connection(conn_opts)
|
15
|
+
key = Bolt::Transport::Api::Connection.get_key(conn_opts)
|
16
|
+
unless (conn = @connections[key])
|
17
|
+
@connections[key] = Bolt::Transport::Api::Connection.new(conn_opts, logger)
|
18
|
+
conn = @connections[key]
|
19
|
+
end
|
20
|
+
conn
|
21
|
+
end
|
22
|
+
|
23
|
+
def batches(targets)
|
24
|
+
targets.group_by { |target| Bolt::Transport::Api::Connection.get_key(target.options) }.values
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This is a copy of the orchestrator transport connection, but without the
|
4
|
+
# 'start_plan' call in the init function which is handled by the orchestrator
|
5
|
+
# client
|
6
|
+
module Bolt
|
7
|
+
module Transport
|
8
|
+
class Api < Orch
|
9
|
+
class Connection
|
10
|
+
attr_reader :logger, :key
|
11
|
+
|
12
|
+
CONTEXT_KEYS = Set.new(%i[plan_name description params]).freeze
|
13
|
+
|
14
|
+
def self.get_key(opts)
|
15
|
+
[
|
16
|
+
opts['service-url'],
|
17
|
+
opts['task-environment'],
|
18
|
+
opts['token-file']
|
19
|
+
].join('-')
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(opts, logger)
|
23
|
+
@logger = logger
|
24
|
+
@key = self.class.get_key(opts)
|
25
|
+
client_keys = %w[service-url token-file cacert]
|
26
|
+
client_opts = client_keys.each_with_object({}) do |k, acc|
|
27
|
+
acc[k] = opts[k] if opts.include?(k)
|
28
|
+
end
|
29
|
+
client_opts['User-Agent'] = "Bolt/#{VERSION}"
|
30
|
+
logger.debug("Creating orchestrator client for #{client_opts}")
|
31
|
+
|
32
|
+
@client = OrchestratorClient.new(client_opts, true)
|
33
|
+
@environment = opts["task-environment"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def finish_plan(plan_result)
|
37
|
+
if @plan_job
|
38
|
+
@client.command.plan_finish(
|
39
|
+
plan_job: @plan_job,
|
40
|
+
result: plan_result.value || '',
|
41
|
+
status: plan_result.status
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_request(targets, task, arguments, description = nil)
|
47
|
+
body = { task: task.name,
|
48
|
+
environment: @environment,
|
49
|
+
noop: arguments['_noop'],
|
50
|
+
params: arguments.reject { |k, _| k.start_with?('_') },
|
51
|
+
scope: {
|
52
|
+
nodes: targets.map(&:host)
|
53
|
+
} }
|
54
|
+
body[:description] = description if description
|
55
|
+
body[:plan_job] = @plan_job if @plan_job
|
56
|
+
body
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_task(targets, task, arguments, options)
|
60
|
+
body = build_request(targets, task, arguments, options['_description'])
|
61
|
+
@client.run_task(body)
|
62
|
+
end
|
63
|
+
|
64
|
+
def query_inventory(targets)
|
65
|
+
@client.post('inventory', nodes: targets.map(&:host))
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/bolt/transport/local.rb
CHANGED
@@ -36,11 +36,15 @@ module Bolt
|
|
36
36
|
|
37
37
|
def in_tmpdir(base)
|
38
38
|
args = base ? [nil, base] : []
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
dir = begin
|
40
|
+
Dir.mktmpdir(*args)
|
41
|
+
rescue StandardError => e
|
42
|
+
raise Bolt::Node::FileError.new("Could not make tempdir: #{e.message}", 'TEMPDIR_ERROR')
|
43
|
+
end
|
44
|
+
|
45
|
+
yield dir
|
46
|
+
ensure
|
47
|
+
FileUtils.remove_entry dir if dir
|
44
48
|
end
|
45
49
|
private :in_tmpdir
|
46
50
|
|
@@ -19,7 +19,7 @@ module Bolt
|
|
19
19
|
result_output = Bolt::Node::Output.new
|
20
20
|
result_output.stdout << stdout unless stdout.nil?
|
21
21
|
result_output.stderr << stderr unless stderr.nil?
|
22
|
-
result_output.exit_code = rc.
|
22
|
+
result_output.exit_code = rc.exitstatus
|
23
23
|
result_output
|
24
24
|
end
|
25
25
|
end
|
data/lib/bolt/version.rb
CHANGED
data/lib/plan_executor/app.rb
CHANGED
@@ -8,6 +8,7 @@ require 'bolt/inventory'
|
|
8
8
|
require 'bolt/pal'
|
9
9
|
require 'bolt/puppetdb'
|
10
10
|
require 'plan_executor/applicator'
|
11
|
+
require 'plan_executor/executor'
|
11
12
|
require 'concurrent'
|
12
13
|
require 'json'
|
13
14
|
require 'json-schema'
|
@@ -31,7 +32,7 @@ module PlanExecutor
|
|
31
32
|
@worker = Concurrent::SingleThreadExecutor.new
|
32
33
|
|
33
34
|
# Create a basic executor, leave concurrency up to Orchestrator.
|
34
|
-
@executor = executor ||
|
35
|
+
@executor = executor || PlanExecutor::Executor.new(0)
|
35
36
|
# Use an empty inventory until we figure out where this data comes from.
|
36
37
|
@inventory = Bolt::Inventory.new(nil)
|
37
38
|
# TODO: what should max compiles be set to for apply?
|
@@ -0,0 +1,194 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Used for $ERROR_INFO. This *must* be capitalized!
|
4
|
+
require 'English'
|
5
|
+
require 'json'
|
6
|
+
require 'logging'
|
7
|
+
require 'set'
|
8
|
+
require 'bolt/result'
|
9
|
+
require 'bolt/config'
|
10
|
+
require 'bolt/transport/api'
|
11
|
+
require 'bolt/notifier'
|
12
|
+
require 'bolt/result_set'
|
13
|
+
require 'bolt/puppetdb'
|
14
|
+
|
15
|
+
module PlanExecutor
|
16
|
+
class Executor
|
17
|
+
attr_reader :noop, :transport
|
18
|
+
|
19
|
+
def initialize(noop = nil)
|
20
|
+
@logger = Logging.logger[self]
|
21
|
+
@plan_logging = false
|
22
|
+
@noop = noop
|
23
|
+
@logger.debug { "Started" }
|
24
|
+
@notifier = Bolt::Notifier.new
|
25
|
+
@transport = Bolt::Transport::Api.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# This handles running the job, catching errors, and turning the result
|
29
|
+
# into a result set
|
30
|
+
def execute(targets)
|
31
|
+
result_array = begin
|
32
|
+
yield
|
33
|
+
rescue StandardError => e
|
34
|
+
@logger.warn(e)
|
35
|
+
# CODEREVIEW how should we fail if there's an error?
|
36
|
+
Array(Bolt::Result.from_exception(targets[0], e))
|
37
|
+
end
|
38
|
+
Bolt::ResultSet.new(result_array)
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO: Remove in favor of service logging
|
42
|
+
def log_action(description, targets)
|
43
|
+
# When running a plan, info messages like starting a task are promoted to notice.
|
44
|
+
log_method = @plan_logging ? :notice : :info
|
45
|
+
target_str = if targets.length > 5
|
46
|
+
"#{targets.count} targets"
|
47
|
+
else
|
48
|
+
targets.map(&:uri).join(', ')
|
49
|
+
end
|
50
|
+
|
51
|
+
@logger.send(log_method, "Starting: #{description} on #{target_str}")
|
52
|
+
|
53
|
+
start_time = Time.now
|
54
|
+
results = yield
|
55
|
+
duration = Time.now - start_time
|
56
|
+
|
57
|
+
failures = results.error_set.length
|
58
|
+
plural = failures == 1 ? '' : 's'
|
59
|
+
|
60
|
+
@logger.send(log_method, "Finished: #{description} with #{failures} failure#{plural} in #{duration.round(2)} sec")
|
61
|
+
|
62
|
+
results
|
63
|
+
end
|
64
|
+
|
65
|
+
def log_plan(plan_name)
|
66
|
+
log_method = @plan_logging ? :notice : :info
|
67
|
+
@logger.send(log_method, "Starting: plan #{plan_name}")
|
68
|
+
start_time = Time.now
|
69
|
+
|
70
|
+
results = nil
|
71
|
+
begin
|
72
|
+
results = yield
|
73
|
+
ensure
|
74
|
+
duration = Time.now - start_time
|
75
|
+
@logger.send(log_method, "Finished: plan #{plan_name} in #{duration.round(2)} sec")
|
76
|
+
end
|
77
|
+
|
78
|
+
results
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_command(targets, command, options = {}, &callback)
|
82
|
+
description = options.fetch('_description', "command '#{command}'")
|
83
|
+
log_action(description, targets) do
|
84
|
+
notify = proc { |event| @notifier.notify(callback, event) if callback }
|
85
|
+
|
86
|
+
results = execute(targets) do
|
87
|
+
@transport.batch_command(targets, command, options, ¬ify)
|
88
|
+
end
|
89
|
+
|
90
|
+
@notifier.shutdown
|
91
|
+
results
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def run_script(targets, script, arguments, options = {}, &callback)
|
96
|
+
description = options.fetch('_description', "script #{script}")
|
97
|
+
log_action(description, targets) do
|
98
|
+
notify = proc { |event| @notifier.notify(callback, event) if callback }
|
99
|
+
|
100
|
+
results = execute(targets) do
|
101
|
+
@transport.batch_script(targets, script, arguments, options, ¬ify)
|
102
|
+
end
|
103
|
+
|
104
|
+
@notifier.shutdown
|
105
|
+
results
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def run_task(targets, task, arguments, options = {}, &callback)
|
110
|
+
description = options.fetch('_description', "task #{task.name}")
|
111
|
+
log_action(description, targets) do
|
112
|
+
notify = proc { |event| @notifier.notify(callback, event) if callback }
|
113
|
+
|
114
|
+
arguments['_task'] = task.name
|
115
|
+
|
116
|
+
results = execute(targets) do
|
117
|
+
@transport.batch_task(targets, task, arguments, options, ¬ify)
|
118
|
+
end
|
119
|
+
|
120
|
+
@notifier.shutdown
|
121
|
+
results
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def upload_file(targets, source, destination, options = {}, &callback)
|
126
|
+
description = options.fetch('_description', "file upload from #{source} to #{destination}")
|
127
|
+
log_action(description, targets) do
|
128
|
+
notify = proc { |event| @notifier.notify(callback, event) if callback }
|
129
|
+
|
130
|
+
results = execute(targets) do
|
131
|
+
@transport.batch_upload(targets, source, destination, options, ¬ify)
|
132
|
+
end
|
133
|
+
|
134
|
+
@notifier.shutdown
|
135
|
+
results
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class TimeoutError < RuntimeError; end
|
140
|
+
|
141
|
+
def wait_until_available(targets,
|
142
|
+
description: 'wait until available',
|
143
|
+
wait_time: 120,
|
144
|
+
retry_interval: 1)
|
145
|
+
log_action(description, targets) do
|
146
|
+
begin
|
147
|
+
wait_until(wait_time, retry_interval) { @transport.batch_connected?(targets) }
|
148
|
+
targets.map { |target| Bolt::Result.new(target) }
|
149
|
+
rescue TimeoutError => e
|
150
|
+
targets.map { |target| Bolt::Result.from_exception(target, e) }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def wait_until(timeout, retry_interval)
|
156
|
+
start = wait_now
|
157
|
+
until yield
|
158
|
+
raise(TimeoutError, 'Timed out waiting for target') if (wait_now - start).to_i >= timeout
|
159
|
+
sleep(retry_interval)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Plan context doesn't make sense for most transports but it is tightly
|
164
|
+
# coupled with the orchestrator transport since the transport behaves
|
165
|
+
# differently when a plan is running. In order to limit how much this
|
166
|
+
# pollutes the transport API we only handle the orchestrator transport here.
|
167
|
+
# Since we callt this function without resolving targets this will result
|
168
|
+
# in the orchestrator transport always being initialized during plan runs.
|
169
|
+
# For now that's ok.
|
170
|
+
#
|
171
|
+
# In the future if other transports need this or if we want a plan stack
|
172
|
+
# we'll need to refactor.
|
173
|
+
def start_plan(plan_context)
|
174
|
+
@transport.plan_context = plan_context
|
175
|
+
@plan_logging = true
|
176
|
+
end
|
177
|
+
|
178
|
+
def finish_plan(plan_result)
|
179
|
+
@transport.finish_plan(plan_result)
|
180
|
+
end
|
181
|
+
|
182
|
+
def without_default_logging
|
183
|
+
old_log = @plan_logging
|
184
|
+
@plan_logging = false
|
185
|
+
yield
|
186
|
+
ensure
|
187
|
+
@plan_logging = old_log
|
188
|
+
end
|
189
|
+
|
190
|
+
def report_bundled_content(mode, name); end
|
191
|
+
|
192
|
+
def report_function_call(function); end
|
193
|
+
end
|
194
|
+
end
|
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: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-01-
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -357,6 +357,8 @@ files:
|
|
357
357
|
- lib/bolt/task.rb
|
358
358
|
- lib/bolt/task/puppet_server.rb
|
359
359
|
- lib/bolt/task/remote.rb
|
360
|
+
- lib/bolt/transport/api.rb
|
361
|
+
- lib/bolt/transport/api/connection.rb
|
360
362
|
- lib/bolt/transport/base.rb
|
361
363
|
- lib/bolt/transport/docker.rb
|
362
364
|
- lib/bolt/transport/docker/connection.rb
|
@@ -393,6 +395,7 @@ files:
|
|
393
395
|
- lib/plan_executor/app.rb
|
394
396
|
- lib/plan_executor/applicator.rb
|
395
397
|
- lib/plan_executor/config.rb
|
398
|
+
- lib/plan_executor/executor.rb
|
396
399
|
- lib/plan_executor/schemas/run_plan.json
|
397
400
|
- libexec/apply_catalog.rb
|
398
401
|
- libexec/bolt_catalog
|