bolt 2.24.0 → 2.28.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.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +1 -1
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
  5. data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +1 -1
  6. data/lib/bolt/analytics.rb +7 -3
  7. data/lib/bolt/applicator.rb +21 -21
  8. data/lib/bolt/bolt_option_parser.rb +75 -7
  9. data/lib/bolt/catalog.rb +4 -2
  10. data/lib/bolt/cli.rb +126 -147
  11. data/lib/bolt/config.rb +56 -26
  12. data/lib/bolt/config/options.rb +24 -2
  13. data/lib/bolt/executor.rb +1 -1
  14. data/lib/bolt/inventory.rb +8 -1
  15. data/lib/bolt/inventory/group.rb +1 -1
  16. data/lib/bolt/inventory/inventory.rb +1 -1
  17. data/lib/bolt/inventory/target.rb +1 -1
  18. data/lib/bolt/logger.rb +9 -2
  19. data/lib/bolt/outputter/logger.rb +1 -1
  20. data/lib/bolt/pal.rb +21 -10
  21. data/lib/bolt/pal/yaml_plan/evaluator.rb +1 -1
  22. data/lib/bolt/plugin/puppetdb.rb +1 -1
  23. data/lib/bolt/project.rb +63 -17
  24. data/lib/bolt/puppetdb/client.rb +1 -1
  25. data/lib/bolt/puppetdb/config.rb +1 -1
  26. data/lib/bolt/puppetfile.rb +160 -0
  27. data/lib/bolt/puppetfile/installer.rb +43 -0
  28. data/lib/bolt/puppetfile/module.rb +66 -0
  29. data/lib/bolt/r10k_log_proxy.rb +1 -1
  30. data/lib/bolt/rerun.rb +2 -2
  31. data/lib/bolt/result.rb +23 -0
  32. data/lib/bolt/shell.rb +1 -1
  33. data/lib/bolt/task.rb +1 -1
  34. data/lib/bolt/transport/base.rb +5 -5
  35. data/lib/bolt/transport/docker/connection.rb +1 -1
  36. data/lib/bolt/transport/local/connection.rb +1 -1
  37. data/lib/bolt/transport/ssh.rb +1 -1
  38. data/lib/bolt/transport/ssh/connection.rb +1 -1
  39. data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
  40. data/lib/bolt/transport/winrm.rb +1 -1
  41. data/lib/bolt/transport/winrm/connection.rb +1 -1
  42. data/lib/bolt/util.rb +11 -11
  43. data/lib/bolt/version.rb +1 -1
  44. data/lib/bolt_server/base_config.rb +1 -1
  45. data/lib/bolt_server/config.rb +1 -1
  46. data/lib/bolt_server/file_cache.rb +12 -12
  47. data/lib/bolt_server/transport_app.rb +125 -26
  48. data/lib/bolt_spec/bolt_context.rb +4 -4
  49. data/lib/bolt_spec/run.rb +3 -0
  50. metadata +9 -12
@@ -11,7 +11,7 @@ module Bolt
11
11
  require 'winrm'
12
12
  require 'winrm-fs'
13
13
 
14
- @transport_logger = Logging.logger[::WinRM]
14
+ @transport_logger = Bolt::Logger.logger(::WinRM)
15
15
  @transport_logger.level = :warn
16
16
  end
17
17
 
@@ -18,7 +18,7 @@ module Bolt
18
18
  @user = @target.user
19
19
  # Build set of extensions from extensions config as well as interpreters
20
20
 
21
- @logger = Logging.logger[@target.safe_name]
21
+ @logger = Bolt::Logger.logger(@target.safe_name)
22
22
  logger.trace("Initializing winrm connection to #{@target.safe_name}")
23
23
  @transport_logger = transport_logger
24
24
  end
@@ -6,7 +6,7 @@ module Bolt
6
6
  def read_yaml_hash(path, file_name)
7
7
  require 'yaml'
8
8
 
9
- logger = Logging.logger[self]
9
+ logger = Bolt::Logger.logger(self)
10
10
  path = File.expand_path(path)
11
11
  content = File.open(path, "r:UTF-8") { |f| YAML.safe_load(f.read) } || {}
12
12
  unless content.is_a?(Hash)
@@ -179,16 +179,16 @@ module Bolt
179
179
  # object was frozen
180
180
  frozen = obj.frozen?
181
181
  cl = begin
182
- obj.clone(freeze: false)
183
- # Some datatypes, such as FalseClass, can't be unfrozen. These
184
- # aren't the types we recurse on, so we can leave them frozen
185
- rescue ArgumentError => e
186
- if e.message =~ /can't unfreeze/
187
- obj.clone
188
- else
189
- raise e
190
- end
191
- end
182
+ obj.clone(freeze: false)
183
+ # Some datatypes, such as FalseClass, can't be unfrozen. These
184
+ # aren't the types we recurse on, so we can leave them frozen
185
+ rescue ArgumentError => e
186
+ if e.message =~ /can't unfreeze/
187
+ obj.clone
188
+ else
189
+ raise e
190
+ end
191
+ end
192
192
  rescue *error_types
193
193
  cloned[obj.object_id] = obj
194
194
  obj
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.24.0'
4
+ VERSION = '2.28.0'
5
5
  end
@@ -7,7 +7,7 @@ module BoltServer
7
7
  class BaseConfig
8
8
  def config_keys
9
9
  %w[host port ssl-cert ssl-key ssl-ca-cert
10
- ssl-cipher-suites loglevel logfile whitelist]
10
+ ssl-cipher-suites loglevel logfile whitelist projects-dir]
11
11
  end
12
12
 
13
13
  def env_keys
@@ -7,7 +7,7 @@ require 'bolt/error'
7
7
  module BoltServer
8
8
  class Config < BoltServer::BaseConfig
9
9
  def config_keys
10
- super + %w[concurrency cache-dir file-server-conn-timeout file-server-uri]
10
+ super + %w[concurrency cache-dir file-server-conn-timeout file-server-uri projects-dir]
11
11
  end
12
12
 
13
13
  def env_keys
@@ -33,7 +33,7 @@ module BoltServer
33
33
  @executor = executor
34
34
  @cache_dir = config['cache-dir']
35
35
  @config = config
36
- @logger = Logging.logger[self]
36
+ @logger = Bolt::Logger.logger(self)
37
37
  @cache_dir_mutex = cache_dir_mutex
38
38
 
39
39
  if do_purge
@@ -64,17 +64,17 @@ module BoltServer
64
64
 
65
65
  def client
66
66
  @client ||= begin
67
- uri = URI(@config['file-server-uri'])
68
- https = Net::HTTP.new(uri.host, uri.port)
69
- https.use_ssl = true
70
- https.ssl_version = :TLSv1_2
71
- https.ca_file = @config['ssl-ca-cert']
72
- https.cert = OpenSSL::X509::Certificate.new(ssl_cert)
73
- https.key = OpenSSL::PKey::RSA.new(ssl_key)
74
- https.verify_mode = OpenSSL::SSL::VERIFY_PEER
75
- https.open_timeout = @config['file-server-conn-timeout']
76
- https
77
- end
67
+ uri = URI(@config['file-server-uri'])
68
+ https = Net::HTTP.new(uri.host, uri.port)
69
+ https.use_ssl = true
70
+ https.ssl_version = :TLSv1_2
71
+ https.ca_file = @config['ssl-ca-cert']
72
+ https.cert = OpenSSL::X509::Certificate.new(ssl_cert)
73
+ https.key = OpenSSL::PKey::RSA.new(ssl_key)
74
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
75
+ https.open_timeout = @config['file-server-conn-timeout']
76
+ https
77
+ end
78
78
  end
79
79
 
80
80
  def request_file(path, params, file)
@@ -5,6 +5,7 @@ require 'addressable/uri'
5
5
  require 'bolt'
6
6
  require 'bolt/error'
7
7
  require 'bolt/inventory'
8
+ require 'bolt/project'
8
9
  require 'bolt/target'
9
10
  require 'bolt_server/file_cache'
10
11
  require 'bolt/task/puppet_server'
@@ -191,20 +192,44 @@ module BoltServer
191
192
  end
192
193
 
193
194
  def in_pe_pal_env(environment)
194
- if environment.nil?
195
- [400, '`environment` is a required argument']
196
- else
197
- @pal_mutex.synchronize do
198
- pal = BoltServer::PE::PAL.new({}, environment)
199
- yield pal
200
- rescue Puppet::Environments::EnvironmentNotFound
201
- [400, {
202
- "class" => 'bolt/unknown-environment',
203
- "message" => "Environment #{environment} not found"
204
- }.to_json]
205
- rescue Bolt::Error => e
206
- [400, e.to_json]
207
- end
195
+ return [400, '`environment` is a required argument'] if environment.nil?
196
+ @pal_mutex.synchronize do
197
+ pal = BoltServer::PE::PAL.new({}, environment)
198
+ yield pal
199
+ rescue Puppet::Environments::EnvironmentNotFound
200
+ [400, {
201
+ "class" => 'bolt/unknown-environment',
202
+ "message" => "Environment #{environment} not found"
203
+ }.to_json]
204
+ rescue Bolt::Error => e
205
+ [400, e.to_json]
206
+ end
207
+ end
208
+
209
+ def in_bolt_project(bolt_project)
210
+ return [400, '`project_ref` is a required argument'] if bolt_project.nil?
211
+ project_dir = File.join(@config['projects-dir'], bolt_project)
212
+ return [400, "`project_ref`: #{project_dir} does not exist"] unless Dir.exist?(project_dir)
213
+ @pal_mutex.synchronize do
214
+ project = Bolt::Project.create_project(project_dir)
215
+ bolt_config = Bolt::Config.from_project(project, {})
216
+ pal = Bolt::PAL.new(bolt_config.modulepath, nil, nil, nil, nil, nil, bolt_config.project)
217
+ module_path = [
218
+ BoltServer::PE::PAL::PE_BOLTLIB_PATH,
219
+ Bolt::PAL::BOLTLIB_PATH,
220
+ *bolt_config.modulepath,
221
+ Bolt::PAL::MODULES_PATH
222
+ ]
223
+ # CODEREVIEW: I *think* this is the only thing we need to make different between bolt's PAL. Is it acceptable
224
+ # to hack this? Modulepath is currently a readable attribute, could we make it writeable?
225
+ pal.instance_variable_set(:@modulepath, module_path)
226
+ context = {
227
+ pal: pal,
228
+ config: bolt_config
229
+ }
230
+ yield context
231
+ rescue Bolt::Error => e
232
+ [400, e.to_json]
208
233
  end
209
234
  end
210
235
 
@@ -221,14 +246,12 @@ module BoltServer
221
246
  plan_info
222
247
  end
223
248
 
224
- def build_puppetserver_uri(file_identifier, module_name, environment)
249
+ def build_puppetserver_uri(file_identifier, module_name, parameters)
225
250
  segments = file_identifier.split('/', 3)
226
251
  if segments.size == 1
227
252
  {
228
253
  'path' => "/puppet/v3/file_content/tasks/#{module_name}/#{file_identifier}",
229
- 'params' => {
230
- 'environment' => environment
231
- }
254
+ 'params' => parameters
232
255
  }
233
256
  else
234
257
  module_segment, mount_segment, name_segment = *segments
@@ -241,14 +264,12 @@ module BoltServer
241
264
  when 'lib'
242
265
  "/puppet/v3/file_content/plugins/#{name_segment}"
243
266
  end,
244
- 'params' => {
245
- 'environment' => environment
246
- }
267
+ 'params' => parameters
247
268
  }
248
269
  end
249
270
  end
250
271
 
251
- def pe_task_info(pal, module_name, task_name, environment)
272
+ def pe_task_info(pal, module_name, task_name, parameters)
252
273
  # Handle case where task name is simply module name with special `init` task
253
274
  task_name = if task_name == 'init' || task_name.nil?
254
275
  module_name
@@ -261,7 +282,7 @@ module BoltServer
261
282
  'filename' => file_hash['name'],
262
283
  'sha256' => Digest::SHA256.hexdigest(File.read(file_hash['path'])),
263
284
  'size_bytes' => File.size(file_hash['path']),
264
- 'uri' => build_puppetserver_uri(file_hash['name'], module_name, environment)
285
+ 'uri' => build_puppetserver_uri(file_hash['name'], module_name, parameters)
265
286
  }
266
287
  end
267
288
  {
@@ -271,6 +292,21 @@ module BoltServer
271
292
  }
272
293
  end
273
294
 
295
+ def allowed_helper(metadata, allowlist)
296
+ allowed = allowlist.nil? || allowlist.include?(metadata['name']) ? true : false
297
+ metadata.merge({ 'allowed' => allowed })
298
+ end
299
+
300
+ def task_list(pal)
301
+ tasks = pal.list_tasks
302
+ tasks.map { |task_name, _description| { 'name' => task_name } }
303
+ end
304
+
305
+ def plan_list(pal)
306
+ plans = pal.list_plans.flatten
307
+ plans.map { |plan_name| { 'name' => plan_name } }
308
+ end
309
+
274
310
  get '/' do
275
311
  200
276
312
  end
@@ -401,12 +437,40 @@ module BoltServer
401
437
  end
402
438
  end
403
439
 
440
+ # Fetches the metadata for a single plan
441
+ #
442
+ # @param project_ref [String] the project to fetch the plan from
443
+ get '/project_plans/:module_name/:plan_name' do
444
+ in_bolt_project(params['project_ref']) do |context|
445
+ plan_info = pe_plan_info(context[:pal], params[:module_name], params[:plan_name])
446
+ plan_info = allowed_helper(plan_info, context[:config].project.plans)
447
+ [200, plan_info.to_json]
448
+ end
449
+ end
450
+
404
451
  # Fetches the metadata for a single task
405
452
  #
406
453
  # @param environment [String] the environment to fetch the task from
407
454
  get '/tasks/:module_name/:task_name' do
408
455
  in_pe_pal_env(params['environment']) do |pal|
409
- task_info = pe_task_info(pal, params[:module_name], params[:task_name], params['environment'])
456
+ ps_parameters = {
457
+ 'environment' => params['environment']
458
+ }
459
+ task_info = pe_task_info(pal, params[:module_name], params[:task_name], ps_parameters)
460
+ [200, task_info.to_json]
461
+ end
462
+ end
463
+
464
+ # Fetches the metadata for a single task
465
+ #
466
+ # @param bolt_project_ref [String] the reference to the bolt-project directory to load task metadata from
467
+ get '/project_tasks/:module_name/:task_name' do
468
+ in_bolt_project(params['project_ref']) do |context|
469
+ ps_parameters = {
470
+ 'project' => params['project_ref']
471
+ }
472
+ task_info = pe_task_info(context[:pal], params[:module_name], params[:task_name], ps_parameters)
473
+ task_info = allowed_helper(task_info, context[:config].project.tasks)
410
474
  [200, task_info.to_json]
411
475
  end
412
476
  end
@@ -435,13 +499,30 @@ module BoltServer
435
499
  end
436
500
  end
437
501
 
502
+ # Fetches the list of plans for a project
503
+ #
504
+ # @param project_ref [String] the project to fetch the list of plans from
505
+ get '/project_plans' do
506
+ in_bolt_project(params['project_ref']) do |context|
507
+ plans_response = plan_list(context[:pal])
508
+
509
+ # Dig in context for the allowlist of plans from project object
510
+ plans_response.map! { |metadata| allowed_helper(metadata, context[:config].project.plans) }
511
+
512
+ # We structure this array of plans to be an array of hashes so that it matches the structure
513
+ # returned by the puppetserver API that serves data like this. Structuring the output this way
514
+ # makes switching between puppetserver and bolt-server easier, which makes changes to switch
515
+ # to bolt-server smaller/simpler.
516
+ [200, plans_response.to_json]
517
+ end
518
+ end
519
+
438
520
  # Fetches the list of tasks for an environment
439
521
  #
440
522
  # @param environment [String] the environment to fetch the list of tasks from
441
523
  get '/tasks' do
442
524
  in_pe_pal_env(params['environment']) do |pal|
443
- tasks = pal.list_tasks
444
- tasks_response = tasks.map { |task_name, _description| { 'name' => task_name } }.to_json
525
+ tasks_response = task_list(pal).to_json
445
526
 
446
527
  # We structure this array of tasks to be an array of hashes so that it matches the structure
447
528
  # returned by the puppetserver API that serves data like this. Structuring the output this way
@@ -451,6 +532,24 @@ module BoltServer
451
532
  end
452
533
  end
453
534
 
535
+ # Fetches the list of tasks for a bolt-project
536
+ #
537
+ # @param project_ref [String] the project to fetch the list of tasks from
538
+ get '/project_tasks' do
539
+ in_bolt_project(params['project_ref']) do |context|
540
+ tasks_response = task_list(context[:pal])
541
+
542
+ # Dig in context for the allowlist of tasks from project object
543
+ tasks_response.map! { |metadata| allowed_helper(metadata, context[:config].project.tasks) }
544
+
545
+ # We structure this array of tasks to be an array of hashes so that it matches the structure
546
+ # returned by the puppetserver API that serves data like this. Structuring the output this way
547
+ # makes switching between puppetserver and bolt-server easier, which makes changes to switch
548
+ # to bolt-server smaller/simpler.
549
+ [200, tasks_response.to_json]
550
+ end
551
+ end
552
+
454
553
  error 404 do
455
554
  err = Bolt::Error.new("Could not find route #{request.path}",
456
555
  'boltserver/not-found')
@@ -142,10 +142,10 @@ module BoltSpec
142
142
  # Override in your tests
143
143
  def config
144
144
  @config ||= begin
145
- conf = Bolt::Config.default
146
- conf.modulepath = [modulepath].flatten
147
- conf
148
- end
145
+ conf = Bolt::Config.default
146
+ conf.modulepath = [modulepath].flatten
147
+ conf
148
+ end
149
149
  end
150
150
 
151
151
  def plugins
@@ -8,6 +8,7 @@ require 'bolt/pal'
8
8
  require 'bolt/plugin'
9
9
  require 'bolt/puppetdb'
10
10
  require 'bolt/util'
11
+ require 'bolt/logger'
11
12
 
12
13
  # This is intended to provide a relatively stable method of executing bolt in process from tests.
13
14
  module BoltSpec
@@ -153,6 +154,8 @@ module BoltSpec
153
154
  end
154
155
 
155
156
  def initialize(config_data, inventory_data, project_path)
157
+ Bolt::Logger.initialize_logging
158
+
156
159
  @config_data = config_data || {}
157
160
  @inventory_data = inventory_data || {}
158
161
  @project_path = project_path
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: 2.24.0
4
+ version: 2.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-24 00:00:00.000000000 Z
11
+ date: 2020-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -182,22 +182,16 @@ dependencies:
182
182
  name: puppet
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
- - - ">="
186
- - !ruby/object:Gem::Version
187
- version: 6.16.0
188
- - - "<"
185
+ - - '='
189
186
  - !ruby/object:Gem::Version
190
- version: '7'
187
+ version: 6.18.0
191
188
  type: :runtime
192
189
  prerelease: false
193
190
  version_requirements: !ruby/object:Gem::Requirement
194
191
  requirements:
195
- - - ">="
196
- - !ruby/object:Gem::Version
197
- version: 6.16.0
198
- - - "<"
192
+ - - '='
199
193
  - !ruby/object:Gem::Version
200
- version: '7'
194
+ version: 6.18.0
201
195
  - !ruby/object:Gem::Dependency
202
196
  name: puppetfile-resolver
203
197
  requirement: !ruby/object:Gem::Requirement
@@ -504,6 +498,9 @@ files:
504
498
  - lib/bolt/puppetdb.rb
505
499
  - lib/bolt/puppetdb/client.rb
506
500
  - lib/bolt/puppetdb/config.rb
501
+ - lib/bolt/puppetfile.rb
502
+ - lib/bolt/puppetfile/installer.rb
503
+ - lib/bolt/puppetfile/module.rb
507
504
  - lib/bolt/r10k_log_proxy.rb
508
505
  - lib/bolt/rerun.rb
509
506
  - lib/bolt/resource_instance.rb