bolt 2.8.0 → 2.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/types/planresult.pp +11 -1
- data/lib/bolt/cli.rb +20 -0
- data/lib/bolt/config.rb +2 -0
- data/lib/bolt/config/transport/docker.rb +2 -0
- data/lib/bolt/config/transport/local.rb +2 -0
- data/lib/bolt/config/transport/orch.rb +2 -0
- data/lib/bolt/config/transport/remote.rb +2 -0
- data/lib/bolt/config/transport/ssh.rb +3 -0
- data/lib/bolt/config/transport/winrm.rb +2 -0
- data/lib/bolt/inventory/group.rb +1 -0
- data/lib/bolt/outputter/human.rb +5 -4
- data/lib/bolt/outputter/json.rb +1 -1
- data/lib/bolt/pal.rb +2 -1
- data/lib/bolt/rerun.rb +1 -1
- data/lib/bolt/result.rb +46 -23
- data/lib/bolt/result_set.rb +2 -5
- data/lib/bolt/shell/powershell.rb +12 -4
- data/lib/bolt/transport/winrm/connection.rb +2 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +7 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed1409405ce8a0e0367aea1cb1b7e2d194546c594b96932288c9ae81cef73209
|
4
|
+
data.tar.gz: 0cfb52ce0dbeffb427988da5e3e338649d2f65defbeef133f01c745802661ac6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 986a57cd4c6b101ce69f11dec9b6f95212129c2930aded3d0b0c0d8243bcf98ef66b981af0398ecb79cea291603483057c3df7f82e56fa6ab40f7981f4f7ae0d
|
7
|
+
data.tar.gz: 9ff02c10dd26709f6027c2d9ab45b051d67c0de62c87abe974faee9db28cf2ddde9e086f8cf83e2765bf43e1ff64690d849ad7e032fdc3cbce9990680f5f55f9
|
@@ -2,4 +2,14 @@
|
|
2
2
|
# should be used as the return type of functions that run plans and return the
|
3
3
|
# results.
|
4
4
|
|
5
|
-
type Boltlib::PlanResult = Variant[Boolean,
|
5
|
+
type Boltlib::PlanResult = Variant[Boolean,
|
6
|
+
Numeric,
|
7
|
+
String,
|
8
|
+
Undef,
|
9
|
+
Error,
|
10
|
+
Result,
|
11
|
+
ApplyResult,
|
12
|
+
ResultSet,
|
13
|
+
Target,
|
14
|
+
Array[Boltlib::PlanResult],
|
15
|
+
Hash[String, Boltlib::PlanResult]]
|
data/lib/bolt/cli.rb
CHANGED
@@ -804,6 +804,20 @@ module Bolt
|
|
804
804
|
end
|
805
805
|
|
806
806
|
def bundled_content
|
807
|
+
# If the bundled content directory is empty, Bolt is likely installed as a gem.
|
808
|
+
if ENV['BOLT_GEM'].nil? && incomplete_install?
|
809
|
+
msg = <<~MSG.chomp
|
810
|
+
Bolt may be installed as a gem. To use Bolt reliably and with all of its
|
811
|
+
dependencies, uninstall the 'bolt' gem and install Bolt as a package:
|
812
|
+
https://puppet.com/docs/bolt/latest/bolt_installing.html
|
813
|
+
|
814
|
+
If you meant to install Bolt as a gem and want to disable this warning,
|
815
|
+
set the BOLT_GEM environment variable.
|
816
|
+
MSG
|
817
|
+
|
818
|
+
@logger.warn(msg)
|
819
|
+
end
|
820
|
+
|
807
821
|
# We only need to enumerate bundled content when running a task or plan
|
808
822
|
content = { 'Plan' => [],
|
809
823
|
'Task' => [],
|
@@ -827,5 +841,11 @@ module Bolt
|
|
827
841
|
MSG
|
828
842
|
@logger.debug(msg)
|
829
843
|
end
|
844
|
+
|
845
|
+
# Gem installs include the aggregate, canary, and puppetdb_fact modules, while
|
846
|
+
# package installs include modules listed in the Bolt repo Puppetfile
|
847
|
+
def incomplete_install?
|
848
|
+
(Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact]).empty?
|
849
|
+
end
|
830
850
|
end
|
831
851
|
end
|
data/lib/bolt/config.rb
CHANGED
@@ -34,6 +34,8 @@ module Bolt
|
|
34
34
|
'remote' => Bolt::Config::Transport::Remote
|
35
35
|
}.freeze
|
36
36
|
|
37
|
+
# NOTE: All configuration options should have a corresponding schema property
|
38
|
+
# in schemas/bolt-config.schema.json
|
37
39
|
OPTIONS = {
|
38
40
|
"apply_settings" => "A map of Puppet settings to use when applying Puppet code",
|
39
41
|
"color" => "Whether to use colored output when printing messages to the console.",
|
@@ -7,6 +7,8 @@ module Bolt
|
|
7
7
|
class Config
|
8
8
|
module Transport
|
9
9
|
class Docker < Base
|
10
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
11
|
+
# in schemas/bolt-transport-definitions.json
|
10
12
|
OPTIONS = {
|
11
13
|
"cleanup" => { type: TrueClass,
|
12
14
|
desc: "Whether to clean up temporary files created on targets." },
|
@@ -7,6 +7,8 @@ module Bolt
|
|
7
7
|
class Config
|
8
8
|
module Transport
|
9
9
|
class Local < Base
|
10
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
11
|
+
# in schemas/bolt-transport-definitions.json
|
10
12
|
OPTIONS = {
|
11
13
|
"cleanup" => { type: TrueClass,
|
12
14
|
desc: "Whether to clean up temporary files created on targets." },
|
@@ -7,6 +7,8 @@ module Bolt
|
|
7
7
|
class Config
|
8
8
|
module Transport
|
9
9
|
class Orch < Base
|
10
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
11
|
+
# in schemas/bolt-transport-definitions.json
|
10
12
|
OPTIONS = {
|
11
13
|
"cacert" => { type: String,
|
12
14
|
desc: "The path to the CA certificate." },
|
@@ -7,6 +7,8 @@ module Bolt
|
|
7
7
|
class Config
|
8
8
|
module Transport
|
9
9
|
class Remote < Base
|
10
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
11
|
+
# in schemas/bolt-transport-definitions.json
|
10
12
|
OPTIONS = {
|
11
13
|
"run-on" => { type: String,
|
12
14
|
desc: "The proxy target that the task executes on." }
|
@@ -8,6 +8,9 @@ module Bolt
|
|
8
8
|
module Transport
|
9
9
|
class SSH < Base
|
10
10
|
LOGIN_SHELLS = %w[sh bash zsh dash ksh powershell].freeze
|
11
|
+
|
12
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
13
|
+
# in schemas/bolt-transport-definitions.json
|
11
14
|
OPTIONS = {
|
12
15
|
"cleanup" => { type: TrueClass,
|
13
16
|
desc: "Whether to clean up temporary files created on targets." },
|
@@ -7,6 +7,8 @@ module Bolt
|
|
7
7
|
class Config
|
8
8
|
module Transport
|
9
9
|
class WinRM < Base
|
10
|
+
# NOTE: All transport configuration options should have a corresponding schema definition
|
11
|
+
# in schemas/bolt-transport-definitions.json
|
10
12
|
OPTIONS = {
|
11
13
|
"basic-auth-only" => { type: TrueClass,
|
12
14
|
desc: "Force basic authentication. This option is only available when using SSL." },
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -12,6 +12,7 @@ module Bolt
|
|
12
12
|
# Regex used to validate group names and target aliases.
|
13
13
|
NAME_REGEX = /\A[a-z0-9_][a-z0-9_-]*\Z/.freeze
|
14
14
|
|
15
|
+
# NOTE: All keys should have a corresponding schema property in schemas/bolt-inventory.schema.json
|
15
16
|
DATA_KEYS = %w[config facts vars features plugin_hooks].freeze
|
16
17
|
TARGET_KEYS = DATA_KEYS + %w[name alias uri]
|
17
18
|
GROUP_KEYS = DATA_KEYS + %w[name groups targets]
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -107,13 +107,14 @@ module Bolt
|
|
107
107
|
|
108
108
|
# Use special handling if the result looks like a command or script result
|
109
109
|
if result.generic_value.keys == %w[stdout stderr exit_code]
|
110
|
-
|
110
|
+
safe_value = result.safe_value
|
111
|
+
unless safe_value['stdout'].strip.empty?
|
111
112
|
@stream.puts(indent(2, "STDOUT:"))
|
112
|
-
@stream.puts(indent(4,
|
113
|
+
@stream.puts(indent(4, safe_value['stdout']))
|
113
114
|
end
|
114
|
-
unless
|
115
|
+
unless safe_value['stderr'].strip.empty?
|
115
116
|
@stream.puts(indent(2, "STDERR:"))
|
116
|
-
@stream.puts(indent(4,
|
117
|
+
@stream.puts(indent(4, safe_value['stderr']))
|
117
118
|
end
|
118
119
|
elsif result.generic_value.any?
|
119
120
|
@stream.puts(indent(2, ::JSON.pretty_generate(result.generic_value)))
|
data/lib/bolt/outputter/json.rb
CHANGED
data/lib/bolt/pal.rb
CHANGED
@@ -329,7 +329,8 @@ module Bolt
|
|
329
329
|
raise Bolt::Error.unknown_plan(plan_name)
|
330
330
|
end
|
331
331
|
|
332
|
-
|
332
|
+
# path may be a Pathname object, so make sure to stringify it
|
333
|
+
mod = plan_sig.instance_variable_get(:@plan_func).loader.parent.path.to_s
|
333
334
|
|
334
335
|
# If it's a Puppet language plan, use strings to extract data. The only
|
335
336
|
# way to tell is to check which filename exists in the module.
|
data/lib/bolt/rerun.rb
CHANGED
@@ -45,7 +45,7 @@ module Bolt
|
|
45
45
|
end
|
46
46
|
|
47
47
|
if result_set.is_a?(Bolt::ResultSet)
|
48
|
-
data = result_set.map { |res| res.
|
48
|
+
data = result_set.map { |res| { target: res.target.name, status: res.status } }
|
49
49
|
FileUtils.mkdir_p(File.dirname(@path))
|
50
50
|
File.write(@path, data.to_json)
|
51
51
|
elsif File.exist?(@path)
|
data/lib/bolt/result.rb
CHANGED
@@ -40,18 +40,23 @@ module Bolt
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def self.for_task(target, stdout, stderr, exit_code, task)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
43
|
+
stdout.force_encoding('utf-8') unless stdout.encoding == Encoding::UTF_8
|
44
|
+
value = if stdout.valid_encoding?
|
45
|
+
parse_hash(stdout) || { '_output' => stdout }
|
46
|
+
else
|
47
|
+
{ '_error' => { 'kind' => 'puppetlabs.tasks/task-error',
|
48
|
+
'issue_code' => 'TASK_ERROR',
|
49
|
+
'msg' => 'The task result contained invalid UTF-8 on stdout',
|
50
|
+
'details' => {} } }
|
51
|
+
end
|
52
|
+
|
52
53
|
if exit_code != 0 && value['_error'].nil?
|
53
54
|
msg = if stdout.empty?
|
54
|
-
|
55
|
+
if stderr.empty?
|
56
|
+
"The task failed with exit code #{exit_code} and no output"
|
57
|
+
else
|
58
|
+
"The task failed with exit code #{exit_code} and no stdout, but stderr contained:\n#{stderr}"
|
59
|
+
end
|
55
60
|
else
|
56
61
|
"The task failed with exit code #{exit_code}"
|
57
62
|
end
|
@@ -63,6 +68,13 @@ module Bolt
|
|
63
68
|
new(target, value: value, action: 'task', object: task)
|
64
69
|
end
|
65
70
|
|
71
|
+
def self.parse_hash(string)
|
72
|
+
value = JSON.parse(string)
|
73
|
+
value if value.is_a? Hash
|
74
|
+
rescue JSON::ParserError
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
66
78
|
def self.for_upload(target, source, destination)
|
67
79
|
new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'", action: 'upload', object: source)
|
68
80
|
end
|
@@ -110,18 +122,8 @@ module Bolt
|
|
110
122
|
message && !message.strip.empty?
|
111
123
|
end
|
112
124
|
|
113
|
-
def status_hash
|
114
|
-
{
|
115
|
-
target: @target.name,
|
116
|
-
action: action,
|
117
|
-
object: object,
|
118
|
-
status: status,
|
119
|
-
value: @value
|
120
|
-
}
|
121
|
-
end
|
122
|
-
|
123
125
|
def generic_value
|
124
|
-
|
126
|
+
safe_value.reject { |k, _| %w[_error _output].include? k }
|
125
127
|
end
|
126
128
|
|
127
129
|
def eql?(other)
|
@@ -139,15 +141,36 @@ module Bolt
|
|
139
141
|
end
|
140
142
|
|
141
143
|
def to_json(opts = nil)
|
142
|
-
|
144
|
+
to_data.to_json(opts)
|
143
145
|
end
|
144
146
|
|
145
147
|
def to_s
|
146
148
|
to_json
|
147
149
|
end
|
148
150
|
|
151
|
+
# This is the value with all non-UTF-8 characters removed, suitable for
|
152
|
+
# printing or converting to JSON. It *should* only be possible to have
|
153
|
+
# non-UTF-8 characters in stdout/stderr keys as they are not allowed from
|
154
|
+
# tasks but we scrub the whole thing just in case.
|
155
|
+
def safe_value
|
156
|
+
Bolt::Util.walk_vals(value) do |val|
|
157
|
+
if val.is_a?(String)
|
158
|
+
# Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
|
159
|
+
val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
|
160
|
+
else
|
161
|
+
val
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
149
166
|
def to_data
|
150
|
-
|
167
|
+
{
|
168
|
+
"target" => @target.name,
|
169
|
+
"action" => action,
|
170
|
+
"object" => object,
|
171
|
+
"status" => status,
|
172
|
+
"value" => safe_value
|
173
|
+
}
|
151
174
|
end
|
152
175
|
|
153
176
|
def status
|
data/lib/bolt/result_set.rb
CHANGED
@@ -99,17 +99,14 @@ module Bolt
|
|
99
99
|
self.class == other.class && @results == other.results
|
100
100
|
end
|
101
101
|
|
102
|
-
def to_a
|
103
|
-
@results.map(&:status_hash)
|
104
|
-
end
|
105
|
-
|
106
102
|
def to_json(opts = nil)
|
107
|
-
|
103
|
+
to_data.to_json(opts)
|
108
104
|
end
|
109
105
|
|
110
106
|
def to_data
|
111
107
|
@results.map(&:to_data)
|
112
108
|
end
|
109
|
+
alias to_a to_data
|
113
110
|
|
114
111
|
def to_s
|
115
112
|
to_json
|
@@ -267,10 +267,18 @@ module Bolt
|
|
267
267
|
|
268
268
|
result = Bolt::Node::Output.new
|
269
269
|
inp.close
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
270
|
+
stdout = Thread.new do
|
271
|
+
# Set to binmode to preserve \r\n line endings, but save and restore
|
272
|
+
# the proper encoding so the string isn't later misinterpreted
|
273
|
+
encoding = out.external_encoding
|
274
|
+
out.binmode
|
275
|
+
result.stdout << out.read.force_encoding(encoding)
|
276
|
+
end
|
277
|
+
stderr = Thread.new do
|
278
|
+
encoding = err.external_encoding
|
279
|
+
err.binmode
|
280
|
+
result.stderr << err.read.force_encoding(encoding)
|
281
|
+
end
|
274
282
|
|
275
283
|
stdout.join
|
276
284
|
stderr.join
|
@@ -108,8 +108,8 @@ module Bolt
|
|
108
108
|
# it will fail if the shell attempts to provide stdin
|
109
109
|
inp.close
|
110
110
|
|
111
|
-
out_rd, out_wr = IO.pipe
|
112
|
-
err_rd, err_wr = IO.pipe
|
111
|
+
out_rd, out_wr = IO.pipe('UTF-8')
|
112
|
+
err_rd, err_wr = IO.pipe('UTF-8')
|
113
113
|
th = Thread.new do
|
114
114
|
result = @session.run(command)
|
115
115
|
out_wr << result.stdout
|
data/lib/bolt/version.rb
CHANGED
@@ -57,8 +57,8 @@ module BoltServer
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def scrub_stack_trace(result)
|
60
|
-
if result.dig(
|
61
|
-
result[
|
60
|
+
if result.dig('value', '_error', 'details', 'stack_trace')
|
61
|
+
result['value']['_error']['details'].reject! { |k| k == 'stack_trace' }
|
62
62
|
end
|
63
63
|
result
|
64
64
|
end
|
@@ -87,14 +87,14 @@ module BoltServer
|
|
87
87
|
# If the `result_set` contains only one item, it will be returned
|
88
88
|
# as a single result object. Set `aggregate` to treat it as a set
|
89
89
|
# of results with length 1 instead.
|
90
|
-
def
|
90
|
+
def result_set_to_data(result_set, aggregate: false)
|
91
91
|
scrubbed_results = result_set.map do |result|
|
92
|
-
scrub_stack_trace(result.
|
92
|
+
scrub_stack_trace(result.to_data)
|
93
93
|
end
|
94
94
|
|
95
95
|
if aggregate || scrubbed_results.length > 1
|
96
96
|
# For actions that act on multiple targets, construct a status hash for the aggregate result
|
97
|
-
all_succeeded = scrubbed_results.all? { |r| r[
|
97
|
+
all_succeeded = scrubbed_results.all? { |r| r['status'] == 'success' }
|
98
98
|
{
|
99
99
|
status: all_succeeded ? 'success' : 'failure',
|
100
100
|
result: scrubbed_results
|
@@ -297,7 +297,7 @@ module BoltServer
|
|
297
297
|
return [400, error.to_json] unless error.nil?
|
298
298
|
|
299
299
|
aggregate = body['target'].nil?
|
300
|
-
[200,
|
300
|
+
[200, result_set_to_data(result_set, aggregate: aggregate).to_json]
|
301
301
|
end
|
302
302
|
|
303
303
|
def make_winrm_target(target_hash)
|
@@ -337,7 +337,7 @@ module BoltServer
|
|
337
337
|
return [400, error.to_json] if error
|
338
338
|
|
339
339
|
aggregate = body['target'].nil?
|
340
|
-
[200,
|
340
|
+
[200, result_set_to_data(result_set, aggregate: aggregate).to_json]
|
341
341
|
end
|
342
342
|
|
343
343
|
# Fetches the metadata for a single plan
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.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: 2020-05-
|
11
|
+
date: 2020-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|