bolt 0.21.8 → 0.22.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/file_upload.rb +7 -84
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +19 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +93 -0
- data/exe/bolt-server +6 -0
- data/lib/bolt/applicator.rb +6 -5
- data/lib/bolt/bolt_option_parser.rb +1 -1
- data/lib/bolt/cli.rb +1 -1
- data/lib/bolt/executor.rb +7 -2
- data/lib/bolt/puppetdb/config.rb +12 -4
- data/lib/bolt/target.rb +16 -0
- data/lib/bolt/task.rb +23 -0
- data/lib/bolt/transport/base.rb +45 -0
- data/lib/bolt/transport/local.rb +8 -2
- data/lib/bolt/transport/local/shell.rb +1 -4
- data/lib/bolt/transport/orch.rb +7 -1
- data/lib/bolt/transport/ssh.rb +20 -12
- data/lib/bolt/transport/ssh/connection.rb +31 -16
- data/lib/bolt/transport/winrm.rb +25 -6
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_ext/server.rb +73 -0
- data/lib/bolt_ext/server_acl.rb +37 -0
- data/lib/bolt_ext/server_config.rb +87 -0
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe765b38afea737ae2308098b790fde05b73da5d4eaa661c2121313ccb675d48
|
4
|
+
data.tar.gz: 3280f5388127bce11a71f3aed0db819e7869f0d6751dedb3d0ec815c83623674
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50c22855422b4a2cba254cb43647c8ead003b53816b082173a4383f143cf44e3b6a9ca20ff1792d986597e7256fffe875a7130fe036689860f8931f7c5622b29
|
7
|
+
data.tar.gz: 101c06ea0ad6836d3c773ab877fbc74f972f38c14a33d92f00d0da52e83cd797c58a1b502fc79c0b8edd85df3cccc9bfbbb972ce54d64d09de4a6d7ca1a1f31f
|
@@ -1,93 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
# Uploads the given file or directory to the given set of targets and returns the result from each upload.
|
6
|
-
# This function does nothing if the list of targets is empty.
|
3
|
+
# This function wraps the upload_file function with a deprecation warning, for backward compatibility.
|
7
4
|
Puppet::Functions.create_function(:file_upload, Puppet::Functions::InternalFunction) do
|
8
|
-
|
9
|
-
# @param source A source path, either an absolute path or a modulename/filename selector for a file in
|
10
|
-
# <moduleroot>/files.
|
11
|
-
# @param destination An absolute path on the target(s).
|
12
|
-
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
13
|
-
# @param options Additional options: '_catch_errors', '_run_as'.
|
14
|
-
# @return A list of results, one entry per target.
|
15
|
-
# @example Upload a local file to Linux targets and change owner to 'root'
|
16
|
-
# file_upload('/var/tmp/payload.tgz', '/tmp/payload.tgz', $targets, '_run_as' => 'root')
|
17
|
-
# @example Upload a module file to a Windows target
|
18
|
-
# file_upload('postgres/default.conf', 'C:/ProgramData/postgres/default.conf', $target)
|
19
|
-
dispatch :file_upload do
|
20
|
-
scope_param
|
21
|
-
param 'String[1]', :source
|
22
|
-
param 'String[1]', :destination
|
23
|
-
param 'Boltlib::TargetSpec', :targets
|
24
|
-
optional_param 'Hash[String[1], Any]', :options
|
25
|
-
return_type 'ResultSet'
|
26
|
-
end
|
27
|
-
|
28
|
-
# Upload a file, logging the provided description.
|
29
|
-
# @param source A source path, either an absolute path or a modulename/filename selector for a file in
|
30
|
-
# <moduleroot>/files.
|
31
|
-
# @param destination An absolute path on the target(s).
|
32
|
-
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
33
|
-
# @param description A description to be output when calling this function.
|
34
|
-
# @param options Additional options: '_catch_errors', '_run_as'.
|
35
|
-
# @return A list of results, one entry per target.
|
36
|
-
# @example Upload a file
|
37
|
-
# file_upload('/var/tmp/payload.tgz', '/tmp/payload.tgz', $targets, 'Uploading payload to unpack')
|
38
|
-
dispatch :file_upload_with_description do
|
39
|
-
scope_param
|
40
|
-
param 'String[1]', :source
|
41
|
-
param 'String[1]', :destination
|
42
|
-
param 'Boltlib::TargetSpec', :targets
|
43
|
-
param 'String', :description
|
44
|
-
optional_param 'Hash[String[1], Any]', :options
|
45
|
-
return_type 'ResultSet'
|
46
|
-
end
|
47
|
-
|
48
|
-
def file_upload(scope, source, destination, targets, options = nil)
|
49
|
-
file_upload_with_description(scope, source, destination, targets, nil, options)
|
50
|
-
end
|
51
|
-
|
52
|
-
def file_upload_with_description(scope, source, destination, targets, description = nil, options = nil)
|
53
|
-
options ||= {}
|
54
|
-
options = options.merge('_description' => description) if description
|
55
|
-
|
56
|
-
unless Puppet[:tasks]
|
57
|
-
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
58
|
-
Puppet::Pops::Issues::TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, operation: 'file_upload'
|
59
|
-
)
|
60
|
-
end
|
61
|
-
|
5
|
+
def file_upload(*args)
|
62
6
|
executor = Puppet.lookup(:bolt_executor) { nil }
|
63
|
-
|
64
|
-
unless executor && inventory && Puppet.features.bolt?
|
65
|
-
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
66
|
-
Puppet::Pops::Issues::TASK_MISSING_BOLT, action: _('do file uploads')
|
67
|
-
)
|
68
|
-
end
|
69
|
-
|
70
|
-
executor.report_function_call('file_upload')
|
7
|
+
executor&.report_function_call('file_upload')
|
71
8
|
|
72
|
-
|
73
|
-
unless found && Puppet::FileSystem.exist?(found)
|
74
|
-
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
75
|
-
Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: source
|
76
|
-
)
|
77
|
-
end
|
9
|
+
file, line = Puppet::Pops::PuppetStack.top_of_stack
|
78
10
|
|
79
|
-
|
80
|
-
|
81
|
-
if targets.empty?
|
82
|
-
call_function('debug', "Simulating file upload of '#{found}' - no targets given - no action taken")
|
83
|
-
r = Bolt::ResultSet.new([])
|
84
|
-
else
|
85
|
-
r = executor.file_upload(targets, found, destination, options)
|
86
|
-
end
|
11
|
+
msg = "The file_upload function is deprecated and will be removed; use upload_file instead"
|
12
|
+
Puppet.puppet_deprecation_warning(msg, key: 'bolt-function/file_upload', file: file, line: line)
|
87
13
|
|
88
|
-
|
89
|
-
raise Bolt::RunFailure.new(r, 'upload_file', source)
|
90
|
-
end
|
91
|
-
r
|
14
|
+
call_function('upload_file', *args)
|
92
15
|
end
|
93
16
|
end
|
@@ -13,7 +13,7 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
|
|
13
13
|
# @example Run a local script on Linux targets as 'root'
|
14
14
|
# run_script('/var/tmp/myscript', $targets, '_run_as' => 'root')
|
15
15
|
# @example Run a module-provided script with arguments
|
16
|
-
#
|
16
|
+
# run_script('iis/setup.ps1', $target, 'arguments' => ['/u', 'Administrator'])
|
17
17
|
dispatch :run_script do
|
18
18
|
scope_param
|
19
19
|
param 'String[1]', :script
|
@@ -31,7 +31,7 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
|
|
31
31
|
# Additional options: '_catch_errors', '_run_as'.
|
32
32
|
# @return A list of results, one entry per target.
|
33
33
|
# @example Run a script
|
34
|
-
#
|
34
|
+
# run_script('/var/tmp/myscript', $targets, 'Downloading my application')
|
35
35
|
dispatch :run_script_with_description do
|
36
36
|
scope_param
|
37
37
|
param 'String[1]', :script
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bolt/error'
|
4
|
+
require 'bolt/pal'
|
4
5
|
|
5
6
|
# Runs a given instance of a `Task` on the given set of targets and returns the result from each.
|
6
7
|
# This function does nothing if the list of targets is empty.
|
@@ -120,7 +121,24 @@ Puppet::Functions.create_function(:run_task) do
|
|
120
121
|
end
|
121
122
|
|
122
123
|
unless Puppet::Pops::Types::TypeFactory.data.instance?(use_args)
|
123
|
-
|
124
|
+
# generate a helpful error message about the type-mismatch between the type Data
|
125
|
+
# and the actual type of use_args
|
126
|
+
use_args_t = Puppet::Pops::Types::TypeCalculator.infer_set(use_args)
|
127
|
+
desc = Puppet::Pops::Types::TypeMismatchDescriber.singleton.describe_mismatch(
|
128
|
+
'Task parameters are not of type Data. run_task()',
|
129
|
+
Puppet::Pops::Types::TypeFactory.data, use_args_t
|
130
|
+
)
|
131
|
+
raise with_stack(:TYPE_NOT_DATA, desc)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Wrap parameters marked with '"sensitive": true' in the task metadata with a
|
135
|
+
# Sensitive wrapper type. This way it's not shown in logs.
|
136
|
+
if task.parameters
|
137
|
+
use_args.each do |k, v|
|
138
|
+
if task.parameters[k] && task.parameters[k]['sensitive']
|
139
|
+
use_args[k] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(v)
|
140
|
+
end
|
141
|
+
end
|
124
142
|
end
|
125
143
|
|
126
144
|
if executor.noop
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/error'
|
4
|
+
|
5
|
+
# Uploads the given file or directory to the given set of targets and returns the result from each upload.
|
6
|
+
# This function does nothing if the list of targets is empty.
|
7
|
+
Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunction) do
|
8
|
+
# Upload a file.
|
9
|
+
# @param source A source path, either an absolute path or a modulename/filename selector for a file in
|
10
|
+
# <moduleroot>/files.
|
11
|
+
# @param destination An absolute path on the target(s).
|
12
|
+
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
13
|
+
# @param options Additional options: '_catch_errors', '_run_as'.
|
14
|
+
# @return A list of results, one entry per target.
|
15
|
+
# @example Upload a local file to Linux targets and change owner to 'root'
|
16
|
+
# upload_file('/var/tmp/payload.tgz', '/tmp/payload.tgz', $targets, '_run_as' => 'root')
|
17
|
+
# @example Upload a module file to a Windows target
|
18
|
+
# upload_file('postgres/default.conf', 'C:/ProgramData/postgres/default.conf', $target)
|
19
|
+
dispatch :upload_file do
|
20
|
+
scope_param
|
21
|
+
param 'String[1]', :source
|
22
|
+
param 'String[1]', :destination
|
23
|
+
param 'Boltlib::TargetSpec', :targets
|
24
|
+
optional_param 'Hash[String[1], Any]', :options
|
25
|
+
return_type 'ResultSet'
|
26
|
+
end
|
27
|
+
|
28
|
+
# Upload a file, logging the provided description.
|
29
|
+
# @param source A source path, either an absolute path or a modulename/filename selector for a file in
|
30
|
+
# <moduleroot>/files.
|
31
|
+
# @param destination An absolute path on the target(s).
|
32
|
+
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
33
|
+
# @param description A description to be output when calling this function.
|
34
|
+
# @param options Additional options: '_catch_errors', '_run_as'.
|
35
|
+
# @return A list of results, one entry per target.
|
36
|
+
# @example Upload a file
|
37
|
+
# upload_file('/var/tmp/payload.tgz', '/tmp/payload.tgz', $targets, 'Uploading payload to unpack')
|
38
|
+
dispatch :upload_file_with_description do
|
39
|
+
scope_param
|
40
|
+
param 'String[1]', :source
|
41
|
+
param 'String[1]', :destination
|
42
|
+
param 'Boltlib::TargetSpec', :targets
|
43
|
+
param 'String', :description
|
44
|
+
optional_param 'Hash[String[1], Any]', :options
|
45
|
+
return_type 'ResultSet'
|
46
|
+
end
|
47
|
+
|
48
|
+
def upload_file(scope, source, destination, targets, options = nil)
|
49
|
+
upload_file_with_description(scope, source, destination, targets, nil, options)
|
50
|
+
end
|
51
|
+
|
52
|
+
def upload_file_with_description(scope, source, destination, targets, description = nil, options = nil)
|
53
|
+
options ||= {}
|
54
|
+
options = options.merge('_description' => description) if description
|
55
|
+
|
56
|
+
unless Puppet[:tasks]
|
57
|
+
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
58
|
+
Puppet::Pops::Issues::TASK_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, operation: 'upload_file'
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
executor = Puppet.lookup(:bolt_executor) { nil }
|
63
|
+
inventory = Puppet.lookup(:bolt_inventory) { nil }
|
64
|
+
unless executor && inventory && Puppet.features.bolt?
|
65
|
+
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
66
|
+
Puppet::Pops::Issues::TASK_MISSING_BOLT, action: _('do file uploads')
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
executor.report_function_call('upload_file')
|
71
|
+
|
72
|
+
found = Puppet::Parser::Files.find_file(source, scope.compiler.environment)
|
73
|
+
unless found && Puppet::FileSystem.exist?(found)
|
74
|
+
raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
|
75
|
+
Puppet::Pops::Issues::NO_SUCH_FILE_OR_DIRECTORY, file: source
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Ensure that that given targets are all Target instances
|
80
|
+
targets = inventory.get_targets(targets)
|
81
|
+
if targets.empty?
|
82
|
+
call_function('debug', "Simulating file upload of '#{found}' - no targets given - no action taken")
|
83
|
+
r = Bolt::ResultSet.new([])
|
84
|
+
else
|
85
|
+
r = executor.upload_file(targets, found, destination, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
if !r.ok && !options['_catch_errors']
|
89
|
+
raise Bolt::RunFailure.new(r, 'upload_file', source)
|
90
|
+
end
|
91
|
+
r
|
92
|
+
end
|
93
|
+
end
|
data/exe/bolt-server
ADDED
data/lib/bolt/applicator.rb
CHANGED
@@ -7,11 +7,10 @@ require 'json'
|
|
7
7
|
require 'logging'
|
8
8
|
require 'minitar'
|
9
9
|
require 'open3'
|
10
|
+
require 'bolt/task'
|
10
11
|
require 'bolt/util/puppet_log_level'
|
11
12
|
|
12
13
|
module Bolt
|
13
|
-
Task = Struct.new(:name, :implementations, :input_method)
|
14
|
-
|
15
14
|
class Applicator
|
16
15
|
def initialize(inventory, executor, modulepath, pdb_client, hiera_config, max_compiles)
|
17
16
|
@inventory = inventory
|
@@ -41,7 +40,7 @@ module Bolt
|
|
41
40
|
@custom_facts_task ||= begin
|
42
41
|
path = File.join(libexec, 'custom_facts.rb')
|
43
42
|
impl = { 'name' => 'custom_facts.rb', 'path' => path, 'requirements' => [], 'supports_noop' => true }
|
44
|
-
Task.new('custom_facts', [impl], 'stdin')
|
43
|
+
Task.new(name: 'custom_facts', implementations: [impl], input_method: 'stdin')
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -49,7 +48,7 @@ module Bolt
|
|
49
48
|
@catalog_apply_task ||= begin
|
50
49
|
path = File.join(libexec, 'apply_catalog.rb')
|
51
50
|
impl = { 'name' => 'apply_catalog.rb', 'path' => path, 'requirements' => [], 'supports_noop' => true }
|
52
|
-
Task.new('apply_catalog', [impl], 'stdin')
|
51
|
+
Task.new(name: 'apply_catalog', implementations: [impl], input_method: 'stdin')
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
@@ -121,7 +120,9 @@ module Bolt
|
|
121
120
|
'msg' => "Puppet is not installed on the target, please install it to enable 'apply'",
|
122
121
|
'kind' => 'bolt/apply-error'
|
123
122
|
})
|
124
|
-
elsif exit_code == 1 &&
|
123
|
+
elsif exit_code == 1 &&
|
124
|
+
(error_hash['msg'] =~ /Could not find executable 'ruby.exe'/ ||
|
125
|
+
error_hash['msg'] =~ /The term 'ruby.exe' is not recognized as the name of a cmdlet/)
|
125
126
|
# Windows does not have Ruby present
|
126
127
|
Result.new(result.target, error:
|
127
128
|
{
|
data/lib/bolt/cli.rb
CHANGED
@@ -296,7 +296,7 @@ module Bolt
|
|
296
296
|
raise Bolt::CLIError, "A destination path must be specified"
|
297
297
|
end
|
298
298
|
validate_file('source file', src)
|
299
|
-
executor.
|
299
|
+
executor.upload_file(targets, src, dest, executor_opts) do |event|
|
300
300
|
outputter.print_event(event)
|
301
301
|
end
|
302
302
|
end
|
data/lib/bolt/executor.rb
CHANGED
@@ -18,14 +18,18 @@ module Bolt
|
|
18
18
|
attr_reader :noop, :transports
|
19
19
|
attr_accessor :run_as
|
20
20
|
|
21
|
+
# FIXME: There must be a better way
|
22
|
+
# https://makandracards.com/makandra/36011-ruby-do-not-mix-optional-and-keyword-arguments
|
21
23
|
def initialize(concurrency = 1,
|
22
24
|
analytics = Bolt::Analytics::NoopClient.new,
|
23
25
|
noop = nil,
|
24
|
-
bundled_content: nil
|
26
|
+
bundled_content: nil,
|
27
|
+
load_config: true)
|
25
28
|
@analytics = analytics
|
26
29
|
@bundled_content = bundled_content
|
27
30
|
@logger = Logging.logger[self]
|
28
31
|
@plan_logging = false
|
32
|
+
@load_config = load_config
|
29
33
|
|
30
34
|
@transports = Bolt::TRANSPORTS.each_with_object({}) do |(key, val), coll|
|
31
35
|
coll[key.to_s] = Concurrent::Delay.new do
|
@@ -212,6 +216,7 @@ module Bolt
|
|
212
216
|
log_action(description, targets) do
|
213
217
|
notify = proc { |event| @notifier.notify(callback, event) if callback }
|
214
218
|
options = { '_run_as' => run_as }.merge(options) if run_as
|
219
|
+
options = options.merge('_load_config' => @load_config)
|
215
220
|
arguments['_task'] = task.name
|
216
221
|
|
217
222
|
results = batch_execute(targets) do |transport, batch|
|
@@ -225,7 +230,7 @@ module Bolt
|
|
225
230
|
end
|
226
231
|
end
|
227
232
|
|
228
|
-
def
|
233
|
+
def upload_file(targets, source, destination, options = {}, &callback)
|
229
234
|
description = options.fetch('_description', "file upload from #{source} to #{destination}")
|
230
235
|
log_action(description, targets) do
|
231
236
|
notify = proc { |event| @notifier.notify(callback, event) if callback }
|
data/lib/bolt/puppetdb/config.rb
CHANGED
@@ -6,10 +6,18 @@ require 'bolt/util'
|
|
6
6
|
module Bolt
|
7
7
|
module PuppetDB
|
8
8
|
class Config
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
if !ENV['HOME'].nil?
|
10
|
+
DEFAULT_TOKEN = File.expand_path('~/.puppetlabs/token')
|
11
|
+
DEFAULT_CONFIG = { user: File.expand_path('~/.puppetlabs/client-tools/puppetdb.conf'),
|
12
|
+
global: '/etc/puppetlabs/client-tools/puppetdb.conf',
|
13
|
+
win_global: 'C:/ProgramData/PuppetLabs/client-tools/puppetdb.conf' }.freeze
|
14
|
+
else
|
15
|
+
DEFAULT_TOKEN = Bolt::Util.windows? ? 'nul' : '/dev/null'
|
16
|
+
DEFAULT_CONFIG = { user: '/etc/puppetlabs/puppet/puppetdb.conf',
|
17
|
+
global: '/etc/puppetlabs/puppet/puppetdb.conf',
|
18
|
+
win_global: 'C:/ProgramData/PuppetLabs/client-tools/puppetdb.conf' }.freeze
|
19
|
+
|
20
|
+
end
|
13
21
|
|
14
22
|
def self.load_config(filename, options)
|
15
23
|
global_path = Bolt::Util.windows? ? DEFAULT_CONFIG[:win_global] : DEFAULT_CONFIG[:global]
|
data/lib/bolt/target.rb
CHANGED
@@ -18,6 +18,22 @@ module Bolt
|
|
18
18
|
@uri_obj = parse(uri)
|
19
19
|
@options = options || {}
|
20
20
|
@options.freeze
|
21
|
+
|
22
|
+
if @options['user']
|
23
|
+
@user = @options['user']
|
24
|
+
end
|
25
|
+
|
26
|
+
if @options['password']
|
27
|
+
@password = @options['password']
|
28
|
+
end
|
29
|
+
|
30
|
+
if @options['port']
|
31
|
+
@port = @options['port']
|
32
|
+
end
|
33
|
+
|
34
|
+
if @options['protocol']
|
35
|
+
@protocol = @options['protocol']
|
36
|
+
end
|
21
37
|
end
|
22
38
|
|
23
39
|
def update_conf(conf)
|
data/lib/bolt/task.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
Task = Struct.new(
|
5
|
+
:name,
|
6
|
+
:implementations,
|
7
|
+
:input_method,
|
8
|
+
:file,
|
9
|
+
:metadata
|
10
|
+
) do
|
11
|
+
|
12
|
+
TASK_DEFAULTS = {
|
13
|
+
implementations: {},
|
14
|
+
input_method: 'both',
|
15
|
+
metadata: {}
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
def initialize(task)
|
19
|
+
super()
|
20
|
+
TASK_DEFAULTS.merge(task).each { |k, v| self[k] = v }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/bolt/transport/base.rb
CHANGED
@@ -75,6 +75,15 @@ module Bolt
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
# Transform a parameter map to an environment variable map, with parameter names prefixed
|
79
|
+
# with 'PT_' and values transformed to JSON unless they're strings.
|
80
|
+
def envify_params(params)
|
81
|
+
params.each_with_object({}) do |(k, v), h|
|
82
|
+
v = v.to_json unless v.is_a?(String)
|
83
|
+
h["PT_#{k}"] = v
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
78
87
|
# Raises an error if more than one target was given in the batch.
|
79
88
|
#
|
80
89
|
# The default implementations of batch_* strictly assume the transport is
|
@@ -152,6 +161,15 @@ module Bolt
|
|
152
161
|
targets.map { |target| [target] }
|
153
162
|
end
|
154
163
|
|
164
|
+
def from_api?(task)
|
165
|
+
if task.respond_to? :file
|
166
|
+
unless task.file.nil?
|
167
|
+
return true
|
168
|
+
end
|
169
|
+
end
|
170
|
+
false
|
171
|
+
end
|
172
|
+
|
155
173
|
# Transports should override this method with their own implementation of running a command.
|
156
174
|
def run_command(*_args)
|
157
175
|
raise NotImplementedError, "run_command() must be implemented by the transport class"
|
@@ -171,6 +189,33 @@ module Bolt
|
|
171
189
|
def upload(*_args)
|
172
190
|
raise NotImplementedError, "upload() must be implemented by the transport class"
|
173
191
|
end
|
192
|
+
|
193
|
+
# Unwraps any Sensitive data in an arguments Hash, so the plain-text is passed
|
194
|
+
# to the Task/Script.
|
195
|
+
#
|
196
|
+
# This works on deeply nested data structures composed of Hashes, Arrays, and
|
197
|
+
# and plain-old data types (int, string, etc).
|
198
|
+
def unwrap_sensitive_args(arguments)
|
199
|
+
# Skip this if Puppet isn't loaded
|
200
|
+
return arguments unless defined?(Puppet::Pops::Types::PSensitiveType::Sensitive)
|
201
|
+
|
202
|
+
case arguments
|
203
|
+
when Array
|
204
|
+
# iterate over the array, unwrapping all elements
|
205
|
+
arguments.map { |x| unwrap_sensitive_args(x) }
|
206
|
+
when Hash
|
207
|
+
# iterate over the arguments hash and unwrap all keys and values
|
208
|
+
arguments.each_with_object({}) { |(k, v), h|
|
209
|
+
h[unwrap_sensitive_args(k)] = unwrap_sensitive_args(v)
|
210
|
+
}
|
211
|
+
when Puppet::Pops::Types::PSensitiveType::Sensitive
|
212
|
+
# this value is Sensitive, unwrap it
|
213
|
+
unwrap_sensitive_args(arguments.unwrap)
|
214
|
+
else
|
215
|
+
# unknown data type, just return it
|
216
|
+
arguments
|
217
|
+
end
|
218
|
+
end
|
174
219
|
end
|
175
220
|
end
|
176
221
|
end
|
data/lib/bolt/transport/local.rb
CHANGED
@@ -70,6 +70,8 @@ module Bolt
|
|
70
70
|
with_tmpscript(File.absolute_path(script), target.options['tmpdir']) do |file|
|
71
71
|
logger.debug "Running '#{file}' with #{arguments}"
|
72
72
|
|
73
|
+
# unpack any Sensitive data AFTER we log
|
74
|
+
arguments = unwrap_sensitive_args(arguments)
|
73
75
|
if arguments.empty?
|
74
76
|
# We will always provide separated arguments, so work-around Open3's handling of a single
|
75
77
|
# argument as the entire command string for script paths containing spaces.
|
@@ -84,11 +86,15 @@ module Bolt
|
|
84
86
|
executable = target.select_impl(task, PROVIDED_FEATURES)
|
85
87
|
raise "No suitable implementation of #{task.name} for #{target.name}" unless executable
|
86
88
|
|
89
|
+
# unpack any Sensitive data, write it to a separate variable because
|
90
|
+
# we log 'arguments' below
|
91
|
+
unwrapped_arguments = unwrap_sensitive_args(arguments)
|
87
92
|
input_method = task.input_method || "both"
|
88
|
-
stdin = STDIN_METHODS.include?(input_method) ? JSON.dump(
|
89
|
-
env = ENVIRONMENT_METHODS.include?(input_method) ?
|
93
|
+
stdin = STDIN_METHODS.include?(input_method) ? JSON.dump(unwrapped_arguments) : nil
|
94
|
+
env = ENVIRONMENT_METHODS.include?(input_method) ? envify_params(unwrapped_arguments) : nil
|
90
95
|
|
91
96
|
with_tmpscript(executable, target.options['tmpdir']) do |script|
|
97
|
+
# log the arguments with sensitive data redacted, do NOT log unwrapped_arguments
|
92
98
|
logger.debug("Running '#{script}' with #{arguments}")
|
93
99
|
|
94
100
|
output = @conn.execute(script, stdin: stdin, env: env)
|
@@ -8,10 +8,7 @@ module Bolt
|
|
8
8
|
class Local
|
9
9
|
class Shell
|
10
10
|
def execute(*command, options)
|
11
|
-
if options[:env]
|
12
|
-
env = options[:env].each_with_object({}) { |(k, v), h| h["PT_#{k}"] = v.to_s }
|
13
|
-
command = [env] + command
|
14
|
-
end
|
11
|
+
command = [options[:env]] + command if options[:env]
|
15
12
|
|
16
13
|
if options[:stdin]
|
17
14
|
stdout, stderr, rc = Open3.capture3(*command, stdin_data: options[:stdin])
|
data/lib/bolt/transport/orch.rb
CHANGED
@@ -11,7 +11,11 @@ require 'bolt/result'
|
|
11
11
|
module Bolt
|
12
12
|
module Transport
|
13
13
|
class Orch < Base
|
14
|
-
CONF_FILE =
|
14
|
+
CONF_FILE = if !ENV['HOME'].nil?
|
15
|
+
File.expand_path('~/.puppetlabs/client-tools/orchestrator.conf')
|
16
|
+
else
|
17
|
+
'/etc/puppetlabs/client-tools/orchestrator.conf'
|
18
|
+
end
|
15
19
|
BOLT_COMMAND_TASK = Struct.new(:name).new('bolt_shim::command').freeze
|
16
20
|
BOLT_SCRIPT_TASK = Struct.new(:name).new('bolt_shim::script').freeze
|
17
21
|
BOLT_UPLOAD_TASK = Struct.new(:name).new('bolt_shim::upload').freeze
|
@@ -149,6 +153,8 @@ module Bolt
|
|
149
153
|
end
|
150
154
|
|
151
155
|
begin
|
156
|
+
# unpack any Sensitive data
|
157
|
+
arguments = unwrap_sensitive_args(arguments)
|
152
158
|
results = get_connection(targets.first.options).run_task(targets, task, arguments, options)
|
153
159
|
|
154
160
|
process_run_results(targets, results)
|
data/lib/bolt/transport/ssh.rb
CHANGED
@@ -63,8 +63,8 @@ module Bolt
|
|
63
63
|
@transport_logger.level = :warn
|
64
64
|
end
|
65
65
|
|
66
|
-
def with_connection(target)
|
67
|
-
conn = Connection.new(target, @transport_logger)
|
66
|
+
def with_connection(target, load_config = true)
|
67
|
+
conn = Connection.new(target, @transport_logger, load_config)
|
68
68
|
conn.connect
|
69
69
|
yield conn
|
70
70
|
ensure
|
@@ -105,6 +105,9 @@ module Bolt
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def run_script(target, script, arguments, options = {})
|
108
|
+
# unpack any Sensitive data
|
109
|
+
arguments = unwrap_sensitive_args(arguments)
|
110
|
+
|
108
111
|
with_connection(target) do |conn|
|
109
112
|
conn.running_as(options['_run_as']) do
|
110
113
|
conn.with_remote_tempdir do |dir|
|
@@ -118,11 +121,10 @@ module Bolt
|
|
118
121
|
end
|
119
122
|
|
120
123
|
def run_task(target, task, arguments, options = {})
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
+
# unpack any Sensitive data
|
125
|
+
arguments = unwrap_sensitive_args(arguments)
|
124
126
|
input_method = task.input_method || "both"
|
125
|
-
with_connection(target) do |conn|
|
127
|
+
with_connection(target, options.fetch('_load_config', true)) do |conn|
|
126
128
|
conn.running_as(options['_run_as']) do
|
127
129
|
stdin, output = nil
|
128
130
|
|
@@ -134,15 +136,21 @@ module Bolt
|
|
134
136
|
end
|
135
137
|
|
136
138
|
if ENVIRONMENT_METHODS.include?(input_method)
|
137
|
-
environment = arguments
|
138
|
-
val = val.to_json unless val.is_a?(String)
|
139
|
-
env.merge("PT_#{param}" => val)
|
140
|
-
end
|
141
|
-
execute_options[:environment] = environment
|
139
|
+
execute_options[:environment] = envify_params(arguments)
|
142
140
|
end
|
143
141
|
|
144
142
|
conn.with_remote_tempdir do |dir|
|
145
|
-
|
143
|
+
if from_api?(task)
|
144
|
+
filename = task.file['filename']
|
145
|
+
remote_task_path = conn.write_executable_from_content(dir,
|
146
|
+
Base64.decode64(task.file['file_content']),
|
147
|
+
filename)
|
148
|
+
else
|
149
|
+
executable = target.select_impl(task, PROVIDED_FEATURES)
|
150
|
+
raise "No suitable implementation of #{task.name} for #{target.name}" unless executable
|
151
|
+
|
152
|
+
remote_task_path = conn.write_remote_executable(dir, executable)
|
153
|
+
end
|
146
154
|
if conn.run_as && stdin
|
147
155
|
wrapper = make_wrapper_stringio(remote_task_path, stdin)
|
148
156
|
remote_wrapper_path = conn.write_remote_executable(dir, wrapper, 'wrapper.sh')
|
@@ -54,10 +54,12 @@ module Bolt
|
|
54
54
|
attr_reader :logger, :user, :target
|
55
55
|
attr_writer :run_as
|
56
56
|
|
57
|
-
def initialize(target, transport_logger)
|
57
|
+
def initialize(target, transport_logger, load_config = true)
|
58
58
|
@target = target
|
59
|
+
@load_config = load_config
|
59
60
|
|
60
|
-
|
61
|
+
ssh_user = load_config ? Net::SSH::Config.for(target.host)[:user] : nil
|
62
|
+
@user = @target.user || ssh_user || Etc.getlogin
|
61
63
|
@run_as = nil
|
62
64
|
|
63
65
|
@logger = Logging.logger[@target.host]
|
@@ -97,20 +99,26 @@ module Bolt
|
|
97
99
|
end
|
98
100
|
options[:timeout] = target.options['connect-timeout'] if target.options['connect-timeout']
|
99
101
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
if
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
102
|
+
if @load_config
|
103
|
+
# Mirroring:
|
104
|
+
# https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/agent.rb#L80
|
105
|
+
# https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/authentication/pageant.rb#L403
|
106
|
+
if defined?(UNIXSocket) && UNIXSocket
|
107
|
+
if ENV['SSH_AUTH_SOCK'].to_s.empty?
|
108
|
+
@logger.debug { "Disabling use_agent in net-ssh: ssh-agent is not available" }
|
109
|
+
options[:use_agent] = false
|
110
|
+
end
|
111
|
+
elsif Bolt::Util.windows?
|
112
|
+
pageant_wide = 'Pageant'.encode('UTF-16LE')
|
113
|
+
if Win.FindWindow(pageant_wide, pageant_wide).to_i == 0
|
114
|
+
@logger.debug { "Disabling use_agent in net-ssh: pageant process not running" }
|
115
|
+
options[:use_agent] = false
|
116
|
+
end
|
113
117
|
end
|
118
|
+
else
|
119
|
+
# Disable ssh config and ssh-agent if requested via load_config
|
120
|
+
options[:config] = false
|
121
|
+
options[:use_agent] = false
|
114
122
|
end
|
115
123
|
|
116
124
|
@session = Net::SSH.start(target.host, @user, options)
|
@@ -302,12 +310,19 @@ module Bolt
|
|
302
310
|
|
303
311
|
def write_remote_executable(dir, file, filename = nil)
|
304
312
|
filename ||= File.basename(file)
|
305
|
-
remote_path =
|
313
|
+
remote_path = File.join(dir.to_s, filename)
|
306
314
|
write_remote_file(file, remote_path)
|
307
315
|
make_executable(remote_path)
|
308
316
|
remote_path
|
309
317
|
end
|
310
318
|
|
319
|
+
def write_executable_from_content(dest, content, filename)
|
320
|
+
remote_path = File.join(dest.to_s, filename)
|
321
|
+
@session.scp.upload!(StringIO.new(content), remote_path)
|
322
|
+
make_executable(remote_path)
|
323
|
+
remote_path
|
324
|
+
end
|
325
|
+
|
311
326
|
def make_executable(path)
|
312
327
|
result = execute(['chmod', 'u+x', path])
|
313
328
|
if result.exit_code != 0
|
data/lib/bolt/transport/winrm.rb
CHANGED
@@ -69,6 +69,9 @@ module Bolt
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def run_script(target, script, arguments, _options = {})
|
72
|
+
# unpack any Sensitive data
|
73
|
+
arguments = unwrap_sensitive_args(arguments)
|
74
|
+
|
72
75
|
with_connection(target) do |conn|
|
73
76
|
conn.with_remote_file(script) do |remote_path|
|
74
77
|
if powershell_file?(remote_path)
|
@@ -103,8 +106,21 @@ catch
|
|
103
106
|
end
|
104
107
|
|
105
108
|
def run_task(target, task, arguments, _options = {})
|
106
|
-
|
107
|
-
|
109
|
+
if from_api?(task)
|
110
|
+
# TODO: Remove as part of BOLT-664
|
111
|
+
dir = Dir.mktmpdir
|
112
|
+
executable = File.join(dir, task.file['filename'])
|
113
|
+
File.open(executable, 'w') { |f|
|
114
|
+
f.write(Base64.decode64(task.file['file_content']))
|
115
|
+
}
|
116
|
+
task.input_method = powershell_file?(executable) ? 'powershell' : 'both'
|
117
|
+
else
|
118
|
+
executable = target.select_impl(task, PROVIDED_FEATURES)
|
119
|
+
raise "No suitable implementation of #{task.name} for #{target.name}" unless executable
|
120
|
+
end
|
121
|
+
|
122
|
+
# unpack any Sensitive data
|
123
|
+
arguments = unwrap_sensitive_args(arguments)
|
108
124
|
|
109
125
|
input_method = task.input_method
|
110
126
|
input_method ||= powershell_file?(executable) ? 'powershell' : 'both'
|
@@ -114,12 +130,11 @@ catch
|
|
114
130
|
end
|
115
131
|
|
116
132
|
if ENVIRONMENT_METHODS.include?(input_method)
|
117
|
-
arguments.each do |(arg, val)|
|
118
|
-
|
119
|
-
cmd = "[Environment]::SetEnvironmentVariable('PT_#{arg}', @'\n#{val}\n'@)"
|
133
|
+
envify_params(arguments).each do |(arg, val)|
|
134
|
+
cmd = "[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
|
120
135
|
result = conn.execute(cmd)
|
121
136
|
if result.exit_code != 0
|
122
|
-
raise EnvironmentVarError(
|
137
|
+
raise Bolt::Node::EnvironmentVarError.new(arg, val)
|
123
138
|
end
|
124
139
|
end
|
125
140
|
end
|
@@ -147,6 +162,10 @@ try { & "#{remote_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
|
|
147
162
|
path, args = *process_from_extension(remote_path)
|
148
163
|
conn.execute_process(path, args, stdin)
|
149
164
|
end
|
165
|
+
|
166
|
+
if from_api?(task)
|
167
|
+
FileUtils.remove_entry dir
|
168
|
+
end
|
150
169
|
Bolt::Result.for_task(target, output.stdout.string,
|
151
170
|
output.stderr.string,
|
152
171
|
output.exit_code)
|
data/lib/bolt/version.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'sinatra'
|
4
|
+
require 'bolt'
|
5
|
+
require 'bolt/task'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class TransportAPI < Sinatra::Base
|
9
|
+
# This disables Sinatra's error page generation
|
10
|
+
set :show_exceptions, false
|
11
|
+
|
12
|
+
get '/' do
|
13
|
+
200
|
14
|
+
end
|
15
|
+
|
16
|
+
get '/500_error' do
|
17
|
+
raise 'Unexpected error'
|
18
|
+
end
|
19
|
+
|
20
|
+
post '/ssh/run_task' do
|
21
|
+
content_type :json
|
22
|
+
|
23
|
+
body = JSON.parse(request.body.read)
|
24
|
+
keys = %w[user password port ssh-key-content connect-timeout run-as-command run-as
|
25
|
+
tmpdir host-key-check known-hosts-content private-key-content sudo-password]
|
26
|
+
opts = body['target'].select { |k, _| keys.include? k }
|
27
|
+
|
28
|
+
if opts['private-key-content'] && opts['password']
|
29
|
+
return [400, "Only include one of 'password' and 'private-key-content'"]
|
30
|
+
end
|
31
|
+
if opts['private-key-content']
|
32
|
+
opts['private-key'] = { 'key-data' => opts['private-key-content'] }
|
33
|
+
opts.delete('private-key-content')
|
34
|
+
end
|
35
|
+
|
36
|
+
target = [Bolt::Target.new(body['target']['hostname'], opts)]
|
37
|
+
task = Bolt::Task.new(body['task'])
|
38
|
+
parameters = body['parameters'] || {}
|
39
|
+
|
40
|
+
executor = Bolt::Executor.new(load_config: false)
|
41
|
+
|
42
|
+
# Since this will only be on one node we can just return the first result
|
43
|
+
results = executor.run_task(target, task, parameters)
|
44
|
+
[200, results.first.to_json]
|
45
|
+
end
|
46
|
+
|
47
|
+
post '/winrm/run_task' do
|
48
|
+
content_type :json
|
49
|
+
|
50
|
+
body = JSON.parse(request.body.read)
|
51
|
+
keys = %w[user password port connect-timeout ssl ssl-verify tmpdir cacert extensions]
|
52
|
+
opts = body['target'].select { |k, _| keys.include? k }
|
53
|
+
opts['protocol'] = 'winrm'
|
54
|
+
target = [Bolt::Target.new(body['target']['hostname'], opts)]
|
55
|
+
task = Bolt::Task.new(body['task'])
|
56
|
+
parameters = body['parameters'] || {}
|
57
|
+
|
58
|
+
executor = Bolt::Executor.new(load_config: false)
|
59
|
+
|
60
|
+
# Since this will only be on one node we can just return the first result
|
61
|
+
results = executor.run_task(target, task, parameters)
|
62
|
+
[200, results.first.to_json]
|
63
|
+
end
|
64
|
+
|
65
|
+
error 404 do
|
66
|
+
[404, "Could not find route #{request.path}"]
|
67
|
+
end
|
68
|
+
|
69
|
+
error 500 do
|
70
|
+
e = env['sinatra.error']
|
71
|
+
[500, "500: Unknown error: #{e.message}"]
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails/auth/rack'
|
4
|
+
|
5
|
+
class TransportACL < Rails::Auth::ErrorPage::Middleware
|
6
|
+
class X509Matcher
|
7
|
+
def initialize(options)
|
8
|
+
@options = options.freeze
|
9
|
+
end
|
10
|
+
|
11
|
+
def match(env)
|
12
|
+
certificate = Rails::Auth::X509::Certificate.new(env['puma.peercert'])
|
13
|
+
# This can be extended fairly easily to search OpenSSL::X509::Certificate#extensions for subjectAltNames.
|
14
|
+
@options.all? { |name, value| certificate[name] == value }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(app, whitelist)
|
19
|
+
acls = []
|
20
|
+
whitelist.each do |entry|
|
21
|
+
acls << {
|
22
|
+
'resources' => [
|
23
|
+
{
|
24
|
+
'method' => 'ALL',
|
25
|
+
'path' => '/.*'
|
26
|
+
}
|
27
|
+
],
|
28
|
+
'allow_x509_subject' => {
|
29
|
+
'cn' => entry
|
30
|
+
}
|
31
|
+
}
|
32
|
+
end
|
33
|
+
acl = Rails::Auth::ACL.new(acls, matchers: { allow_x509_subject: X509Matcher })
|
34
|
+
mid = Rails::Auth::ACL::Middleware.new(app, acl: acl)
|
35
|
+
super(mid, page_body: 'Access denied')
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'hocon'
|
4
|
+
|
5
|
+
class TransportConfig
|
6
|
+
attr_accessor :host, :port, :ssl_cert, :ssl_key, :ssl_ca_cert, :ssl_cipher_suites,
|
7
|
+
:loglevel, :logfile, :whitelist, :concurrency
|
8
|
+
|
9
|
+
def initialize(global = nil, local = nil)
|
10
|
+
@host = '127.0.0.1'
|
11
|
+
@port = 62658
|
12
|
+
@ssl_cert = nil
|
13
|
+
@ssl_key = nil
|
14
|
+
@ssl_ca_cert = nil
|
15
|
+
@ssl_cipher_suites = ['ECDHE-ECDSA-AES256-GCM-SHA384',
|
16
|
+
'ECDHE-RSA-AES256-GCM-SHA384',
|
17
|
+
'ECDHE-ECDSA-CHACHA20-POLY1305',
|
18
|
+
'ECDHE-RSA-CHACHA20-POLY1305',
|
19
|
+
'ECDHE-ECDSA-AES128-GCM-SHA256',
|
20
|
+
'ECDHE-RSA-AES128-GCM-SHA256',
|
21
|
+
'ECDHE-ECDSA-AES256-SHA384',
|
22
|
+
'ECDHE-RSA-AES256-SHA384',
|
23
|
+
'ECDHE-ECDSA-AES128-SHA256',
|
24
|
+
'ECDHE-RSA-AES128-SHA256']
|
25
|
+
|
26
|
+
@loglevel = 'notice'
|
27
|
+
@logfile = nil
|
28
|
+
@whitelist = nil
|
29
|
+
@concurrency = 100
|
30
|
+
|
31
|
+
global_path = global || '/etc/puppetlabs/bolt-server/conf.d/bolt-server.conf'
|
32
|
+
local_path = local || File.join(ENV['HOME'].to_s, ".puppetlabs", "bolt-server.conf")
|
33
|
+
|
34
|
+
load_config(global_path)
|
35
|
+
load_config(local_path)
|
36
|
+
validate
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_config(path)
|
40
|
+
begin
|
41
|
+
parsed_hocon = Hocon.load(path)['bolt-server']
|
42
|
+
rescue Hocon::ConfigError => e
|
43
|
+
raise "Hocon data in '#{path}' failed to load.\n Error: '#{e.message}'"
|
44
|
+
rescue Errno::EACCES
|
45
|
+
raise "Your user doesn't have permission to read #{path}"
|
46
|
+
end
|
47
|
+
|
48
|
+
unless parsed_hocon.nil?
|
49
|
+
%w[host port ssl-cert ssl-key ssl-ca-cert ssl-cipher-suites loglevel logfile whitelist concurrency].each do |key|
|
50
|
+
varname = '@' + key.tr('-', '_')
|
51
|
+
instance_variable_set(varname, parsed_hocon[key]) if parsed_hocon.key?(key)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate
|
57
|
+
required_keys = %w[ssl_cert ssl_key ssl_ca_cert]
|
58
|
+
ssl_keys = %w[ssl_cert ssl_key ssl_ca_cert]
|
59
|
+
required_keys.each do |k|
|
60
|
+
next unless send(k).nil?
|
61
|
+
raise Bolt::ValidationError, <<-MSG
|
62
|
+
You must configure #{k} in either /etc/puppetlabs/bolt-server/conf.d/bolt-server.conf or ~/.puppetlabs/bolt-server.conf
|
63
|
+
MSG
|
64
|
+
end
|
65
|
+
|
66
|
+
unless @port.is_a?(Integer) && @port > 0
|
67
|
+
raise Bolt::ValidationError, "Configured 'port' must be a valid integer greater than 0"
|
68
|
+
end
|
69
|
+
ssl_keys.each do |sk|
|
70
|
+
unless File.file?(send(sk)) && File.readable?(send(sk))
|
71
|
+
raise Bolt::ValidationError, "Configured #{sk} must be a valid filepath"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
unless @ssl_cipher_suites.is_a?(Array)
|
76
|
+
raise Bolt::ValidationError, "Configured 'ssl-cipher-suites' must be an array of cipher suite names"
|
77
|
+
end
|
78
|
+
|
79
|
+
unless @whitelist.nil? || @whitelist.is_a?(Array)
|
80
|
+
raise Bolt::ValidationError, "Configured 'whitelist' must be an array of names"
|
81
|
+
end
|
82
|
+
|
83
|
+
unless @concurrency.is_a?(Integer) && @concurrency.positive?
|
84
|
+
raise Bolt::ValidationError, "Configured 'concurrency' must be a positive integer"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
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: 0.
|
4
|
+
version: 0.22.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -282,6 +282,7 @@ email:
|
|
282
282
|
executables:
|
283
283
|
- bolt
|
284
284
|
- bolt-inventory-pdb
|
285
|
+
- bolt-server
|
285
286
|
extensions: []
|
286
287
|
extra_rdoc_files: []
|
287
288
|
files:
|
@@ -302,12 +303,14 @@ files:
|
|
302
303
|
- bolt-modules/boltlib/lib/puppet/functions/run_task.rb
|
303
304
|
- bolt-modules/boltlib/lib/puppet/functions/set_feature.rb
|
304
305
|
- bolt-modules/boltlib/lib/puppet/functions/set_var.rb
|
306
|
+
- bolt-modules/boltlib/lib/puppet/functions/upload_file.rb
|
305
307
|
- bolt-modules/boltlib/lib/puppet/functions/vars.rb
|
306
308
|
- bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb
|
307
309
|
- bolt-modules/boltlib/types/planresult.pp
|
308
310
|
- bolt-modules/boltlib/types/targetspec.pp
|
309
311
|
- exe/bolt
|
310
312
|
- exe/bolt-inventory-pdb
|
313
|
+
- exe/bolt-server
|
311
314
|
- lib/bolt.rb
|
312
315
|
- lib/bolt/analytics.rb
|
313
316
|
- lib/bolt/applicator.rb
|
@@ -340,6 +343,7 @@ files:
|
|
340
343
|
- lib/bolt/result.rb
|
341
344
|
- lib/bolt/result_set.rb
|
342
345
|
- lib/bolt/target.rb
|
346
|
+
- lib/bolt/task.rb
|
343
347
|
- lib/bolt/transport/base.rb
|
344
348
|
- lib/bolt/transport/local.rb
|
345
349
|
- lib/bolt/transport/local/shell.rb
|
@@ -353,6 +357,9 @@ files:
|
|
353
357
|
- lib/bolt/util/puppet_log_level.rb
|
354
358
|
- lib/bolt/version.rb
|
355
359
|
- lib/bolt_ext/puppetdb_inventory.rb
|
360
|
+
- lib/bolt_ext/server.rb
|
361
|
+
- lib/bolt_ext/server_acl.rb
|
362
|
+
- lib/bolt_ext/server_config.rb
|
356
363
|
- lib/bolt_spec/plans.rb
|
357
364
|
- lib/bolt_spec/plans/mock_executor.rb
|
358
365
|
- lib/bolt_spec/run.rb
|