bolt 1.24.0 → 1.25.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/catch_errors.rb +3 -2
- data/lib/bolt/inventory/group2.rb +38 -32
- data/lib/bolt/inventory/inventory2.rb +0 -1
- data/lib/bolt/outputter/human.rb +17 -14
- data/lib/bolt/plugin/pkcs7.rb +1 -1
- data/lib/bolt/plugin/prompt.rb +3 -3
- data/lib/bolt/plugin/puppetdb.rb +2 -2
- data/lib/bolt/plugin/terraform.rb +2 -2
- data/lib/bolt/secret/base.rb +3 -3
- data/lib/bolt/util.rb +2 -2
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/base_config.rb +14 -14
- data/lib/bolt_server/schemas/action-run_task.json +16 -0
- data/lib/bolt_server/schemas/partials/target-any.json +10 -0
- data/lib/bolt_server/schemas/partials/target-ssh.json +80 -0
- data/lib/bolt_server/schemas/partials/target-winrm.json +63 -0
- data/lib/bolt_server/schemas/{task.json → partials/task.json} +1 -1
- data/lib/bolt_server/schemas/transport-ssh.json +10 -0
- data/lib/bolt_server/schemas/transport-winrm.json +10 -0
- data/lib/bolt_server/transport_app.rb +44 -31
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +2 -1
- data/lib/bolt_spec/plans/action_stubs/task_stub.rb +2 -1
- metadata +9 -5
- data/lib/bolt_server/schemas/ssh-run_task.json +0 -79
- data/lib/bolt_server/schemas/winrm-run_task.json +0 -76
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22679235d7475d2cc80d765770e6b81b0ed0852d4563a889c00043fe61f359f1
|
4
|
+
data.tar.gz: a04c075cb1551ab90f6e6e9f0e6e02670b6081475a9d8f0d49fd9e7f046d0992
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4633d131919822b23981f8533b580e727817c95aa78a1afc05a4ca66bd14e08f3fd8f9c116f89e8726e171616c5cee1138d27260972dfc5e0718a9cb7a6096fb
|
7
|
+
data.tar.gz: 6a1f67bf10a65e26cd56cc9a82ad050a9023d5738b5d129cf43e203a50c1403c7cef4dd67085774dfba1d853f10174983b8e09716cc0a9d8441a24c69c62fe8b
|
@@ -8,8 +8,8 @@
|
|
8
8
|
Puppet::Functions.create_function(:catch_errors) do
|
9
9
|
# @param error_types An array of error types to catch
|
10
10
|
# @param block The block of steps to catch errors on
|
11
|
-
# @return
|
12
|
-
#
|
11
|
+
# @return If an error is raised in the block then the error will be returned,
|
12
|
+
# otherwise the result will be returned
|
13
13
|
# @example Catch errors for a block
|
14
14
|
# catch_errors() || {
|
15
15
|
# run_command("whoami", $nodes)
|
@@ -26,6 +26,7 @@ Puppet::Functions.create_function(:catch_errors) do
|
|
26
26
|
dispatch :catch_errors do
|
27
27
|
optional_param 'Array[String[1]]', :error_types
|
28
28
|
block_param 'Callable[0, 0]', :block
|
29
|
+
return_type 'Any'
|
29
30
|
end
|
30
31
|
|
31
32
|
def catch_errors(error_types = nil)
|
@@ -13,7 +13,7 @@ module Bolt
|
|
13
13
|
|
14
14
|
DATA_KEYS = %w[name config facts vars features].freeze
|
15
15
|
NODE_KEYS = DATA_KEYS + %w[alias uri]
|
16
|
-
GROUP_KEYS = DATA_KEYS + %w[groups targets
|
16
|
+
GROUP_KEYS = DATA_KEYS + %w[groups targets]
|
17
17
|
CONFIG_KEYS = Bolt::TRANSPORTS.keys.map(&:to_s) + ['transport']
|
18
18
|
|
19
19
|
def initialize(data, plugins)
|
@@ -31,6 +31,12 @@ module Bolt
|
|
31
31
|
raise ValidationError.new("Group name must be a String, not #{@name.inspect}", nil) unless @name.is_a?(String)
|
32
32
|
raise ValidationError.new("Invalid group name #{@name}", @name) unless @name =~ NAME_REGEX
|
33
33
|
|
34
|
+
# DEPRECATION : remove this before finalization
|
35
|
+
if data.key?('target-lookups')
|
36
|
+
msg = "'target-lookups' are no longer a separate key. Merge 'target-lookups' and 'targets' lists and replace 'plugin' with '_plugin'" # rubocop:disable Metrics/LineLength
|
37
|
+
raise ValidationError.new(msg, @name)
|
38
|
+
end
|
39
|
+
|
34
40
|
unless (unexpected_keys = data.keys - GROUP_KEYS).empty?
|
35
41
|
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in group #{@name}"
|
36
42
|
@logger.warn(msg)
|
@@ -42,8 +48,6 @@ module Bolt
|
|
42
48
|
|
43
49
|
@config = config_only_plugin(fetch_value(data, 'config', Hash))
|
44
50
|
|
45
|
-
@target_lookups = fetch_value(data, 'target-lookups', Array)
|
46
|
-
|
47
51
|
unless (unexpected_keys = @config.keys - CONFIG_KEYS).empty?
|
48
52
|
msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in config for group #{@name}"
|
49
53
|
@logger.warn(msg)
|
@@ -61,8 +65,15 @@ module Bolt
|
|
61
65
|
# resolved, and requires a depth-first traversal to categorize them.
|
62
66
|
if target.is_a?(String)
|
63
67
|
@name_or_alias << target
|
68
|
+
# Handle plugins at this level so that lookups cannot trigger recursive lookups
|
69
|
+
elsif target.is_a?(Hash)
|
70
|
+
if target.include?('_plugin')
|
71
|
+
lookup_targets(target)
|
72
|
+
else
|
73
|
+
add_target(target)
|
74
|
+
end
|
64
75
|
else
|
65
|
-
|
76
|
+
raise ValidationError.new("Node entry must be a String or Hash, not #{target.class}", @name)
|
66
77
|
end
|
67
78
|
end
|
68
79
|
|
@@ -77,12 +88,9 @@ module Bolt
|
|
77
88
|
raise ValidationError.new("Cannot set group #{key.inspect} with plugin", nil)
|
78
89
|
end
|
79
90
|
end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
84
|
-
elsif data.is_a? Array
|
85
|
-
data.map { |v| validate_config_plugin(v, key, group_name) }
|
91
|
+
|
92
|
+
Bolt::Util.walk_vals(data, true) do |v|
|
93
|
+
validate_config_plugin(v, key, group_name)
|
86
94
|
end
|
87
95
|
end
|
88
96
|
private :validate_config_plugin
|
@@ -91,11 +99,14 @@ module Bolt
|
|
91
99
|
Bolt::Util.walk_vals(data) do |value|
|
92
100
|
if value.is_a?(Hash) && value.include?('_plugin')
|
93
101
|
unless (plugin = @plugins.by_name(value['_plugin']))
|
94
|
-
raise ValidationError.new("
|
102
|
+
raise ValidationError.new("Config lookup specifies an unknown plugin: #{value['_plugin'].inspect}", @name)
|
95
103
|
end
|
96
|
-
|
104
|
+
unless plugin.hooks.include?('inventory_config')
|
105
|
+
raise ValidationError.new("#{plugin.name} does not support inventory_config.", @name)
|
106
|
+
end
|
107
|
+
plugin.validate_inventory_config(value) if plugin.respond_to?(:validate_inventory_config)
|
97
108
|
Concurrent::Delay.new do
|
98
|
-
plugin.
|
109
|
+
plugin.inventory_config(value)
|
99
110
|
end
|
100
111
|
else
|
101
112
|
value
|
@@ -119,11 +130,13 @@ module Bolt
|
|
119
130
|
end
|
120
131
|
|
121
132
|
def add_target(target)
|
122
|
-
#
|
123
|
-
#
|
133
|
+
# This check ensures target lookup plugins do not returns bare strings.
|
134
|
+
# Remove it if we decide to allows task plugins to return string node
|
135
|
+
# names.
|
124
136
|
unless target.is_a?(Hash)
|
125
|
-
raise ValidationError.new("Node entry must be a
|
137
|
+
raise ValidationError.new("Node entry must be a Hash, not #{target.class}", @name)
|
126
138
|
end
|
139
|
+
# This check prevents plugins from returning plugins
|
127
140
|
raise ValidationError.new("Cannot set target with plugin", @name) if target.key?('_plugin')
|
128
141
|
target.each do |k, v|
|
129
142
|
next if k == 'config'
|
@@ -160,6 +173,7 @@ module Bolt
|
|
160
173
|
end
|
161
174
|
|
162
175
|
target['config'] = config_only_plugin(target['config'])
|
176
|
+
|
163
177
|
unless target.include?('alias')
|
164
178
|
return
|
165
179
|
end
|
@@ -181,24 +195,16 @@ module Bolt
|
|
181
195
|
end
|
182
196
|
end
|
183
197
|
|
184
|
-
def lookup_targets(
|
185
|
-
@
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
raise ValidationError.new("target-lookup does not specify a plugin: #{lookup}", @name)
|
191
|
-
end
|
192
|
-
|
193
|
-
unless (plugin = plugins.by_name(lookup['plugin']))
|
194
|
-
raise ValidationError.new("target-lookup specifies an unkown plugin: '#{lookup['plugin']}'", @name)
|
195
|
-
end
|
196
|
-
|
197
|
-
targets = plugin.lookup_targets(lookup)
|
198
|
-
targets.each { |target| add_target(target) }
|
198
|
+
def lookup_targets(lookup)
|
199
|
+
unless (plugin = @plugins.by_name(lookup['_plugin']))
|
200
|
+
raise ValidationError.new("Target lookup specifies an unknown plugin: '#{lookup['plugin']}'", @name)
|
201
|
+
end
|
202
|
+
unless plugin.hooks.include?('inventory_targets')
|
203
|
+
raise ValidationError.new("#{plugin.name} does not support inventory_targets.", @name)
|
199
204
|
end
|
200
205
|
|
201
|
-
|
206
|
+
targets = plugin.inventory_targets(lookup)
|
207
|
+
targets.each { |target| add_target(target) }
|
202
208
|
end
|
203
209
|
|
204
210
|
def data_merge(data1, data2)
|
data/lib/bolt/outputter/human.rb
CHANGED
@@ -37,21 +37,7 @@ module Bolt
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def handle_event(event)
|
40
|
-
return unless enabled? || event[:type] == :enable_default_output
|
41
|
-
|
42
40
|
case event[:type]
|
43
|
-
when :node_start
|
44
|
-
print_start(event[:target]) if @verbose
|
45
|
-
when :node_result
|
46
|
-
print_result(event[:result]) if @verbose
|
47
|
-
when :step_start
|
48
|
-
print_step_start(event) if plan_logging?
|
49
|
-
when :step_finish
|
50
|
-
print_step_finish(event) if plan_logging?
|
51
|
-
when :plan_start
|
52
|
-
print_plan_start(event)
|
53
|
-
when :plan_finish
|
54
|
-
print_plan_finish(event)
|
55
41
|
when :enable_default_output
|
56
42
|
@disable_depth -= 1
|
57
43
|
when :disable_default_output
|
@@ -59,6 +45,23 @@ module Bolt
|
|
59
45
|
when :message
|
60
46
|
print_message_event(event)
|
61
47
|
end
|
48
|
+
|
49
|
+
if enabled?
|
50
|
+
case event[:type]
|
51
|
+
when :node_start
|
52
|
+
print_start(event[:target]) if @verbose
|
53
|
+
when :node_result
|
54
|
+
print_result(event[:result]) if @verbose
|
55
|
+
when :step_start
|
56
|
+
print_step_start(event) if plan_logging?
|
57
|
+
when :step_finish
|
58
|
+
print_step_finish(event) if plan_logging?
|
59
|
+
when :plan_start
|
60
|
+
print_plan_start(event)
|
61
|
+
when :plan_finish
|
62
|
+
print_plan_finish(event)
|
63
|
+
end
|
64
|
+
end
|
62
65
|
end
|
63
66
|
|
64
67
|
def enabled?
|
data/lib/bolt/plugin/pkcs7.rb
CHANGED
@@ -7,7 +7,7 @@ module Bolt
|
|
7
7
|
class Plugin
|
8
8
|
class Pkcs7 < Bolt::Secret::Base
|
9
9
|
def self.validate_config(config)
|
10
|
-
known_keys = [
|
10
|
+
known_keys = %w[private-key public-key keysize]
|
11
11
|
known_keys.each do |key|
|
12
12
|
unless key.is_a? String
|
13
13
|
raise Bolt::ValidationError, "Invalid config for pkcs7 plugin: '#{key}' is not a String"
|
data/lib/bolt/plugin/prompt.rb
CHANGED
@@ -14,14 +14,14 @@ module Bolt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def hooks
|
17
|
-
['
|
17
|
+
['inventory_config']
|
18
18
|
end
|
19
19
|
|
20
|
-
def
|
20
|
+
def validate_inventory_config(opts)
|
21
21
|
raise Bolt::ValidationError, "Prompt requires a 'message'" unless opts['message']
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def inventory_config(opts)
|
25
25
|
STDOUT.print "#{opts['message']}:"
|
26
26
|
value = STDIN.noecho(&:gets).chomp
|
27
27
|
STDOUT.puts
|
data/lib/bolt/plugin/puppetdb.rb
CHANGED
@@ -23,7 +23,7 @@ module Bolt
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def hooks
|
26
|
-
['
|
26
|
+
['inventory_targets']
|
27
27
|
end
|
28
28
|
|
29
29
|
def warn_missing_fact(certname, fact)
|
@@ -41,7 +41,7 @@ module Bolt
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
44
|
+
def inventory_targets(opts)
|
45
45
|
targets = @puppetdb_client.query_certnames(opts['query'])
|
46
46
|
facts = []
|
47
47
|
|
@@ -14,14 +14,14 @@ module Bolt
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def hooks
|
17
|
-
['
|
17
|
+
['inventory_targets']
|
18
18
|
end
|
19
19
|
|
20
20
|
def warn_missing_property(name, property)
|
21
21
|
@logger.warn("Could not find property #{property} of terraform resource #{name}")
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def inventory_targets(opts)
|
25
25
|
state = load_statefile(opts)
|
26
26
|
|
27
27
|
resources = extract_resources(state)
|
data/lib/bolt/secret/base.rb
CHANGED
@@ -4,7 +4,7 @@ module Bolt
|
|
4
4
|
class Secret
|
5
5
|
class Base
|
6
6
|
def hooks
|
7
|
-
%w[
|
7
|
+
%w[inventory_config secret_encrypt secret_decrypt secret_createkeys]
|
8
8
|
end
|
9
9
|
|
10
10
|
def encode(raw)
|
@@ -31,9 +31,9 @@ module Bolt
|
|
31
31
|
raw, _plugin = decode(opts['encrypted-value'])
|
32
32
|
decrypt_value(raw)
|
33
33
|
end
|
34
|
-
alias
|
34
|
+
alias inventory_config secret_decrypt
|
35
35
|
|
36
|
-
def
|
36
|
+
def validate_inventory_config(opts)
|
37
37
|
decode(opts['encrypted-value'])
|
38
38
|
end
|
39
39
|
end
|
data/lib/bolt/util.rb
CHANGED
@@ -134,8 +134,8 @@ module Bolt
|
|
134
134
|
# Accepts a Data object and returns a copy with all hash and array values
|
135
135
|
# Arrays and hashes including the initial object are modified before
|
136
136
|
# their descendants are.
|
137
|
-
def walk_vals(data, &block)
|
138
|
-
data = yield(data)
|
137
|
+
def walk_vals(data, skip_top = false, &block)
|
138
|
+
data = yield(data) unless skip_top
|
139
139
|
if data.is_a? Hash
|
140
140
|
map_vals(data) { |v| walk_vals(v, &block) }
|
141
141
|
elsif data.is_a? Array
|
data/lib/bolt/version.rb
CHANGED
@@ -6,31 +6,31 @@ require 'bolt/error'
|
|
6
6
|
module BoltServer
|
7
7
|
class BaseConfig
|
8
8
|
def config_keys
|
9
|
-
[
|
10
|
-
|
9
|
+
%w[host port ssl-cert ssl-key ssl-ca-cert
|
10
|
+
ssl-cipher-suites loglevel logfile whitelist]
|
11
11
|
end
|
12
12
|
|
13
13
|
def env_keys
|
14
|
-
[
|
14
|
+
%w[ssl-cert ssl-key ssl-ca-cert loglevel]
|
15
15
|
end
|
16
16
|
|
17
17
|
def defaults
|
18
18
|
{ 'host' => '127.0.0.1',
|
19
19
|
'loglevel' => 'notice',
|
20
|
-
'ssl-cipher-suites' => [
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
'ssl-cipher-suites' => %w[ECDHE-ECDSA-AES256-GCM-SHA384
|
21
|
+
ECDHE-RSA-AES256-GCM-SHA384
|
22
|
+
ECDHE-ECDSA-CHACHA20-POLY1305
|
23
|
+
ECDHE-RSA-CHACHA20-POLY1305
|
24
|
+
ECDHE-ECDSA-AES128-GCM-SHA256
|
25
|
+
ECDHE-RSA-AES128-GCM-SHA256
|
26
|
+
ECDHE-ECDSA-AES256-SHA384
|
27
|
+
ECDHE-RSA-AES256-SHA384
|
28
|
+
ECDHE-ECDSA-AES128-SHA256
|
29
|
+
ECDHE-RSA-AES128-SHA256] }
|
30
30
|
end
|
31
31
|
|
32
32
|
def ssl_keys
|
33
|
-
[
|
33
|
+
%w[ssl-cert ssl-key ssl-ca-cert]
|
34
34
|
end
|
35
35
|
|
36
36
|
def required_keys
|
@@ -0,0 +1,16 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "run_task request",
|
4
|
+
"description": "POST <transport>/run_task request schema",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"task": { "$ref": "partial:task" },
|
8
|
+
"parameters": {
|
9
|
+
"type": "object",
|
10
|
+
"description": "JSON formatted parameters to be provided to task"
|
11
|
+
},
|
12
|
+
"target": { "$ref": "partial:target-any" }
|
13
|
+
},
|
14
|
+
"required": ["target", "task"],
|
15
|
+
"additionalProperties": false
|
16
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"id": "partial:target-any",
|
3
|
+
"$schema": "http://json-schema.org/draft-04/schema",
|
4
|
+
"title": "Target information about where to run a bolt action, either over SSH or WinRM",
|
5
|
+
"type": "object",
|
6
|
+
"anyOf": [
|
7
|
+
{ "$ref": "partial:target-ssh" },
|
8
|
+
{ "$ref": "partial:target-winrm" }
|
9
|
+
]
|
10
|
+
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
{
|
2
|
+
"id": "partial:target-ssh",
|
3
|
+
"$schema": "http://json-schema.org/draft-04/schema",
|
4
|
+
"title": "Target information about where to run a bolt action over SSH",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"hostname": {
|
8
|
+
"type": "string",
|
9
|
+
"description": "Target identifier"
|
10
|
+
},
|
11
|
+
"user": {
|
12
|
+
"type": "string",
|
13
|
+
"description": "Login user"
|
14
|
+
},
|
15
|
+
"password": {
|
16
|
+
"type": "string",
|
17
|
+
"description": "Password for SSH transport authentication"
|
18
|
+
},
|
19
|
+
"private-key-content": {
|
20
|
+
"type": "string",
|
21
|
+
"description": "Contents of private key for SSH"
|
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
|
+
"run-as-command": {
|
32
|
+
"type": "array",
|
33
|
+
"description": "Command elevate permissions",
|
34
|
+
"items": {
|
35
|
+
"type": "string"
|
36
|
+
}
|
37
|
+
},
|
38
|
+
"run-as": {
|
39
|
+
"type": "string",
|
40
|
+
"description": "A different user to run commands as after login"
|
41
|
+
},
|
42
|
+
"tmpdir": {
|
43
|
+
"type": "string",
|
44
|
+
"description": "The directory to upload and execute temporary files on the target"
|
45
|
+
},
|
46
|
+
"tty": {
|
47
|
+
"type": "boolean",
|
48
|
+
"description": "Should bolt use pseudo tty to meet sudoer restrictions"
|
49
|
+
},
|
50
|
+
"host-key-check": {
|
51
|
+
"type": "boolean",
|
52
|
+
"description": "Whether to perform host key validation when connecting over SSH"
|
53
|
+
},
|
54
|
+
"sudo-password": {
|
55
|
+
"type": "string",
|
56
|
+
"description": "Password to use when changing users via run-as"
|
57
|
+
},
|
58
|
+
"interpreters": {
|
59
|
+
"type": "object",
|
60
|
+
"description": "Map of file extensions to remote executable"
|
61
|
+
}
|
62
|
+
},
|
63
|
+
"oneOf": [
|
64
|
+
{
|
65
|
+
"required": [
|
66
|
+
"password"
|
67
|
+
]
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"required": [
|
71
|
+
"private-key-content"
|
72
|
+
]
|
73
|
+
}
|
74
|
+
],
|
75
|
+
"required": [
|
76
|
+
"hostname",
|
77
|
+
"user"
|
78
|
+
],
|
79
|
+
"additionalProperties": false
|
80
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
{
|
2
|
+
"id": "partial:target-winrm",
|
3
|
+
"$schema": "http://json-schema.org/draft-04/schema",
|
4
|
+
"title": "Target information about where to run a bolt action over WinRM",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"hostname": {
|
8
|
+
"type": "string",
|
9
|
+
"description": "Target identifier"
|
10
|
+
},
|
11
|
+
"user": {
|
12
|
+
"type": "string",
|
13
|
+
"description": "Login user"
|
14
|
+
},
|
15
|
+
"password": {
|
16
|
+
"type": "string",
|
17
|
+
"description": "Password for WinRM transport authentication"
|
18
|
+
},
|
19
|
+
"port": {
|
20
|
+
"type": "integer",
|
21
|
+
"description": "Connection port"
|
22
|
+
},
|
23
|
+
"connect-timeout": {
|
24
|
+
"type": "integer",
|
25
|
+
"description": "How long Bolt should wait when establishing connections"
|
26
|
+
},
|
27
|
+
"tmpdir": {
|
28
|
+
"type": "string",
|
29
|
+
"description": "The directory to upload and execute temporary files on the target"
|
30
|
+
},
|
31
|
+
"ssl": {
|
32
|
+
"type": "boolean",
|
33
|
+
"description": "When true, Bolt will use https connections for WinRM"
|
34
|
+
},
|
35
|
+
"ssl-verify": {
|
36
|
+
"type": "boolean",
|
37
|
+
"description": "When true, verifies the targets certificate matches the cacert"
|
38
|
+
},
|
39
|
+
"cacert": {
|
40
|
+
"type": "string",
|
41
|
+
"description": "The path to the CA certificate"
|
42
|
+
},
|
43
|
+
"extensions": {
|
44
|
+
"type": "array",
|
45
|
+
"description": "List of file extensions that are accepted for scripts or tasks"
|
46
|
+
},
|
47
|
+
"interpreters": {
|
48
|
+
"type": "object",
|
49
|
+
"description": "Map of file extensions to remote executable"
|
50
|
+
},
|
51
|
+
"file-protocol": {
|
52
|
+
"type": "string",
|
53
|
+
"enum": ["winrm", "smb"],
|
54
|
+
"description": "Protocol for file transfer, WinRM or SMB"
|
55
|
+
},
|
56
|
+
"smb-port": {
|
57
|
+
"type": "integer",
|
58
|
+
"description": "Port for SMB protocol"
|
59
|
+
}
|
60
|
+
},
|
61
|
+
"required": ["hostname", "user", "password"],
|
62
|
+
"additionalProperties": false
|
63
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema",
|
3
|
+
"title": "Action request with SSH transport",
|
4
|
+
"description": "POST /ssh/<action> request schema",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"target": { "$ref": "partial:target-ssh" }
|
8
|
+
},
|
9
|
+
"required": ["target"]
|
10
|
+
}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema",
|
3
|
+
"title": "Action request with WinRM transport",
|
4
|
+
"description": "POST /winrm/<action> request schema",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"target": { "$ref": "partial:target-winrm" }
|
8
|
+
},
|
9
|
+
"required": ["target"]
|
10
|
+
}
|
@@ -15,15 +15,23 @@ module BoltServer
|
|
15
15
|
# This disables Sinatra's error page generation
|
16
16
|
set :show_exceptions, false
|
17
17
|
|
18
|
+
# These partial schemas are reused to build multiple request schemas
|
19
|
+
PARTIAL_SCHEMAS = %w[target-any target-ssh target-winrm task].freeze
|
20
|
+
|
21
|
+
# These schemas combine shared schemas to describe client requests
|
22
|
+
REQUEST_SCHEMAS = %w[action-run_task transport-ssh transport-winrm].freeze
|
23
|
+
|
18
24
|
def initialize(config)
|
19
25
|
@config = config
|
20
|
-
@schemas =
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
26
|
+
@schemas = Hash[REQUEST_SCHEMAS.map do |basename|
|
27
|
+
[basename, JSON.parse(File.read(File.join(__dir__, ['schemas', "#{basename}.json"])))]
|
28
|
+
end]
|
29
|
+
|
30
|
+
PARTIAL_SCHEMAS.each do |basename|
|
31
|
+
schema_content = JSON.parse(File.read(File.join(__dir__, ['schemas', 'partials', "#{basename}.json"])))
|
32
|
+
shared_schema = JSON::Schema.new(schema_content, Addressable::URI.parse("partial:#{basename}"))
|
33
|
+
JSON::Validator.add_schema(shared_schema)
|
34
|
+
end
|
27
35
|
|
28
36
|
@executor = Bolt::Executor.new(0)
|
29
37
|
|
@@ -48,6 +56,19 @@ module BoltServer
|
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
59
|
+
def run_task(target, body)
|
60
|
+
error = validate_schema(@schemas["action-run_task"], body)
|
61
|
+
return [400, error.to_json] unless error.nil?
|
62
|
+
|
63
|
+
task = Bolt::Task::PuppetServer.new(body['task'], @file_cache)
|
64
|
+
parameters = body['parameters'] || {}
|
65
|
+
results = @executor.run_task(target, task, parameters)
|
66
|
+
|
67
|
+
# Since this will only be on one node we can just return the first result
|
68
|
+
result = scrub_stack_trace(results.first.status_hash)
|
69
|
+
[200, result.to_json]
|
70
|
+
end
|
71
|
+
|
51
72
|
get '/' do
|
52
73
|
200
|
53
74
|
end
|
@@ -67,14 +88,18 @@ module BoltServer
|
|
67
88
|
raise 'Unexpected error'
|
68
89
|
end
|
69
90
|
|
70
|
-
|
71
|
-
content_type :json
|
91
|
+
ACTIONS = %w[run_task].freeze
|
72
92
|
|
93
|
+
post '/ssh/:action' do
|
94
|
+
not_found unless ACTIONS.include?(params[:action])
|
95
|
+
|
96
|
+
content_type :json
|
73
97
|
body = JSON.parse(request.body.read)
|
74
|
-
|
98
|
+
|
99
|
+
error = validate_schema(@schemas["transport-ssh"], body)
|
75
100
|
return [400, error.to_json] unless error.nil?
|
76
101
|
|
77
|
-
opts = body['target']
|
102
|
+
opts = body['target'].clone
|
78
103
|
if opts['private-key-content']
|
79
104
|
opts['private-key'] = { 'key-data' => opts['private-key-content'] }
|
80
105
|
opts.delete('private-key-content')
|
@@ -83,35 +108,23 @@ module BoltServer
|
|
83
108
|
|
84
109
|
target = [Bolt::Target.new(body['target']['hostname'], opts)]
|
85
110
|
|
86
|
-
|
87
|
-
|
88
|
-
parameters = body['parameters'] || {}
|
89
|
-
|
90
|
-
# Since this will only be on one node we can just return the first result
|
91
|
-
results = @executor.run_task(target, task, parameters)
|
92
|
-
result = scrub_stack_trace(results.first.status_hash)
|
93
|
-
[200, result.to_json]
|
111
|
+
method(params[:action]).call(target, body)
|
94
112
|
end
|
95
113
|
|
96
|
-
post '/winrm
|
97
|
-
|
114
|
+
post '/winrm/:action' do
|
115
|
+
not_found unless ACTIONS.include?(params[:action])
|
98
116
|
|
117
|
+
content_type :json
|
99
118
|
body = JSON.parse(request.body.read)
|
100
|
-
|
119
|
+
|
120
|
+
error = validate_schema(@schemas["transport-winrm"], body)
|
101
121
|
return [400, error.to_json] unless error.nil?
|
102
122
|
|
103
|
-
opts = body['target'].merge('protocol' => 'winrm')
|
123
|
+
opts = body['target'].clone.merge('protocol' => 'winrm')
|
104
124
|
|
105
125
|
target = [Bolt::Target.new(body['target']['hostname'], opts)]
|
106
126
|
|
107
|
-
|
108
|
-
|
109
|
-
parameters = body['parameters'] || {}
|
110
|
-
|
111
|
-
# Since this will only be on one node we can just return the first result
|
112
|
-
results = @executor.run_task(target, task, parameters)
|
113
|
-
result = scrub_stack_trace(results.first.status_hash)
|
114
|
-
[200, result.to_json]
|
127
|
+
method(params[:action]).call(target, body)
|
115
128
|
end
|
116
129
|
|
117
130
|
error 404 do
|
@@ -31,7 +31,7 @@ module BoltSpec
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def parameters
|
34
|
-
@invocation[:
|
34
|
+
@invocation[:params]
|
35
35
|
end
|
36
36
|
|
37
37
|
def result_for(target, stdout: '', stderr: '')
|
@@ -41,6 +41,7 @@ module BoltSpec
|
|
41
41
|
# Public methods
|
42
42
|
|
43
43
|
def with_params(params)
|
44
|
+
@invocation[:params] = params
|
44
45
|
@invocation[:arguments] = params['arguments']
|
45
46
|
@invocation[:options] = params.select { |k, _v| k.start_with?('_') }
|
46
47
|
self
|
@@ -30,7 +30,7 @@ module BoltSpec
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def parameters
|
33
|
-
@invocation[:
|
33
|
+
@invocation[:params]
|
34
34
|
end
|
35
35
|
|
36
36
|
# Allow any data.
|
@@ -43,6 +43,7 @@ module BoltSpec
|
|
43
43
|
# Restricts the stub to only match invocations with certain parameters.
|
44
44
|
# All parameters must match exactly.
|
45
45
|
def with_params(params)
|
46
|
+
@invocation[:params] = params
|
46
47
|
@invocation[:arguments] = params.reject { |k, _v| k.start_with?('_') }
|
47
48
|
@invocation[:options] = params.select { |k, _v| k.start_with?('_') }
|
48
49
|
self
|
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.25.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-06-
|
11
|
+
date: 2019-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -430,9 +430,13 @@ files:
|
|
430
430
|
- lib/bolt_server/base_config.rb
|
431
431
|
- lib/bolt_server/config.rb
|
432
432
|
- lib/bolt_server/file_cache.rb
|
433
|
-
- lib/bolt_server/schemas/
|
434
|
-
- lib/bolt_server/schemas/
|
435
|
-
- lib/bolt_server/schemas/
|
433
|
+
- lib/bolt_server/schemas/action-run_task.json
|
434
|
+
- lib/bolt_server/schemas/partials/target-any.json
|
435
|
+
- lib/bolt_server/schemas/partials/target-ssh.json
|
436
|
+
- lib/bolt_server/schemas/partials/target-winrm.json
|
437
|
+
- lib/bolt_server/schemas/partials/task.json
|
438
|
+
- lib/bolt_server/schemas/transport-ssh.json
|
439
|
+
- lib/bolt_server/schemas/transport-winrm.json
|
436
440
|
- lib/bolt_server/transport_app.rb
|
437
441
|
- lib/bolt_spec/plans.rb
|
438
442
|
- lib/bolt_spec/plans/action_stubs.rb
|
@@ -1,79 +0,0 @@
|
|
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
|
-
"tty": {
|
49
|
-
"type": "boolean",
|
50
|
-
"description": "Should bolt use pseudo tty to meet sudoer restrictions"
|
51
|
-
},
|
52
|
-
"host-key-check": {
|
53
|
-
"type": "boolean",
|
54
|
-
"description": "Whether to perform host key validation when connecting over SSH"
|
55
|
-
},
|
56
|
-
"sudo-password": {
|
57
|
-
"type": "string",
|
58
|
-
"description": "Password to use when changing users via run-as"
|
59
|
-
},
|
60
|
-
"interpreters": {
|
61
|
-
"type": "object",
|
62
|
-
"description": "Map of file extensions to remote executable"
|
63
|
-
}
|
64
|
-
},
|
65
|
-
"oneOf": [
|
66
|
-
{ "required": ["password"] },
|
67
|
-
{ "required": ["private-key-content"] }
|
68
|
-
],
|
69
|
-
"required": ["hostname", "user"],
|
70
|
-
"additionalProperties": false
|
71
|
-
},
|
72
|
-
"task": { "$ref": "file:task"},
|
73
|
-
"parameters": {
|
74
|
-
"type": "object",
|
75
|
-
"description": "JSON formatted parameters to be provided to task"
|
76
|
-
}
|
77
|
-
},
|
78
|
-
"required": ["target", "task"]
|
79
|
-
}
|
@@ -1,76 +0,0 @@
|
|
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
|
-
"interpreters": {
|
52
|
-
"type": "object",
|
53
|
-
"description": "Map of file extensions to remote executable"
|
54
|
-
},
|
55
|
-
"file-protocol": {
|
56
|
-
"type": "string",
|
57
|
-
"enum": ["winrm", "smb"],
|
58
|
-
"description": "Protocol for file transfer, WinRM or SMB"
|
59
|
-
},
|
60
|
-
"smb-port": {
|
61
|
-
"type": "integer",
|
62
|
-
"description": "Port for SMB protocol"
|
63
|
-
}
|
64
|
-
},
|
65
|
-
|
66
|
-
"required": ["hostname", "user", "password"],
|
67
|
-
"additionalProperties": false
|
68
|
-
},
|
69
|
-
"task": { "$ref": "file:task"},
|
70
|
-
"parameters": {
|
71
|
-
"type": "object",
|
72
|
-
"description": "JSON formatted parameters to be provided to task"
|
73
|
-
}
|
74
|
-
},
|
75
|
-
"required": ["target", "task"]
|
76
|
-
}
|