bolt 1.30.0 → 1.30.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f863b5a29cc2286bc59ed3e987545be4e692c3b17e026ea33b4529625677c086
4
- data.tar.gz: 65a2a7eff22001527871c72fcda6a65eeb6f8a943e1c043c232e1bd873dc9c27
3
+ metadata.gz: b5c70a9312d9fa6f384b140fec6cbc3c173906e9b428e0d3b19faaab91cf6590
4
+ data.tar.gz: 10fe1a0b13b4bd489cd41a9b3971d6e5a8f797fced7ddd1faeaf33c09c9f3e28
5
5
  SHA512:
6
- metadata.gz: 1a0c09806f427b335ed26848072de21ae37ac53d196329dd5f8bcb5e1458460fc59804272a35336c9fc3196067bd74642baa9ac3a50be884eda99aba646c008b
7
- data.tar.gz: d0dbfa6c28ab4a7c0f753e91c99ee0bf4379fe9e1843c7124d52555ea4fdc5ed9c7c46d6783206cb1b518f9e463dbe6d693c115c79c261ec7ae0adda99bab0b8
6
+ metadata.gz: 26f36949b058636604697ba4804e1aeb3068c94dd49890c19f336bb9afabe3256f5383661e876881d51b95c667d6c9f4f6bf149566d9d06e54c21456a6de1301
7
+ data.tar.gz: cc48faf53683fac36548175e42466a80330580942b48f3bdfd2fae060e1e91eef67b289c1e751e58bde2be3ab459bd52a5a2460e3c108468b77fc82ff4c37800
@@ -28,7 +28,7 @@ Puppet::Functions.create_function(:apply_prep) do
28
28
  end
29
29
 
30
30
  def inventory
31
- Puppet.lookup(:bolt_inventory)
31
+ @inventory ||= Puppet.lookup(:bolt_inventory)
32
32
  end
33
33
 
34
34
  def get_task(name, params = {})
@@ -61,7 +61,7 @@ Puppet::Functions.create_function(:apply_prep) do
61
61
  end
62
62
 
63
63
  def executor
64
- Puppet.lookup(:bolt_executor)
64
+ @executor ||= Puppet.lookup(:bolt_executor)
65
65
  end
66
66
 
67
67
  def apply_prep(target_spec)
@@ -195,6 +195,7 @@ module Bolt
195
195
  arguments = {
196
196
  'catalog' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(catalog),
197
197
  'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins),
198
+ '_task' => catalog_apply_task.name,
198
199
  '_noop' => options['_noop']
199
200
  }
200
201
 
@@ -204,6 +205,8 @@ module Bolt
204
205
  end
205
206
  @executor.publish_event(event)
206
207
  end
208
+ # Respect the run_as default set on the executor
209
+ options = { '_run_as' => @executor.run_as }.merge(options) if @executor.run_as
207
210
  results = transport.batch_task(batch, catalog_apply_task, arguments, options, &callback)
208
211
  Array(results).map { |result| ApplyResult.from_task_result(result) }
209
212
  end
@@ -193,7 +193,7 @@ PROCESS{
193
193
  return $xml.Objects | ConvertFrom-Xml
194
194
  }
195
195
  $propbag = @{}
196
- foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^__|type"} | Select-Object -ExpandProperty name) {
196
+ foreach ($name in Get-Member -InputObject $xml -MemberType Properties | Where-Object{$_.Name -notmatch "^(__.*|type)$"} | Select-Object -ExpandProperty name) {
197
197
  Write-Debug "$Name Type: $($xml.$Name.type)" -Debug:$false
198
198
  $propbag."$Name" = Convert-Properties $xml."$name"
199
199
  }
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '1.30.0'
4
+ VERSION = '1.30.1'
5
5
  end
@@ -83,7 +83,7 @@ module BoltServer
83
83
  begin
84
84
  client.request(req) do |resp|
85
85
  if resp.code != "200"
86
- msg = "Failed to download task: #{resp.body}"
86
+ msg = "Failed to download file: #{resp.body}"
87
87
  @logger.warn resp.body
88
88
  raise Error, msg
89
89
  end
@@ -96,7 +96,7 @@ module BoltServer
96
96
  raise e
97
97
  else
98
98
  @logger.warn e
99
- raise Error, "Failed to download task: #{e.message}"
99
+ raise Error, "Failed to download file: #{e.message}"
100
100
  end
101
101
  end
102
102
  ensure
@@ -157,7 +157,7 @@ module BoltServer
157
157
  file_dir = create_cache_dir(file_data['sha256'])
158
158
  file_path = File.join(file_dir, File.basename(file_data['filename']))
159
159
  if check_file(file_path, sha)
160
- @logger.debug("Using prexisting task file: #{file_path}")
160
+ @logger.debug("Using prexisting file: #{file_path}")
161
161
  return file_path
162
162
  end
163
163
 
@@ -0,0 +1,14 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "check_node_connections request",
4
+ "description": "POST <transport>/check_node_connections request schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "targets": {
8
+ "type": "array",
9
+ "items": { "$ref": "partial:target-any" }
10
+ }
11
+ },
12
+ "required": ["targets"],
13
+ "additionalProperties": false
14
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "upload_file request",
4
+ "description": "POST <transport>/upload_file request schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "files": {
8
+ "type": "array",
9
+ "items": {
10
+ "type": "object",
11
+ "properties": {
12
+ "relative_path": {
13
+ "type": "string"
14
+ },
15
+ "uri": {
16
+ "type": "object",
17
+ "properties": {
18
+ "path": {
19
+ "type": "string"
20
+ },
21
+ "params": {
22
+ "type": "object"
23
+ }
24
+ },
25
+ "required": ["path", "params"]
26
+ },
27
+ "sha256": {
28
+ "type": "string"
29
+ },
30
+ "kind": {
31
+ "type": "string"
32
+ }
33
+ },
34
+ "required": ["relative_path", "uri", "sha256", "kind"]
35
+ }
36
+ },
37
+ "job_id": {
38
+ "type": "integer"
39
+ },
40
+ "destination": {
41
+ "type": "string"
42
+ },
43
+ "target": { "$ref": "partial:target-any" }
44
+ },
45
+ "required": ["files", "job_id", "destination", "target"],
46
+ "additionalProperties": false
47
+ }
@@ -4,7 +4,22 @@
4
4
  "description": "POST /ssh/<action> request schema",
5
5
  "type": "object",
6
6
  "properties": {
7
- "target": { "$ref": "partial:target-ssh" }
7
+ "target": { "$ref": "partial:target-ssh" },
8
+ "targets": {
9
+ "type": "array",
10
+ "items": {
11
+ "$ref": "partial:target-ssh"
12
+ }
13
+ }
8
14
  },
9
- "required": ["target"]
15
+ "oneOf": [
16
+ {
17
+ "type": "object",
18
+ "required": ["target"]
19
+ },
20
+ {
21
+ "type": "object",
22
+ "required": ["targets"]
23
+ }
24
+ ]
10
25
  }
@@ -4,7 +4,16 @@
4
4
  "description": "POST /winrm/<action> request schema",
5
5
  "type": "object",
6
6
  "properties": {
7
- "target": { "$ref": "partial:target-winrm" }
7
+ "target": { "$ref": "partial:target-winrm" },
8
+ "targets": {
9
+ "type": "array",
10
+ "items": {
11
+ "$ref": "partial:target-winrm"
12
+ }
13
+ }
8
14
  },
9
- "required": ["target"]
15
+ "oneOf": [
16
+ { "required": ["target"] },
17
+ { "required": ["targets"] }
18
+ ]
10
19
  }
@@ -19,7 +19,14 @@ module BoltServer
19
19
  PARTIAL_SCHEMAS = %w[target-any target-ssh target-winrm task].freeze
20
20
 
21
21
  # These schemas combine shared schemas to describe client requests
22
- REQUEST_SCHEMAS = %w[action-run_task action-run_command transport-ssh transport-winrm].freeze
22
+ REQUEST_SCHEMAS = %w[
23
+ action-check_node_connections
24
+ action-run_command
25
+ action-run_task
26
+ action-upload_file
27
+ transport-ssh
28
+ transport-winrm
29
+ ].freeze
23
30
 
24
31
  def initialize(config)
25
32
  @config = config
@@ -56,21 +63,92 @@ module BoltServer
56
63
  end
57
64
  end
58
65
 
66
+ # Turns a Bolt::ResultSet object into a status hash that is fit
67
+ # to return to the client in a response.
68
+ #
69
+ # If the `result_set` has more than one result, the status hash
70
+ # will have a `status` value and a list of target `results`.
71
+ # If the `result_set` contains only one item, it will be returned
72
+ # as a single result object. Set `aggregate` to treat it as a set
73
+ # of results with length 1 instead.
74
+ def result_set_to_status_hash(result_set, aggregate: false)
75
+ scrubbed_results = result_set.map do |result|
76
+ scrub_stack_trace(result.status_hash)
77
+ end
78
+
79
+ if aggregate || scrubbed_results.length > 1
80
+ # For actions that act on multiple targets, construct a status hash for the aggregate result
81
+ all_succeeded = scrubbed_results.all? { |r| r[:status] == 'success' }
82
+ {
83
+ status: all_succeeded ? 'success' : 'failure',
84
+ result: scrubbed_results
85
+ }
86
+ else
87
+ # If there was only one target, return the first result on its own
88
+ scrubbed_results.first
89
+ end
90
+ end
91
+
59
92
  def run_task(target, body)
60
93
  error = validate_schema(@schemas["action-run_task"], body)
61
- return [400, error.to_json] unless error.nil?
94
+ return [], error unless error.nil?
62
95
 
63
96
  task = Bolt::Task::PuppetServer.new(body['task'], @file_cache)
64
97
  parameters = body['parameters'] || {}
65
- @executor.run_task(target, task, parameters)
98
+ [@executor.run_task(target, task, parameters), nil]
66
99
  end
67
100
 
68
101
  def run_command(target, body)
69
102
  error = validate_schema(@schemas["action-run_command"], body)
70
- return [400, error.to_json] unless error.nil?
103
+ return [], error unless error.nil?
71
104
 
72
105
  command = body['command']
73
- @executor.run_command(target, command)
106
+ [@executor.run_command(target, command), nil]
107
+ end
108
+
109
+ def check_node_connections(targets, body)
110
+ error = validate_schema(@schemas["action-check_node_connections"], body)
111
+ return [], error unless error.nil?
112
+
113
+ # Puppet Enterprise's orchestrator service uses the
114
+ # check_node_connections endpoint to check whether nodes that should be
115
+ # contacted over SSH or WinRM are responsive. The wait time here is 0
116
+ # because the endpoint is meant to be used for a single check of all
117
+ # nodes; External implementations of wait_until_available (like
118
+ # orchestrator's) should contact the endpoint in their own loop.
119
+ [@executor.wait_until_available(targets, wait_time: 0), nil]
120
+ end
121
+
122
+ def upload_file(target, body)
123
+ error = validate_schema(@schemas["action-upload_file"], body)
124
+ return [], error unless error.nil?
125
+
126
+ files = body['files']
127
+ destination = body['destination']
128
+ job_id = body['job_id']
129
+ cache_dir = @file_cache.create_cache_dir(job_id.to_s)
130
+ FileUtils.mkdir_p(cache_dir)
131
+ files.each do |file|
132
+ relative_path = file['relative_path']
133
+ uri = file['uri']
134
+ sha256 = file['sha256']
135
+ kind = file['kind']
136
+ path = File.join(cache_dir, relative_path)
137
+ if kind == 'file'
138
+ # The parent should already be created by `directory` entries,
139
+ # but this is to be on the safe side.
140
+ parent = File.dirname(path)
141
+ FileUtils.mkdir_p(parent)
142
+ @file_cache.download_file(path, sha256, uri)
143
+ elsif kind == 'directory'
144
+ # Create directory in cache so we can move files in.
145
+ FileUtils.mkdir_p(path)
146
+ else
147
+ return [400, Bolt::Error.new("Invalid `kind` of '#{kind}' supplied. Must be `file` or `directory`.",
148
+ 'boltserver/schema-error').to_json]
149
+ end
150
+ end
151
+ [@executor.upload_file(target, cache_dir, destination), nil]
74
152
  end
75
153
 
76
154
  get '/' do
@@ -92,7 +170,31 @@ module BoltServer
92
170
  raise 'Unexpected error'
93
171
  end
94
172
 
95
- ACTIONS = %w[run_task run_command].freeze
173
+ ACTIONS = %w[
174
+ check_node_connections
175
+ run_command
176
+ run_task
177
+ upload_file
178
+ ].freeze
179
+
180
+ def make_ssh_target(target_hash)
181
+ defaults = {
182
+ 'host-key-check' => false
183
+ }
184
+
185
+ overrides = {
186
+ 'load-config' => false
187
+ }
188
+
189
+ opts = defaults.merge(target_hash.clone).merge(overrides)
190
+
191
+ if opts['private-key-content']
192
+ private_key_content = opts.delete('private-key-content')
193
+ opts['private-key'] = { 'key-data' => private_key_content }
194
+ end
195
+
196
+ Bolt::Target.new(target_hash['hostname'], opts)
197
+ end
96
198
 
97
199
  post '/ssh/:action' do
98
200
  not_found unless ACTIONS.include?(params[:action])
@@ -103,20 +205,24 @@ module BoltServer
103
205
  error = validate_schema(@schemas["transport-ssh"], body)
104
206
  return [400, error.to_json] unless error.nil?
105
207
 
106
- defaults = { 'host-key-check' => false }
107
- opts = defaults.merge(body['target'])
108
- if opts['private-key-content']
109
- opts['private-key'] = { 'key-data' => opts['private-key-content'] }
110
- opts.delete('private-key-content')
208
+ targets = (body['targets'] || [body['target']]).map do |target|
209
+ make_ssh_target(target)
111
210
  end
112
- opts['load-config'] = false
113
- target = [Bolt::Target.new(body['target']['hostname'], opts)]
114
211
 
115
- results = method(params[:action]).call(target, body)
212
+ result_set, error = method(params[:action]).call(targets, body)
213
+ return [400, error.to_json] unless error.nil?
214
+
215
+ aggregate = body['target'].nil?
216
+ [200, result_set_to_status_hash(result_set, aggregate: aggregate).to_json]
217
+ end
218
+
219
+ def make_winrm_target(target_hash)
220
+ overrides = {
221
+ 'protocol' => 'winrm'
222
+ }
116
223
 
117
- # Since this will only be on one node we can just return the first result
118
- result = scrub_stack_trace(results.first.status_hash)
119
- [200, result.to_json]
224
+ opts = target_hash.clone.merge(overrides)
225
+ Bolt::Target.new(target_hash['hostname'], opts)
120
226
  end
121
227
 
122
228
  post '/winrm/:action' do
@@ -128,14 +234,15 @@ module BoltServer
128
234
  error = validate_schema(@schemas["transport-winrm"], body)
129
235
  return [400, error.to_json] unless error.nil?
130
236
 
131
- opts = body['target'].clone.merge('protocol' => 'winrm')
132
- target = [Bolt::Target.new(body['target']['hostname'], opts)]
237
+ targets = (body['targets'] || [body['target']]).map do |target|
238
+ make_winrm_target(target)
239
+ end
133
240
 
134
- results = method(params[:action]).call(target, body)
241
+ result_set, error = method(params[:action]).call(targets, body)
242
+ return [400, error.to_json] if error
135
243
 
136
- # Since this will only be on one node we can just return the first result
137
- result = scrub_stack_trace(results.first.status_hash)
138
- [200, result.to_json]
244
+ aggregate = body['target'].nil?
245
+ [200, result_set_to_status_hash(result_set, aggregate: aggregate).to_json]
139
246
  end
140
247
 
141
248
  error 404 do
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.30.0
4
+ version: 1.30.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-05 00:00:00.000000000 Z
11
+ date: 2019-09-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -441,8 +441,10 @@ files:
441
441
  - lib/bolt_server/base_config.rb
442
442
  - lib/bolt_server/config.rb
443
443
  - lib/bolt_server/file_cache.rb
444
+ - lib/bolt_server/schemas/action-check_node_connections.json
444
445
  - lib/bolt_server/schemas/action-run_command.json
445
446
  - lib/bolt_server/schemas/action-run_task.json
447
+ - lib/bolt_server/schemas/action-upload_file.json
446
448
  - lib/bolt_server/schemas/partials/target-any.json
447
449
  - lib/bolt_server/schemas/partials/target-ssh.json
448
450
  - lib/bolt_server/schemas/partials/target-winrm.json