bolt 1.20.0 → 1.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/bolt-modules/boltlib/lib/puppet/datatypes/resultset.rb +1 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +20 -0
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +20 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +29 -0
- data/lib/bolt/cli.rb +1 -19
- data/lib/bolt/outputter/human.rb +9 -3
- data/lib/bolt/plugin/puppetdb.rb +65 -2
- data/lib/bolt/puppetdb/client.rb +16 -0
- data/lib/bolt/result_set.rb +5 -0
- data/lib/bolt/transport/docker.rb +11 -3
- data/lib/bolt/transport/ssh/connection.rb +8 -0
- data/lib/bolt/util.rb +23 -0
- data/lib/bolt/version.rb +1 -1
- 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: e5ccf0b9b2f554bd10a8f4b25eedf4049c6b2820925d614be61a20b29ce64f5e
|
4
|
+
data.tar.gz: 814f77cef6785b3c5978c5b1336f0e8eb6cced7e3e1eb8917c2cbc7c96955deb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '058a15c710a702ea63e4f989b0f96efa3c2c54a074650e0609dd1157f4fe6818fcaa8f2aaadbf145dbf588411cecf0d686e07821d9ad66994cab59de7ec5a9b5'
|
7
|
+
data.tar.gz: 5292df40ecd9c3db055603aa279db60b2b80ce8ac416756d0c3b5e4fef7f072b7b766e78dd236f2630f00c71d3b27fc59b97681546eb680057e34df18da7dd36
|
@@ -9,6 +9,7 @@ Puppet::DataTypes.create_type('ResultSet') do
|
|
9
9
|
count => Callable[[], Integer],
|
10
10
|
empty => Callable[[], Boolean],
|
11
11
|
error_set => Callable[[], ResultSet],
|
12
|
+
filter_set => Callable[[Callable], ResultSet],
|
12
13
|
find => Callable[[String[1]], Optional[Variant[Result, ApplyResult]]],
|
13
14
|
first => Callable[[], Optional[Variant[Result, ApplyResult]]],
|
14
15
|
names => Callable[[], Array[String[1]]],
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# check if a file exists
|
4
|
+
Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFunction) do
|
5
|
+
# @param filename Absolute path or Puppet file path.
|
6
|
+
# @example Check a file on disk
|
7
|
+
# file::exists('/tmp/i_dumped_this_here')
|
8
|
+
# @example check a file from the modulepath
|
9
|
+
# file::exists('example/files/VERSION')
|
10
|
+
dispatch :exists do
|
11
|
+
scope_param
|
12
|
+
required_param 'String', :filename
|
13
|
+
return_type 'Boolean'
|
14
|
+
end
|
15
|
+
|
16
|
+
def exists(scope, filename)
|
17
|
+
found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
|
18
|
+
found && Puppet::FileSystem.exist?(found)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# check if a file is readable
|
4
|
+
Puppet::Functions.create_function(:'file::readable', Puppet::Functions::InternalFunction) do
|
5
|
+
# @param filename Absolute path or Puppet file path.
|
6
|
+
# @example Check a file on disk
|
7
|
+
# file::readable('/tmp/i_dumped_this_here')
|
8
|
+
# @example check a file from the modulepath
|
9
|
+
# file::readable('example/files/VERSION')
|
10
|
+
dispatch :readable do
|
11
|
+
scope_param
|
12
|
+
required_param 'String', :filename
|
13
|
+
return_type 'Boolean'
|
14
|
+
end
|
15
|
+
|
16
|
+
def readable(scope, filename)
|
17
|
+
found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
|
18
|
+
found && File.readable?(found)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Output a message for the user.
|
4
|
+
#
|
5
|
+
# This will print a message to stdout when using the human output format.
|
6
|
+
#
|
7
|
+
# **NOTE:** Not available in apply block
|
8
|
+
Puppet::Functions.create_function(:'out::message') do
|
9
|
+
# Output a message.
|
10
|
+
# @param message The message to output.
|
11
|
+
# @example Print a message
|
12
|
+
# out::message('Something went wrong')
|
13
|
+
dispatch :output_message do
|
14
|
+
param 'String', :message
|
15
|
+
return_type 'Undef'
|
16
|
+
end
|
17
|
+
|
18
|
+
def output_message(message)
|
19
|
+
unless Puppet[:tasks]
|
20
|
+
raise Puppet::ParseErrorWithIssue
|
21
|
+
.from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'out::message')
|
22
|
+
end
|
23
|
+
|
24
|
+
executor = Puppet.lookup(:bolt_executor)
|
25
|
+
executor.publish_event(type: :message, message: message)
|
26
|
+
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
data/lib/bolt/cli.rb
CHANGED
@@ -491,25 +491,7 @@ module Bolt
|
|
491
491
|
raise Bolt::CLIError, "A #{type} must be specified"
|
492
492
|
end
|
493
493
|
|
494
|
-
|
495
|
-
|
496
|
-
if !stat.readable?
|
497
|
-
raise Bolt::FileError.new("The #{type} '#{path}' is unreadable", path)
|
498
|
-
elsif !stat.file? && (!allow_dir || !stat.directory?)
|
499
|
-
expected = allow_dir ? 'file or directory' : 'file'
|
500
|
-
raise Bolt::FileError.new("The #{type} '#{path}' is not a #{expected}", path)
|
501
|
-
elsif stat.directory?
|
502
|
-
Dir.foreach(path) do |file|
|
503
|
-
next if %w[. ..].include?(file)
|
504
|
-
validate_file(type, File.join(path, file), allow_dir)
|
505
|
-
end
|
506
|
-
end
|
507
|
-
rescue Errno::ENOENT
|
508
|
-
raise Bolt::FileError.new("The #{type} '#{path}' does not exist", path)
|
509
|
-
end
|
510
|
-
|
511
|
-
def file_stat(path)
|
512
|
-
File.stat(path)
|
494
|
+
Bolt::Util.validate_file(type, path, allow_dir)
|
513
495
|
end
|
514
496
|
|
515
497
|
def rerun
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -56,6 +56,8 @@ module Bolt
|
|
56
56
|
@disable_depth -= 1
|
57
57
|
when :disable_default_output
|
58
58
|
@disable_depth += 1
|
59
|
+
when :message
|
60
|
+
print_message_event(event)
|
59
61
|
end
|
60
62
|
end
|
61
63
|
|
@@ -334,6 +336,10 @@ module Bolt
|
|
334
336
|
end
|
335
337
|
end
|
336
338
|
|
339
|
+
def print_message_event(event)
|
340
|
+
print_message(event[:message])
|
341
|
+
end
|
342
|
+
|
337
343
|
def fatal_error(err)
|
338
344
|
@stream.puts(colorize(:red, err.message))
|
339
345
|
if err.is_a? Bolt::RunFailure
|
@@ -346,10 +352,10 @@ module Bolt
|
|
346
352
|
end
|
347
353
|
end
|
348
354
|
end
|
349
|
-
end
|
350
355
|
|
351
|
-
|
352
|
-
|
356
|
+
def print_message(message)
|
357
|
+
@stream.puts(message)
|
358
|
+
end
|
353
359
|
end
|
354
360
|
end
|
355
361
|
end
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -3,8 +3,19 @@
|
|
3
3
|
module Bolt
|
4
4
|
class Plugin
|
5
5
|
class Puppetdb
|
6
|
+
class FactLookupError < Bolt::Error
|
7
|
+
def initialize(fact, err = nil)
|
8
|
+
m = String.new("Fact lookup '#{fact}' contains an invalid factname")
|
9
|
+
m << ": #{err}" unless err.nil?
|
10
|
+
super(m, 'bolt.plugin/fact-lookup-error')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
TARGET_OPTS = %w[uri name config].freeze
|
15
|
+
|
6
16
|
def initialize(pdb_client)
|
7
17
|
@puppetdb_client = pdb_client
|
18
|
+
@logger = Logging.logger[self]
|
8
19
|
end
|
9
20
|
|
10
21
|
def name
|
@@ -15,9 +26,61 @@ module Bolt
|
|
15
26
|
['lookup_targets']
|
16
27
|
end
|
17
28
|
|
29
|
+
def warn_missing_fact(certname, fact)
|
30
|
+
@logger.warn("Could not find fact #{fact} for node #{certname}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def fact_path(raw_fact)
|
34
|
+
fact_path = raw_fact.split(".")
|
35
|
+
if fact_path[0] == 'facts'
|
36
|
+
fact_path.drop(1)
|
37
|
+
elsif fact_path == ['certname']
|
38
|
+
fact_path
|
39
|
+
else
|
40
|
+
raise FactLookupError.new(raw_fact, "fact lookups must start with 'facts.'")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
18
44
|
def lookup_targets(opts)
|
19
|
-
|
20
|
-
|
45
|
+
targets = @puppetdb_client.query_certnames(opts['query'])
|
46
|
+
facts = []
|
47
|
+
|
48
|
+
target_opts = opts.select { |k, _| TARGET_OPTS.include?(k) }
|
49
|
+
Bolt::Util.walk_vals(target_opts) do |value|
|
50
|
+
# This is done in parts instead of in place so that we only need to
|
51
|
+
# make one puppetDB query
|
52
|
+
if value.is_a?(String)
|
53
|
+
facts << fact_path(value)
|
54
|
+
end
|
55
|
+
value
|
56
|
+
end
|
57
|
+
|
58
|
+
facts.uniq!
|
59
|
+
# Returns {'mycertname' => [{'path' => ['nested', 'fact'], 'value' => val'}], ... }
|
60
|
+
fact_values = @puppetdb_client.fact_values(targets, facts)
|
61
|
+
|
62
|
+
targets.map do |certname|
|
63
|
+
target_data = fact_values[certname]
|
64
|
+
target = resolve_facts(target_opts, certname, target_data) || {}
|
65
|
+
target['uri'] = certname unless target['uri'] || target['name']
|
66
|
+
|
67
|
+
target
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def resolve_facts(config, certname, target_data)
|
72
|
+
Bolt::Util.walk_vals(config) do |value|
|
73
|
+
if value.is_a?(String)
|
74
|
+
data = target_data&.detect { |d| d['path'] == fact_path(value) }
|
75
|
+
warn_missing_fact(certname, value) if data.nil?
|
76
|
+
# If there's no fact data this will be nil
|
77
|
+
data&.fetch('value', nil)
|
78
|
+
elsif value.is_a?(Array) || value.is_a?(Hash)
|
79
|
+
value
|
80
|
+
else
|
81
|
+
raise FactLookupError.new(value, "fact lookups must be a string")
|
82
|
+
end
|
83
|
+
end
|
21
84
|
end
|
22
85
|
end
|
23
86
|
end
|
data/lib/bolt/puppetdb/client.rb
CHANGED
@@ -42,6 +42,22 @@ module Bolt
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
+
def fact_values(certnames = [], facts = [])
|
46
|
+
return {} if certnames.empty? || facts.empty?
|
47
|
+
|
48
|
+
certnames.uniq!
|
49
|
+
name_query = certnames.map { |c| ["=", "certname", c] }
|
50
|
+
name_query.insert(0, "or")
|
51
|
+
|
52
|
+
facts_query = facts.map { |f| ["=", "path", f] }
|
53
|
+
facts_query.insert(0, "or")
|
54
|
+
|
55
|
+
query = ['and', name_query, facts_query]
|
56
|
+
result = make_query(query, 'fact-contents')
|
57
|
+
result.map! { |h| h.delete_if { |k, _v| %w[environment name].include?(k) } }
|
58
|
+
result.group_by { |c| c['certname'] }
|
59
|
+
end
|
60
|
+
|
45
61
|
def make_query(query, path = nil)
|
46
62
|
body = JSON.generate(query: query)
|
47
63
|
url = "#{uri}/pdb/query/v4"
|
data/lib/bolt/result_set.rb
CHANGED
@@ -30,6 +30,11 @@ module Bolt
|
|
30
30
|
self
|
31
31
|
end
|
32
32
|
|
33
|
+
def filter_set
|
34
|
+
filtered = @results.select { |r| yield r }
|
35
|
+
self.class.new(filtered)
|
36
|
+
end
|
37
|
+
|
33
38
|
def result_hash
|
34
39
|
@result_hash ||= @results.each_with_object({}) do |result, acc|
|
35
40
|
acc[result.target.name] = result
|
@@ -8,7 +8,7 @@ module Bolt
|
|
8
8
|
module Transport
|
9
9
|
class Docker < Base
|
10
10
|
def self.options
|
11
|
-
%w[host service-url service-options tmpdir interpreters]
|
11
|
+
%w[host service-url service-options tmpdir interpreters shell-command tty]
|
12
12
|
end
|
13
13
|
|
14
14
|
def provided_features
|
@@ -56,9 +56,17 @@ module Bolt
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
def run_command(target, command,
|
59
|
+
def run_command(target, command, options = {})
|
60
|
+
if target.options['tty']
|
61
|
+
options[:Tty] = true
|
62
|
+
end
|
63
|
+
if target.options['shell-command'] && !target.options['shell-command'].empty?
|
64
|
+
# escape any double quotes in command
|
65
|
+
command = command.gsub('"', '\"')
|
66
|
+
command = "#{target.options['shell-command']} \" #{command}\""
|
67
|
+
end
|
60
68
|
with_connection(target) do |conn|
|
61
|
-
stdout, stderr, exitcode = conn.execute(*Shellwords.split(command),
|
69
|
+
stdout, stderr, exitcode = conn.execute(*Shellwords.split(command), options)
|
62
70
|
Bolt::Result.for_command(target, stdout, stderr, exitcode, 'command', command)
|
63
71
|
end
|
64
72
|
end
|
@@ -30,6 +30,14 @@ module Bolt
|
|
30
30
|
|
31
31
|
@logger = Logging.logger[@target.host]
|
32
32
|
@transport_logger = transport_logger
|
33
|
+
|
34
|
+
if target.options['private-key']&.instance_of?(String)
|
35
|
+
begin
|
36
|
+
Bolt::Util.validate_file('ssh key', target.options['private-key'])
|
37
|
+
rescue Bolt::FileError => e
|
38
|
+
@logger.warn(e.msg)
|
39
|
+
end
|
40
|
+
end
|
33
41
|
end
|
34
42
|
|
35
43
|
PAGEANT_NAME = "Pageant\0".encode(Encoding::UTF_16LE)
|
data/lib/bolt/util.rb
CHANGED
@@ -179,6 +179,29 @@ module Bolt
|
|
179
179
|
end
|
180
180
|
end
|
181
181
|
|
182
|
+
# This is stubbed for testing validate_file
|
183
|
+
def file_stat(path)
|
184
|
+
File.stat(File.expand_path(path))
|
185
|
+
end
|
186
|
+
|
187
|
+
def validate_file(type, path, allow_dir = false)
|
188
|
+
stat = file_stat(path)
|
189
|
+
|
190
|
+
if !stat.readable?
|
191
|
+
raise Bolt::FileError.new("The #{type} '#{path}' is unreadable", path)
|
192
|
+
elsif !stat.file? && (!allow_dir || !stat.directory?)
|
193
|
+
expected = allow_dir ? 'file or directory' : 'file'
|
194
|
+
raise Bolt::FileError.new("The #{type} '#{path}' is not a #{expected}", path)
|
195
|
+
elsif stat.directory?
|
196
|
+
Dir.foreach(path) do |file|
|
197
|
+
next if %w[. ..].include?(file)
|
198
|
+
validate_file(type, File.join(path, file), allow_dir)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
rescue Errno::ENOENT
|
202
|
+
raise Bolt::FileError.new("The #{type} '#{path}' does not exist", path)
|
203
|
+
end
|
204
|
+
|
182
205
|
# Returns true if windows false if not.
|
183
206
|
def windows?
|
184
207
|
!!File::ALT_SEPARATOR
|
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: 1.
|
4
|
+
version: 1.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: 2019-05-
|
11
|
+
date: 2019-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -346,8 +346,11 @@ files:
|
|
346
346
|
- bolt-modules/boltlib/types/targetspec.pp
|
347
347
|
- bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb
|
348
348
|
- bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb
|
349
|
+
- bolt-modules/file/lib/puppet/functions/file/exists.rb
|
349
350
|
- bolt-modules/file/lib/puppet/functions/file/read.rb
|
351
|
+
- bolt-modules/file/lib/puppet/functions/file/readable.rb
|
350
352
|
- bolt-modules/file/lib/puppet/functions/file/write.rb
|
353
|
+
- bolt-modules/out/lib/puppet/functions/out/message.rb
|
351
354
|
- bolt-modules/system/lib/puppet/functions/system/env.rb
|
352
355
|
- exe/bolt
|
353
356
|
- exe/bolt-inventory-pdb
|