bolt 0.22.0 → 0.23.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/lib/bolt/applicator.rb +3 -2
- data/lib/bolt/bolt_option_parser.rb +5 -2
- data/lib/bolt/boltdir.rb +1 -1
- data/lib/bolt/config.rb +9 -3
- data/lib/bolt/executor.rb +5 -1
- data/lib/bolt/pal.rb +12 -2
- data/lib/bolt/target.rb +2 -6
- data/lib/bolt/transport/base.rb +4 -4
- data/lib/bolt/transport/ssh.rb +1 -1
- data/lib/bolt/transport/winrm.rb +19 -17
- data/lib/bolt/transport/winrm/connection.rb +28 -16
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_ext/schemas/ssh-run_task.json +71 -0
- data/lib/bolt_ext/schemas/task.json +57 -0
- data/lib/bolt_ext/schemas/winrm-run_task.json +62 -0
- data/lib/bolt_ext/server.rb +34 -9
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7e3c1e13c0b81acdf948d563eb5acc20710ba52c6aacd14580defbb722fa6ca
|
4
|
+
data.tar.gz: 5a382bffe6ac5eb12e7214a5f0f64581366a4e224f0a1968e5db8a656994f5a1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5793f401349ef2a8985b97c4938cf61497c51abb39fcd65ca2fbc3c4173719366d659eeb302fb145e1e198e415ed62b50728743f0bf98440b9d8d837ce28617c
|
7
|
+
data.tar.gz: c9dc1835008deda159c9e317134dc68c67a24d5cab366cf72d608fd067d854d58ce1084f289aacb7764008a085d26363c82b186b6885ecf2bdab6ddf0eecc464
|
data/lib/bolt/applicator.rb
CHANGED
@@ -12,10 +12,11 @@ require 'bolt/util/puppet_log_level'
|
|
12
12
|
|
13
13
|
module Bolt
|
14
14
|
class Applicator
|
15
|
-
def initialize(inventory, executor, modulepath, pdb_client, hiera_config, max_compiles)
|
15
|
+
def initialize(inventory, executor, modulepath, plugin_dirs, pdb_client, hiera_config, max_compiles)
|
16
16
|
@inventory = inventory
|
17
17
|
@executor = executor
|
18
18
|
@modulepath = modulepath
|
19
|
+
@plugin_dirs = plugin_dirs
|
19
20
|
@pdb_client = pdb_client
|
20
21
|
@hiera_config = hiera_config ? validate_hiera_config(hiera_config) : nil
|
21
22
|
|
@@ -222,7 +223,7 @@ module Bolt
|
|
222
223
|
sio = StringIO.new
|
223
224
|
output = Minitar::Output.new(Zlib::GzipWriter.new(sio))
|
224
225
|
|
225
|
-
Puppet.lookup(:current_environment).modules.each do |mod|
|
226
|
+
Puppet.lookup(:current_environment).override_with(modulepath: @plugin_dirs).modules.each do |mod|
|
226
227
|
search_dirs = yield mod
|
227
228
|
|
228
229
|
parent = Pathname.new(mod.path).parent
|
@@ -202,7 +202,10 @@ Available options are:
|
|
202
202
|
end
|
203
203
|
define('--modulepath MODULES',
|
204
204
|
"List of directories containing modules, separated by '#{File::PATH_SEPARATOR}'") do |modulepath|
|
205
|
-
|
205
|
+
# When specified from the CLI, modulepath entries are relative to pwd
|
206
|
+
@options[:modulepath] = modulepath.split(File::PATH_SEPARATOR).map do |moduledir|
|
207
|
+
File.expand_path(moduledir)
|
208
|
+
end
|
206
209
|
end
|
207
210
|
define('--boltdir FILEPATH',
|
208
211
|
'Specify what Boltdir to load config from (default: autodiscovered from current working dir)') do |path|
|
@@ -217,7 +220,7 @@ Available options are:
|
|
217
220
|
if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
|
218
221
|
raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
|
219
222
|
end
|
220
|
-
@options[:inventoryfile] = path
|
223
|
+
@options[:inventoryfile] = File.expand_path(path)
|
221
224
|
end
|
222
225
|
|
223
226
|
separator 'Transports:'
|
data/lib/bolt/boltdir.rb
CHANGED
@@ -25,7 +25,7 @@ module Bolt
|
|
25
25
|
@path = Pathname.new(path).expand_path
|
26
26
|
@config_file = @path + 'bolt.yaml'
|
27
27
|
@inventory_file = @path + 'inventory.yaml'
|
28
|
-
@modulepath = [(@path + 'modules').to_s]
|
28
|
+
@modulepath = [(@path + 'modules').to_s, (@path + 'site').to_s]
|
29
29
|
@hiera_config = @path + 'hiera.yaml'
|
30
30
|
@puppetfile = @path + 'Puppetfile'
|
31
31
|
end
|
data/lib/bolt/config.rb
CHANGED
@@ -148,11 +148,17 @@ module Bolt
|
|
148
148
|
update_logs(data['log'])
|
149
149
|
end
|
150
150
|
|
151
|
-
|
151
|
+
# Expand paths relative to the Boltdir. Any settings that came from the
|
152
|
+
# CLI will already be absolute, so the expand will be skipped.
|
153
|
+
if data.key?('modulepath')
|
154
|
+
@modulepath = data['modulepath'].split(File::PATH_SEPARATOR).map do |moduledir|
|
155
|
+
File.expand_path(moduledir, @boltdir.path)
|
156
|
+
end
|
157
|
+
end
|
152
158
|
|
153
|
-
@inventoryfile = data['inventoryfile'] if data.key?('inventoryfile')
|
159
|
+
@inventoryfile = File.expand_path(data['inventoryfile'], @boltdir.path) if data.key?('inventoryfile')
|
154
160
|
|
155
|
-
@hiera_config = data['hiera-config'] if data.key?('hiera-config')
|
161
|
+
@hiera_config = File.expand_path(data['hiera-config'], @boltdir.path) if data.key?('hiera-config')
|
156
162
|
@compile_concurrency = data['compile-concurrency'] if data.key?('compile-concurrency')
|
157
163
|
|
158
164
|
%w[concurrency format puppetdb color transport].each do |key|
|
data/lib/bolt/executor.rb
CHANGED
@@ -40,7 +40,11 @@ module Bolt
|
|
40
40
|
|
41
41
|
@noop = noop
|
42
42
|
@run_as = nil
|
43
|
-
@pool =
|
43
|
+
@pool = if concurrency > 0
|
44
|
+
Concurrent::ThreadPoolExecutor.new(max_threads: concurrency)
|
45
|
+
else
|
46
|
+
Concurrent.global_immediate_executor
|
47
|
+
end
|
44
48
|
@logger.debug { "Started with #{concurrency} max thread(s)" }
|
45
49
|
@notifier = Bolt::Notifier.new
|
46
50
|
end
|
data/lib/bolt/pal.rb
CHANGED
@@ -8,8 +8,8 @@ require 'bolt/applicator'
|
|
8
8
|
|
9
9
|
module Bolt
|
10
10
|
class PAL
|
11
|
-
BOLTLIB_PATH = File.
|
12
|
-
MODULES_PATH = File.
|
11
|
+
BOLTLIB_PATH = File.expand_path('../../bolt-modules', __dir__)
|
12
|
+
MODULES_PATH = File.expand_path('../../modules', __dir__)
|
13
13
|
|
14
14
|
# PALError is used to convert errors from executing puppet code into
|
15
15
|
# Bolt::Errors
|
@@ -44,9 +44,15 @@ module Bolt
|
|
44
44
|
# This makes sure we don't accidentally create puppet dirs
|
45
45
|
with_puppet_settings { |_| nil }
|
46
46
|
|
47
|
+
@original_modulepath = modulepath
|
47
48
|
@modulepath = [BOLTLIB_PATH, *modulepath, MODULES_PATH]
|
48
49
|
@hiera_config = hiera_config
|
49
50
|
@max_compiles = max_compiles
|
51
|
+
|
52
|
+
@logger = Logging.logger[self]
|
53
|
+
if modulepath && !modulepath.empty?
|
54
|
+
@logger.info("Loading modules from #{@modulepath.join(File::PATH_SEPARATOR)}")
|
55
|
+
end
|
50
56
|
end
|
51
57
|
|
52
58
|
# Puppet logging is global so this is class method to avoid confusion
|
@@ -123,6 +129,10 @@ module Bolt
|
|
123
129
|
inventory,
|
124
130
|
executor,
|
125
131
|
@modulepath,
|
132
|
+
# Skip syncing built-in plugins, since we vendor some Puppet 6
|
133
|
+
# versions of "core" types, which are already present on the agent,
|
134
|
+
# but may cause issues on Puppet 5 agents.
|
135
|
+
@original_modulepath,
|
126
136
|
pdb_client,
|
127
137
|
@hiera_config,
|
128
138
|
@max_compiles
|
data/lib/bolt/target.rb
CHANGED
@@ -107,15 +107,11 @@ module Bolt
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def user
|
110
|
-
Addressable::URI.unencode_component(
|
111
|
-
@uri_obj.user || @user
|
112
|
-
)
|
110
|
+
Addressable::URI.unencode_component(@uri_obj.user) || @user
|
113
111
|
end
|
114
112
|
|
115
113
|
def password
|
116
|
-
Addressable::URI.unencode_component(
|
117
|
-
@uri_obj.password || @password
|
118
|
-
)
|
114
|
+
Addressable::URI.unencode_component(@uri_obj.password) || @password
|
119
115
|
end
|
120
116
|
end
|
121
117
|
end
|
data/lib/bolt/transport/base.rb
CHANGED
@@ -58,10 +58,10 @@ module Bolt
|
|
58
58
|
callback&.call(type: :node_start, target: target)
|
59
59
|
|
60
60
|
result = begin
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
yield
|
62
|
+
rescue StandardError => ex
|
63
|
+
Bolt::Result.from_exception(target, ex)
|
64
|
+
end
|
65
65
|
|
66
66
|
callback&.call(type: :node_result, result: result)
|
67
67
|
result
|
data/lib/bolt/transport/ssh.rb
CHANGED
data/lib/bolt/transport/winrm.rb
CHANGED
@@ -73,7 +73,8 @@ module Bolt
|
|
73
73
|
arguments = unwrap_sensitive_args(arguments)
|
74
74
|
|
75
75
|
with_connection(target) do |conn|
|
76
|
-
conn.
|
76
|
+
conn.with_remote_tempdir do |dir|
|
77
|
+
remote_path = conn.write_remote_executable(dir, script)
|
77
78
|
if powershell_file?(remote_path)
|
78
79
|
mapped_args = arguments.map do |a|
|
79
80
|
"$invokeArgs.ArgumentList += @'\n#{a}\n'@"
|
@@ -107,13 +108,9 @@ catch
|
|
107
108
|
|
108
109
|
def run_task(target, task, arguments, _options = {})
|
109
110
|
if from_api?(task)
|
110
|
-
|
111
|
-
|
112
|
-
|
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'
|
111
|
+
task.input_method = powershell_file?(task["file"]["filename"]) ? 'powershell' : 'both'
|
112
|
+
executable = { filename: task["file"]["filename"],
|
113
|
+
file_content: StringIO.new(Base64.decode64(task.file['file_content'])) }
|
117
114
|
else
|
118
115
|
executable = target.select_impl(task, PROVIDED_FEATURES)
|
119
116
|
raise "No suitable implementation of #{task.name} for #{target.name}" unless executable
|
@@ -139,9 +136,17 @@ catch
|
|
139
136
|
end
|
140
137
|
end
|
141
138
|
|
142
|
-
conn.
|
139
|
+
conn.with_remote_tempdir do |dir|
|
140
|
+
remote_task_path = if from_api?(task)
|
141
|
+
conn.write_executable_from_content(dir,
|
142
|
+
executable[:file_content],
|
143
|
+
executable[:filename])
|
144
|
+
else
|
145
|
+
conn.write_remote_executable(dir, executable)
|
146
|
+
end
|
147
|
+
conn.shell_init
|
143
148
|
output =
|
144
|
-
if powershell_file?(
|
149
|
+
if powershell_file?(remote_task_path) && stdin.nil?
|
145
150
|
# NOTE: cannot redirect STDIN to a .ps1 script inside of PowerShell
|
146
151
|
# must create new powershell.exe process like other interpreters
|
147
152
|
# fortunately, using PS with stdin input_method should never happen
|
@@ -150,22 +155,19 @@ catch
|
|
150
155
|
$private:tempArgs = Get-ContentAsJson (
|
151
156
|
$utf8.GetString([System.Convert]::FromBase64String('#{Base64.encode64(JSON.dump(arguments))}'))
|
152
157
|
)
|
153
|
-
$allowedArgs = (Get-Command "#{
|
158
|
+
$allowedArgs = (Get-Command "#{remote_task_path}").Parameters.Keys
|
154
159
|
$private:taskArgs = @{}
|
155
160
|
$private:tempArgs.Keys | ? { $allowedArgs -contains $_ } | % { $private:taskArgs[$_] = $private:tempArgs[$_] }
|
156
|
-
try { & "#{
|
161
|
+
try { & "#{remote_task_path}" @taskArgs } catch { Write-Error $_.Exception; exit 1 }
|
157
162
|
PS
|
158
163
|
else
|
159
|
-
conn.execute(%(try { & "#{
|
164
|
+
conn.execute(%(try { & "#{remote_task_path}" } catch { Write-Error $_.Exception; exit 1 }))
|
160
165
|
end
|
161
166
|
else
|
162
|
-
path, args = *process_from_extension(
|
167
|
+
path, args = *process_from_extension(remote_task_path)
|
163
168
|
conn.execute_process(path, args, stdin)
|
164
169
|
end
|
165
170
|
|
166
|
-
if from_api?(task)
|
167
|
-
FileUtils.remove_entry dir
|
168
|
-
end
|
169
171
|
Bolt::Result.for_task(target, output.stdout.string,
|
170
172
|
output.stderr.string,
|
171
173
|
output.exit_code)
|
@@ -99,6 +99,7 @@ module Bolt
|
|
99
99
|
return nil if @shell_initialized
|
100
100
|
result = execute(<<-PS)
|
101
101
|
$ENV:PATH += ";${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\bin\\;" +
|
102
|
+
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\bin;" +
|
102
103
|
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\sys\\ruby\\bin\\"
|
103
104
|
$ENV:RUBYLIB = "${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\puppet\\lib;" +
|
104
105
|
"${ENV:ProgramFiles}\\Puppet Labs\\Puppet\\facter\\lib;" +
|
@@ -335,8 +336,9 @@ PS
|
|
335
336
|
|
336
337
|
def write_remote_file(source, destination)
|
337
338
|
fs = ::WinRM::FS::FileManager.new(@connection)
|
338
|
-
# TODO: raise FileError here if this fails
|
339
339
|
fs.upload(source, destination)
|
340
|
+
rescue StandardError => e
|
341
|
+
raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
|
340
342
|
end
|
341
343
|
|
342
344
|
def make_tempdir
|
@@ -354,25 +356,35 @@ PS
|
|
354
356
|
result.stdout.string.chomp
|
355
357
|
end
|
356
358
|
|
357
|
-
def
|
358
|
-
|
359
|
+
def with_remote_tempdir
|
360
|
+
dir = make_tempdir
|
361
|
+
yield dir
|
362
|
+
ensure
|
363
|
+
execute(<<-PS)
|
364
|
+
Remove-Item -Force -Recurse -Path "#{dir}"
|
365
|
+
PS
|
366
|
+
end
|
367
|
+
|
368
|
+
def validate_extensions(ext)
|
359
369
|
unless @extensions.include?(ext)
|
360
370
|
raise Bolt::Node::FileError.new("File extension #{ext} is not enabled, "\
|
361
371
|
"to run it please add to 'winrm: extensions'", 'FILETYPE_ERROR')
|
362
372
|
end
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
373
|
+
end
|
374
|
+
|
375
|
+
def write_remote_executable(dir, file, filename = nil)
|
376
|
+
filename ||= File.basename(file)
|
377
|
+
validate_extensions(File.extname(filename))
|
378
|
+
remote_path = "#{dir}\\#{filename}"
|
379
|
+
write_remote_file(file, remote_path)
|
380
|
+
remote_path
|
381
|
+
end
|
382
|
+
|
383
|
+
def write_executable_from_content(dir, content, filename)
|
384
|
+
validate_extensions(File.extname(filename))
|
385
|
+
remote_path = "#{dir}\\#{filename}"
|
386
|
+
write_remote_file(content, remote_path)
|
387
|
+
remote_path
|
376
388
|
end
|
377
389
|
end
|
378
390
|
end
|
data/lib/bolt/version.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "SSH run_task request",
|
4
|
+
"description": "POST ssh/run_task request schema",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"target": {
|
8
|
+
"type": "object",
|
9
|
+
"description": "Target information to run task on",
|
10
|
+
"properties": {
|
11
|
+
"hostname": {
|
12
|
+
"type": "string",
|
13
|
+
"description": "Target identifier"
|
14
|
+
},
|
15
|
+
"user": {
|
16
|
+
"type": "string",
|
17
|
+
"description": "Login user"
|
18
|
+
},
|
19
|
+
"password": {
|
20
|
+
"type": "string",
|
21
|
+
"description": "Password for SSH transport authentication"
|
22
|
+
},
|
23
|
+
"private-key-content": {
|
24
|
+
"type": "string",
|
25
|
+
"description": "Contents of private key for SSH"
|
26
|
+
},
|
27
|
+
"port": {
|
28
|
+
"type": "integer",
|
29
|
+
"description": "Connection port"
|
30
|
+
},
|
31
|
+
"connect-timeout": {
|
32
|
+
"type": "integer",
|
33
|
+
"description": "How long Bolt should wait when establishing connections"
|
34
|
+
},
|
35
|
+
"run-as-command": {
|
36
|
+
"type": "array",
|
37
|
+
"description": "Command elevate permissions",
|
38
|
+
"items": { "type": "string" }
|
39
|
+
},
|
40
|
+
"run-as": {
|
41
|
+
"type": "string",
|
42
|
+
"description": "A different user to run commands as after login"
|
43
|
+
},
|
44
|
+
"tmpdir": {
|
45
|
+
"type": "string",
|
46
|
+
"description": "The directory to upload and execute temporary files on the target"
|
47
|
+
},
|
48
|
+
"host-key-check": {
|
49
|
+
"type": "boolean",
|
50
|
+
"description": "Whether to perform host key validation when connecting over SSH"
|
51
|
+
},
|
52
|
+
"sudo-password": {
|
53
|
+
"type": "string",
|
54
|
+
"description": "Password to use when changing users via run-as"
|
55
|
+
}
|
56
|
+
},
|
57
|
+
"oneOf": [
|
58
|
+
{ "required": ["password"] },
|
59
|
+
{ "required": ["private-key-content"] }
|
60
|
+
],
|
61
|
+
"required": ["hostname", "user"],
|
62
|
+
"additionalProperties": false
|
63
|
+
},
|
64
|
+
"task": { "$ref": "file:task"},
|
65
|
+
"parameters": {
|
66
|
+
"type": "object",
|
67
|
+
"description": "JSON formatted parameters to be provided to task"
|
68
|
+
}
|
69
|
+
},
|
70
|
+
"required": ["target", "task"]
|
71
|
+
}
|
@@ -0,0 +1,57 @@
|
|
1
|
+
{
|
2
|
+
"id": "file:task",
|
3
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
4
|
+
"title": "Task",
|
5
|
+
"description": "Task schema for bolt-server",
|
6
|
+
"type": "object",
|
7
|
+
"description": "The task is a JSON object which includes the following keys",
|
8
|
+
"properties": {
|
9
|
+
"name": {
|
10
|
+
"type": "string",
|
11
|
+
"description": "Task name"
|
12
|
+
},
|
13
|
+
"metadata": {
|
14
|
+
"type": "object",
|
15
|
+
"description": "The metadata object is optional, and contains metadata about the task being run",
|
16
|
+
"properties": {
|
17
|
+
"description": {
|
18
|
+
"type": "string",
|
19
|
+
"description": "The task description from it's metadata"
|
20
|
+
},
|
21
|
+
"parameters": {
|
22
|
+
"type": "object",
|
23
|
+
"description": "Object whose keys are parameter names, and values are objects",
|
24
|
+
"properties": {
|
25
|
+
"description": {
|
26
|
+
"type": "string",
|
27
|
+
"description": "Parameter description"
|
28
|
+
},
|
29
|
+
"type": {
|
30
|
+
"type": "string",
|
31
|
+
"description": "The type the parameter should accept"
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
},
|
36
|
+
"additionalProperties": false
|
37
|
+
},
|
38
|
+
"file": {
|
39
|
+
"type": "object",
|
40
|
+
"description": "File name and content",
|
41
|
+
"properties": {
|
42
|
+
"filename": {
|
43
|
+
"type": "string",
|
44
|
+
"description": "Name of the task file"
|
45
|
+
},
|
46
|
+
"file_content": {
|
47
|
+
"type": "string",
|
48
|
+
"description": "Task's base64 encoded file content"
|
49
|
+
}
|
50
|
+
},
|
51
|
+
"required": ["filename", "file_content"],
|
52
|
+
"additionalProperties": false
|
53
|
+
}
|
54
|
+
},
|
55
|
+
"required": ["name", "file"],
|
56
|
+
"additionalProperties": false
|
57
|
+
}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "WinRm run_task request",
|
4
|
+
"description": "POST winrm/run_task request schema",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"target": {
|
8
|
+
"type": "object",
|
9
|
+
"description": "Target information to run task on",
|
10
|
+
"properties": {
|
11
|
+
"hostname": {
|
12
|
+
"type": "string",
|
13
|
+
"description": "Target identifier"
|
14
|
+
},
|
15
|
+
"user": {
|
16
|
+
"type": "string",
|
17
|
+
"description": "Login user"
|
18
|
+
},
|
19
|
+
"password": {
|
20
|
+
"type": "string",
|
21
|
+
"description": "Password for SSH transport authentication"
|
22
|
+
},
|
23
|
+
"port": {
|
24
|
+
"type": "integer",
|
25
|
+
"description": "Connection port"
|
26
|
+
},
|
27
|
+
"connect-timeout": {
|
28
|
+
"type": "integer",
|
29
|
+
"description": "How long Bolt should wait when establishing connections"
|
30
|
+
},
|
31
|
+
"tmpdir": {
|
32
|
+
"type": "string",
|
33
|
+
"description": "The directory to upload and execute temporary files on the target"
|
34
|
+
},
|
35
|
+
"ssl": {
|
36
|
+
"type": "boolean",
|
37
|
+
"description": "When true, Bolt will use https connections for WinRM"
|
38
|
+
},
|
39
|
+
"ssl-verify": {
|
40
|
+
"type": "boolean",
|
41
|
+
"description": "When true, verifies the targets certificate matches the cacert"
|
42
|
+
},
|
43
|
+
"cacert": {
|
44
|
+
"type": "string",
|
45
|
+
"description": "The path to the CA certificate"
|
46
|
+
},
|
47
|
+
"extensions": {
|
48
|
+
"type": "array",
|
49
|
+
"description": "List of file extensions that are accepted for scripts or tasks"
|
50
|
+
}
|
51
|
+
},
|
52
|
+
"required": ["hostname", "user", "password"],
|
53
|
+
"additionalProperties": false
|
54
|
+
},
|
55
|
+
"task": { "$ref": "file:task"},
|
56
|
+
"parameters": {
|
57
|
+
"type": "object",
|
58
|
+
"description": "JSON formatted parameters to be provided to task"
|
59
|
+
}
|
60
|
+
},
|
61
|
+
"required": ["target", "task"]
|
62
|
+
}
|
data/lib/bolt_ext/server.rb
CHANGED
@@ -4,15 +4,41 @@ require 'sinatra'
|
|
4
4
|
require 'bolt'
|
5
5
|
require 'bolt/task'
|
6
6
|
require 'json'
|
7
|
+
require 'json-schema'
|
7
8
|
|
8
9
|
class TransportAPI < Sinatra::Base
|
9
10
|
# This disables Sinatra's error page generation
|
10
11
|
set :show_exceptions, false
|
11
12
|
|
13
|
+
def initialize(app = nil)
|
14
|
+
@schemas = {
|
15
|
+
"ssh-run_task" => JSON.parse(File.read(File.join(__dir__, 'schemas', 'ssh-run_task.json'))),
|
16
|
+
"winrm-run_task" => JSON.parse(File.read(File.join(__dir__, 'schemas', 'winrm-run_task.json')))
|
17
|
+
}
|
18
|
+
shared_schema = JSON::Schema.new(JSON.parse(File.read(File.join(__dir__, 'schemas', 'task.json'))),
|
19
|
+
Addressable::URI.parse("file:task"))
|
20
|
+
JSON::Validator.add_schema(shared_schema)
|
21
|
+
|
22
|
+
@executor = Bolt::Executor.new(0, load_config: false)
|
23
|
+
|
24
|
+
super(app)
|
25
|
+
end
|
26
|
+
|
12
27
|
get '/' do
|
13
28
|
200
|
14
29
|
end
|
15
30
|
|
31
|
+
if ENV['RACK_ENV'] == 'dev'
|
32
|
+
get '/admin/gc' do
|
33
|
+
GC.start
|
34
|
+
200
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
get '/admin/gc_stat' do
|
39
|
+
[200, GC.stat.to_json]
|
40
|
+
end
|
41
|
+
|
16
42
|
get '/500_error' do
|
17
43
|
raise 'Unexpected error'
|
18
44
|
end
|
@@ -21,13 +47,13 @@ class TransportAPI < Sinatra::Base
|
|
21
47
|
content_type :json
|
22
48
|
|
23
49
|
body = JSON.parse(request.body.read)
|
50
|
+
schema_error = JSON::Validator.fully_validate(@schemas["ssh-run_task"], body)
|
51
|
+
return [400, schema_error.join] if schema_error.any?
|
52
|
+
|
24
53
|
keys = %w[user password port ssh-key-content connect-timeout run-as-command run-as
|
25
54
|
tmpdir host-key-check known-hosts-content private-key-content sudo-password]
|
26
55
|
opts = body['target'].select { |k, _| keys.include? k }
|
27
56
|
|
28
|
-
if opts['private-key-content'] && opts['password']
|
29
|
-
return [400, "Only include one of 'password' and 'private-key-content'"]
|
30
|
-
end
|
31
57
|
if opts['private-key-content']
|
32
58
|
opts['private-key'] = { 'key-data' => opts['private-key-content'] }
|
33
59
|
opts.delete('private-key-content')
|
@@ -37,10 +63,8 @@ class TransportAPI < Sinatra::Base
|
|
37
63
|
task = Bolt::Task.new(body['task'])
|
38
64
|
parameters = body['parameters'] || {}
|
39
65
|
|
40
|
-
executor = Bolt::Executor.new(load_config: false)
|
41
|
-
|
42
66
|
# Since this will only be on one node we can just return the first result
|
43
|
-
results = executor.run_task(target, task, parameters)
|
67
|
+
results = @executor.run_task(target, task, parameters)
|
44
68
|
[200, results.first.to_json]
|
45
69
|
end
|
46
70
|
|
@@ -48,6 +72,9 @@ class TransportAPI < Sinatra::Base
|
|
48
72
|
content_type :json
|
49
73
|
|
50
74
|
body = JSON.parse(request.body.read)
|
75
|
+
schema_error = JSON::Validator.fully_validate(@schemas["winrm-run_task"], body)
|
76
|
+
return [400, schema_error.join] if schema_error.any?
|
77
|
+
|
51
78
|
keys = %w[user password port connect-timeout ssl ssl-verify tmpdir cacert extensions]
|
52
79
|
opts = body['target'].select { |k, _| keys.include? k }
|
53
80
|
opts['protocol'] = 'winrm'
|
@@ -55,10 +82,8 @@ class TransportAPI < Sinatra::Base
|
|
55
82
|
task = Bolt::Task.new(body['task'])
|
56
83
|
parameters = body['parameters'] || {}
|
57
84
|
|
58
|
-
executor = Bolt::Executor.new(load_config: false)
|
59
|
-
|
60
85
|
# Since this will only be on one node we can just return the first result
|
61
|
-
results = executor.run_task(target, task, parameters)
|
86
|
+
results = @executor.run_task(target, task, parameters)
|
62
87
|
[200, results.first.to_json]
|
63
88
|
end
|
64
89
|
|
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.23.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-09-
|
11
|
+
date: 2018-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '1.
|
145
|
+
version: '1.3'
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '1.
|
152
|
+
version: '1.3'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: CFPropertyList
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -357,6 +357,9 @@ files:
|
|
357
357
|
- lib/bolt/util/puppet_log_level.rb
|
358
358
|
- lib/bolt/version.rb
|
359
359
|
- lib/bolt_ext/puppetdb_inventory.rb
|
360
|
+
- lib/bolt_ext/schemas/ssh-run_task.json
|
361
|
+
- lib/bolt_ext/schemas/task.json
|
362
|
+
- lib/bolt_ext/schemas/winrm-run_task.json
|
360
363
|
- lib/bolt_ext/server.rb
|
361
364
|
- lib/bolt_ext/server_acl.rb
|
362
365
|
- lib/bolt_ext/server_config.rb
|