bolt 3.16.0 → 3.16.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: 7d61e436c7358897d1769da6db2544c2e5b3e23310bd5a6663d2b35e2afc1ab5
4
- data.tar.gz: 1322c9a80154199cb658cf7807a88a96448b3b197ec5576ddaeabf76da52c29f
3
+ metadata.gz: 71d383b473f9356ff41f574bb0abe4aea9a9f8c3ac5055a459acffe0da290af1
4
+ data.tar.gz: 00d10803d865a9d6f0cb6eb7162e4a17f619b88e6702b559e0153b3796d732d0
5
5
  SHA512:
6
- metadata.gz: 57bddbbc4d2cb78cb2aa6964e3ecff5a61c95c88e5f6b7524bbbc5b5ce3ca070098aae37d2ebb2ba3fcb569e3a082bf9ec4ebff2013bae31fc65eedadbde4b6c
7
- data.tar.gz: 2c64c1607a21cdebe49550ad8207201fe853478c9f18cb4af04826410ddf507f8c505fbae5340faa64c070910e3f9e86372d4ae1de4b6108fad4eb95fc454316
6
+ metadata.gz: eaad9c12aab69e9d7c9f1d8b312623b8bcca4f26f983beb08b1d09c4568aafe0e430d639b88903df4e1a498a58c4c9d8add5d4b8b3930fe1dec3124bc15afad3
7
+ data.tar.gz: a05ec6c181fc6aa51c73ebfd928d73513d5506c1b0705bed4637bfa9490a6f1c010019c9209f7ed28c134f003a063ff6f4add490a55d658998f566d00c5ff017
data/Puppetfile CHANGED
@@ -35,7 +35,7 @@ mod 'puppetlabs-stdlib', '7.1.0'
35
35
  mod 'puppetlabs-aws_inventory', '0.7.0'
36
36
  mod 'puppetlabs-azure_inventory', '0.5.0'
37
37
  mod 'puppetlabs-gcloud_inventory', '0.3.0'
38
- mod 'puppetlabs-http_request', '0.3.0'
38
+ mod 'puppetlabs-http_request', '0.3.1'
39
39
  mod 'puppetlabs-pkcs7', '0.1.2'
40
40
  mod 'puppetlabs-secure_env_vars', '0.2.0'
41
41
  mod 'puppetlabs-terraform', '0.6.1'
@@ -97,20 +97,34 @@ module Bolt
97
97
  end
98
98
  end
99
99
 
100
- return if unsatisfied_specs.empty?
101
-
100
+ versionless_mods = @modules.select { |mod| mod.is_a?(ForgeModule) && mod.version.nil? }
102
101
  command = Bolt::Util.windows? ? 'Install-BoltModule -Force' : 'bolt module install --force'
103
102
 
104
- message = <<~MESSAGE.chomp
105
- Puppetfile does not include modules that satisfy the following specifications:
103
+ if unsatisfied_specs.any?
104
+ message = <<~MESSAGE.chomp
105
+ Puppetfile does not include modules that satisfy the following specifications:
106
+
107
+ #{unsatisfied_specs.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
108
+
109
+ This Puppetfile might not be managed by Bolt. To forcibly overwrite the
110
+ Puppetfile, run '#{command}'.
111
+ MESSAGE
106
112
 
107
- #{unsatisfied_specs.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
108
-
109
- This Puppetfile might not be managed by Bolt. To forcibly overwrite the
110
- Puppetfile, run '#{command}'.
111
- MESSAGE
113
+ raise Bolt::Error.new(message, 'bolt/missing-module-specs')
114
+ end
112
115
 
113
- raise Bolt::Error.new(message, 'bolt/missing-module-specs')
116
+ if versionless_mods.any?
117
+ message = <<~MESSAGE.chomp
118
+ Puppetfile includes Forge modules without a version requirement:
119
+
120
+ #{versionless_mods.map(&:to_spec).join.chomp}
121
+
122
+ This Puppetfile might not be managed by Bolt. To forcibly overwrite the
123
+ Puppetfile, run '#{command}'.
124
+ MESSAGE
125
+
126
+ raise Bolt::Error.new(message, 'bolt/missing-module-version-specs')
127
+ end
114
128
  end
115
129
  end
116
130
  end
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '3.16.0'
4
+ VERSION = '3.16.1'
5
5
  end
@@ -182,5 +182,17 @@ module BoltServer
182
182
  end
183
183
  end
184
184
  end
185
+
186
+ def get_cached_project_file(versioned_project, file_name)
187
+ file_dir = create_cache_dir(versioned_project)
188
+ file_path = File.join(file_dir, file_name)
189
+ serial_execute { File.read(file_path) if File.exist?(file_path) }
190
+ end
191
+
192
+ def cache_project_file(versioned_project, file_name, data)
193
+ file_dir = create_cache_dir(versioned_project)
194
+ file_path = File.join(file_dir, file_name)
195
+ serial_execute { File.open(file_path, 'w') { |f| f.write(data) } }
196
+ end
185
197
  end
186
198
  end
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "apply request",
4
+ "description": "POST <transport>/apply request schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "versioned_project": {
8
+ "type": "string",
9
+ "description": "Project from which to load code"
10
+ },
11
+ "parameters": {
12
+ "type": "object",
13
+ "properties": {
14
+ "catalog": {
15
+ "type": "object",
16
+ "description": "Compiled catalog to apply"
17
+ },
18
+ "apply_options": {
19
+ "type": "object",
20
+ "description": "Options for application of a catalog"
21
+ }
22
+ }
23
+ },
24
+ "job_id": {
25
+ "type": "integer",
26
+ "description": "job-id associated with request"
27
+ },
28
+ "target": { "$ref": "partial:target-any" }
29
+ },
30
+ "required": ["target", "versioned_project", "parameters", "job_id"],
31
+ "additionalProperties": false
32
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/schema#",
3
+ "title": "apply_prep request",
4
+ "description": "POST <transport>/apply_prep request schema",
5
+ "type": "object",
6
+ "properties": {
7
+ "versioned_project": {
8
+ "type": "String",
9
+ "description": "Project from which to load code"
10
+ },
11
+ "target": { "$ref": "partial:target-any" },
12
+ "job_id": {
13
+ "type": "integer",
14
+ "description": "job-id associated with request"
15
+ }
16
+ },
17
+ "required": ["target", "versioned_project", "job_id"],
18
+ "additionalProperties": false
19
+ }
@@ -43,6 +43,8 @@ module BoltServer
43
43
  transport-ssh
44
44
  transport-winrm
45
45
  connect-data
46
+ action-apply_prep
47
+ action-apply
46
48
  ].freeze
47
49
 
48
50
  # PE_BOLTLIB_PATH is intended to function exactly like the BOLTLIB_PATH used
@@ -75,6 +77,12 @@ module BoltServer
75
77
  # This is needed until the PAL is threadsafe.
76
78
  @pal_mutex = Mutex.new
77
79
 
80
+ # Avoid redundant plugin tarbal construction
81
+ @plugin_mutex = Mutex.new
82
+
83
+ # Avoid redundant project_task metadata construction
84
+ @task_metadata_mutex = Mutex.new
85
+
78
86
  @logger = Bolt::Logger.logger(self)
79
87
 
80
88
  super(nil)
@@ -118,12 +126,7 @@ module BoltServer
118
126
  end
119
127
  end
120
128
 
121
- def run_task(target, body)
122
- validate_schema(@schemas["action-run_task"], body)
123
-
124
- task_data = body['task']
125
- task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
126
- parameters = body['parameters'] || {}
129
+ def task_helper(target, task, parameters)
127
130
  # Wrap parameters marked with '"sensitive": true' in the task metadata with a
128
131
  # Sensitive wrapper type. This way it's not shown in logs.
129
132
  if (param_spec = task.parameters)
@@ -142,6 +145,101 @@ module BoltServer
142
145
  end
143
146
  end
144
147
 
148
+ def run_task(target, body)
149
+ validate_schema(@schemas["action-run_task"], body)
150
+
151
+ task_data = body['task']
152
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
153
+ task_helper(target, task, body['parameters'] || {})
154
+ end
155
+
156
+ def extract_install_task(target)
157
+ unless target.plugin_hooks['puppet_library']['task']
158
+ raise BoltServer::RequestError,
159
+ "Target must have 'task' plugin hook"
160
+ end
161
+ install_task = target.plugin_hooks['puppet_library']['task'].split('::', 2)
162
+ install_task << 'init' if install_task.count == 1
163
+ install_task
164
+ end
165
+
166
+ # This helper is responsible for computing or retrieving from the cache a plugin tarball. There are
167
+ # two supported plugin types 'fact_plugins', and 'all_plugins'. Note that this is cached based on
168
+ # versioned_project as there are no plugins in the "builtin content" directory
169
+ def plugin_tarball(versioned_project, tarball_type)
170
+ tarball_types = %w[fact_plugins all_plugins]
171
+ unless tarball_types.include?(tarball_type)
172
+ raise ArgumentError,
173
+ "tarball_type must be one of: #{tarball_types.join(', ')}"
174
+ end
175
+ # lock this so that in the case an apply/apply_prep with multiple targets hits this endpoint
176
+ # the tarball computation only happens once (all the other targets will just need to read the cached data)
177
+ @plugin_mutex.synchronize do
178
+ if (tarball = @file_cache.get_cached_project_file(versioned_project, tarball_type))
179
+ tarball
180
+ else
181
+ new_tarball = build_project_plugins_tarball(versioned_project) do |mod|
182
+ search_dirs = []
183
+ search_dirs << mod.plugins if mod.plugins?
184
+ search_dirs << mod.pluginfacts if mod.pluginfacts?
185
+ if tarball_type == 'all_plugins'
186
+ search_dirs << mod.files if mod.files?
187
+ type_files = "#{mod.path}/types"
188
+ search_dirs << type_files if File.exist?(type_files)
189
+ end
190
+ search_dirs
191
+ end
192
+ @file_cache.cache_project_file(versioned_project, tarball_type, new_tarball)
193
+ new_tarball
194
+ end
195
+ end
196
+ end
197
+
198
+ # This helper is responsible for computing or retrieving task metadata for a project.
199
+ # It expects task name in segments and uses the combination of task name and versioned_project
200
+ # as a unique identifier for caching in addition to the job_id. The job id is added to protect against
201
+ # a case where the buildtin content is update (where the apply_helpers would be managed)
202
+ def project_task_metadata(versioned_project, task_name_segments, job_id)
203
+ cached_file_name = "#{task_name_segments.join('_')}_#{job_id}"
204
+ # lock this so that in the case an apply/apply_prep with multiple targets hits this endpoint the
205
+ # metadata computation will only be computed once, then the cache will be read.
206
+ @task_metadata_mutex.synchronize do
207
+ if (metadata = @file_cache.get_cached_project_file(versioned_project, cached_file_name))
208
+ JSON.parse(metadata)
209
+ else
210
+ new_metadata = in_bolt_project(versioned_project) do |context|
211
+ ps_parameters = {
212
+ 'versioned_project' => versioned_project
213
+ }
214
+ pe_task_info(context[:pal], *task_name_segments, ps_parameters)
215
+ end
216
+ @file_cache.cache_project_file(versioned_project, cached_file_name, new_metadata.to_json)
217
+ new_metadata
218
+ end
219
+ end
220
+ end
221
+
222
+ def apply_prep(target, body)
223
+ validate_schema(@schemas["action-apply_prep"], body)
224
+ plugins_tarball = plugin_tarball(body['versioned_project'], 'fact_plugins')
225
+ install_task_segments = extract_install_task(target.first)
226
+ task_data = project_task_metadata(body['versioned_project'], install_task_segments, body["job_id"])
227
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
228
+ install_task_result = task_helper(target, task, target.first.plugin_hooks['puppet_library']['parameters'] || {})
229
+ return install_task_result unless install_task_result.ok
230
+ task_data = project_task_metadata(body['versioned_project'], %w[apply_helpers custom_facts], body["job_id"])
231
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
232
+ task_helper(target, task, { 'plugins' => plugins_tarball })
233
+ end
234
+
235
+ def apply(target, body)
236
+ validate_schema(@schemas["action-apply"], body)
237
+ plugins_tarball = plugin_tarball(body['versioned_project'], 'all_plugins')
238
+ task_data = project_task_metadata(body['versioned_project'], %w[apply_helpers apply_catalog], body["job_id"])
239
+ task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
240
+ task_helper(target, task, body['parameters'].merge({ 'plugins' => plugins_tarball }))
241
+ end
242
+
145
243
  def run_command(target, body)
146
244
  validate_schema(@schemas["action-run_command"], body)
147
245
  command = body['command']
@@ -476,6 +574,8 @@ module BoltServer
476
574
  run_task
477
575
  run_script
478
576
  upload_file
577
+ apply
578
+ apply_prep
479
579
  ].freeze
480
580
 
481
581
  def make_ssh_target(target_hash)
@@ -499,7 +599,8 @@ module BoltServer
499
599
  'config' => {
500
600
  'transport' => 'ssh',
501
601
  'ssh' => opts.slice(*Bolt::Config::Transport::SSH.options)
502
- }
602
+ },
603
+ 'plugin_hooks' => target_hash['plugin_hooks']
503
604
  }
504
605
 
505
606
  inventory = Bolt::Inventory.empty
@@ -537,7 +638,8 @@ module BoltServer
537
638
  'config' => {
538
639
  'transport' => 'winrm',
539
640
  'winrm' => opts.slice(*Bolt::Config::Transport::WinRM.options)
540
- }
641
+ },
642
+ 'plugin_hooks' => target_hash['plugin_hooks']
541
643
  }
542
644
 
543
645
  inventory = Bolt::Inventory.empty
@@ -756,12 +858,7 @@ module BoltServer
756
858
  raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
757
859
  content_type :json
758
860
 
759
- plugins_tarball = build_project_plugins_tarball(params['versioned_project']) do |mod|
760
- search_dirs = []
761
- search_dirs << mod.plugins if mod.plugins?
762
- search_dirs << mod.pluginfacts if mod.pluginfacts?
763
- search_dirs
764
- end
861
+ plugins_tarball = plugin_tarball(params['versioned_project'], 'fact_plugins')
765
862
 
766
863
  [200, plugins_tarball.to_json]
767
864
  end
@@ -773,15 +870,7 @@ module BoltServer
773
870
  raise BoltServer::RequestError, "'versioned_project' is a required argument" if params['versioned_project'].nil?
774
871
  content_type :json
775
872
 
776
- plugins_tarball = build_project_plugins_tarball(params['versioned_project']) do |mod|
777
- search_dirs = []
778
- search_dirs << mod.plugins if mod.plugins?
779
- search_dirs << mod.pluginfacts if mod.pluginfacts?
780
- search_dirs << mod.files if mod.files?
781
- type_files = "#{mod.path}/types"
782
- search_dirs << type_files if File.exist?(type_files)
783
- search_dirs
784
- end
873
+ plugins_tarball = plugin_tarball(params['versioned_project'], 'all_plugins')
785
874
 
786
875
  [200, plugins_tarball.to_json]
787
876
  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: 3.16.0
4
+ version: 3.16.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-09 00:00:00.000000000 Z
11
+ date: 2021-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -610,6 +610,8 @@ files:
610
610
  - lib/bolt_server/plugin.rb
611
611
  - lib/bolt_server/plugin/puppet_connect_data.rb
612
612
  - lib/bolt_server/request_error.rb
613
+ - lib/bolt_server/schemas/action-apply.json
614
+ - lib/bolt_server/schemas/action-apply_prep.json
613
615
  - lib/bolt_server/schemas/action-check_node_connections.json
614
616
  - lib/bolt_server/schemas/action-run_command.json
615
617
  - lib/bolt_server/schemas/action-run_script.json