bolt 3.0.1 → 3.6.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +13 -11
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/containerresult.rb +24 -0
  4. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  5. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +20 -2
  6. data/bolt-modules/boltlib/lib/puppet/functions/run_container.rb +162 -0
  7. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +2 -2
  8. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +44 -5
  9. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  10. data/bolt-modules/boltlib/types/planresult.pp +1 -0
  11. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -2
  12. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +20 -2
  13. data/bolt-modules/prompt/lib/puppet/functions/prompt/menu.rb +103 -0
  14. data/lib/bolt/analytics.rb +4 -8
  15. data/lib/bolt/apply_result.rb +1 -1
  16. data/lib/bolt/bolt_option_parser.rb +6 -3
  17. data/lib/bolt/cli.rb +123 -37
  18. data/lib/bolt/config.rb +15 -7
  19. data/lib/bolt/config/options.rb +62 -12
  20. data/lib/bolt/config/transport/lxd.rb +23 -0
  21. data/lib/bolt/config/transport/options.rb +8 -1
  22. data/lib/bolt/config/transport/podman.rb +33 -0
  23. data/lib/bolt/container_result.rb +105 -0
  24. data/lib/bolt/error.rb +15 -0
  25. data/lib/bolt/executor.rb +37 -18
  26. data/lib/bolt/inventory/options.rb +9 -0
  27. data/lib/bolt/inventory/target.rb +16 -0
  28. data/lib/bolt/logger.rb +8 -0
  29. data/lib/bolt/module_installer.rb +2 -2
  30. data/lib/bolt/module_installer/puppetfile.rb +2 -2
  31. data/lib/bolt/module_installer/specs/forge_spec.rb +2 -2
  32. data/lib/bolt/module_installer/specs/git_spec.rb +2 -2
  33. data/lib/bolt/node/output.rb +14 -4
  34. data/lib/bolt/outputter/human.rb +259 -90
  35. data/lib/bolt/outputter/json.rb +3 -1
  36. data/lib/bolt/outputter/logger.rb +17 -0
  37. data/lib/bolt/pal.rb +25 -4
  38. data/lib/bolt/pal/yaml_plan.rb +1 -2
  39. data/lib/bolt/pal/yaml_plan/evaluator.rb +5 -141
  40. data/lib/bolt/pal/yaml_plan/step.rb +91 -31
  41. data/lib/bolt/pal/yaml_plan/step/command.rb +21 -13
  42. data/lib/bolt/pal/yaml_plan/step/download.rb +15 -16
  43. data/lib/bolt/pal/yaml_plan/step/eval.rb +11 -11
  44. data/lib/bolt/pal/yaml_plan/step/message.rb +13 -4
  45. data/lib/bolt/pal/yaml_plan/step/plan.rb +19 -15
  46. data/lib/bolt/pal/yaml_plan/step/resources.rb +82 -21
  47. data/lib/bolt/pal/yaml_plan/step/script.rb +36 -17
  48. data/lib/bolt/pal/yaml_plan/step/task.rb +19 -16
  49. data/lib/bolt/pal/yaml_plan/step/upload.rb +16 -17
  50. data/lib/bolt/pal/yaml_plan/transpiler.rb +3 -3
  51. data/lib/bolt/plan_creator.rb +1 -1
  52. data/lib/bolt/plugin.rb +13 -11
  53. data/lib/bolt/project_manager.rb +1 -1
  54. data/lib/bolt/project_manager/module_migrator.rb +1 -1
  55. data/lib/bolt/result.rb +11 -15
  56. data/lib/bolt/shell.rb +16 -0
  57. data/lib/bolt/shell/bash.rb +61 -31
  58. data/lib/bolt/shell/bash/tmpdir.rb +2 -2
  59. data/lib/bolt/shell/powershell.rb +34 -12
  60. data/lib/bolt/shell/powershell/snippets.rb +30 -3
  61. data/lib/bolt/task.rb +1 -1
  62. data/lib/bolt/transport/base.rb +0 -9
  63. data/lib/bolt/transport/docker.rb +2 -126
  64. data/lib/bolt/transport/docker/connection.rb +81 -167
  65. data/lib/bolt/transport/lxd.rb +26 -0
  66. data/lib/bolt/transport/lxd/connection.rb +99 -0
  67. data/lib/bolt/transport/orch.rb +13 -5
  68. data/lib/bolt/transport/podman.rb +19 -0
  69. data/lib/bolt/transport/podman/connection.rb +98 -0
  70. data/lib/bolt/transport/ssh/connection.rb +1 -1
  71. data/lib/bolt/transport/winrm/connection.rb +1 -1
  72. data/lib/bolt/util.rb +42 -0
  73. data/lib/bolt/version.rb +1 -1
  74. data/lib/bolt_server/transport_app.rb +64 -33
  75. data/lib/bolt_spec/bolt_context.rb +9 -4
  76. data/lib/bolt_spec/plans.rb +1 -109
  77. data/lib/bolt_spec/plans/action_stubs.rb +1 -1
  78. data/lib/bolt_spec/plans/action_stubs/command_stub.rb +8 -1
  79. data/lib/bolt_spec/plans/action_stubs/script_stub.rb +8 -1
  80. data/lib/bolt_spec/plans/mock_executor.rb +91 -7
  81. data/modules/puppet_connect/plans/test_input_data.pp +65 -7
  82. metadata +12 -2
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/logger'
4
+ require 'bolt/node/errors'
5
+ require 'bolt/transport/simple'
6
+
7
+ module Bolt
8
+ module Transport
9
+ class LXD < Simple
10
+ def provided_features
11
+ ['shell']
12
+ end
13
+
14
+ def with_connection(target, options = {})
15
+ Bolt::Logger.warn_once("lxd_experimental",
16
+ "The LXD transport is experimental, and might "\
17
+ "include breaking changes between minor versions.")
18
+ conn = Connection.new(target, options)
19
+ conn.connect
20
+ yield conn
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ require 'bolt/transport/lxd/connection'
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+ require 'bolt/node/errors'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class LXD < Simple
9
+ class Connection
10
+ attr_reader :user, :target
11
+
12
+ def initialize(target, options)
13
+ raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
14
+
15
+ @target = target
16
+ @user = ENV['USER'] || Etc.getlogin
17
+ @options = options
18
+ @logger = Bolt::Logger.logger(target.safe_name)
19
+ @logger.trace("Initializing LXD connection to #{target.safe_name}")
20
+ end
21
+
22
+ def shell
23
+ Bolt::Shell::Bash.new(target, self)
24
+ end
25
+
26
+ def container_id
27
+ "#{@target.transport_config['remote']}:#{@target.host}"
28
+ end
29
+
30
+ def connect
31
+ out, err, status = execute_local_command(%W[list #{container_id} --format json])
32
+ unless status.exitstatus.zero?
33
+ raise "Error listing available containers: #{err}"
34
+ end
35
+ containers = JSON.parse(out)
36
+ if containers.empty?
37
+ raise "Could not find a container with name or ID matching '#{container_id}'"
38
+ end
39
+ @logger.trace("Opened session")
40
+ true
41
+ rescue StandardError => e
42
+ raise Bolt::Node::ConnectError.new(
43
+ "Failed to connect to #{container_id}: #{e.message}",
44
+ 'CONNECT_ERROR'
45
+ )
46
+ end
47
+
48
+ def add_env_vars(env_vars)
49
+ @env_vars = env_vars.each_with_object([]) do |env_var, acc|
50
+ acc << "--env"
51
+ acc << "#{env_var[0]}=#{Shellwords.shellescape(env_var[1])}"
52
+ end
53
+ end
54
+
55
+ def execute(command)
56
+ lxc_command = %w[lxc exec]
57
+ lxc_command += @env_vars if @env_vars
58
+ lxc_command += %W[#{container_id} -- sh -c #{Shellwords.shellescape(command)}]
59
+
60
+ @logger.trace { "Executing: #{lxc_command.join(' ')}" }
61
+ Open3.popen3(lxc_command.join(' '))
62
+ end
63
+
64
+ private def execute_local_command(command)
65
+ Open3.capture3('lxc', *command, { binmode: true })
66
+ end
67
+
68
+ def upload_file(source, destination)
69
+ @logger.trace { "Uploading #{source} to #{destination}" }
70
+ args = %w[--create-dirs]
71
+ if File.directory?(source)
72
+ args << '--recursive'
73
+ # If we don't do this, LXD will upload to
74
+ # /tmp/d2020-11/d2020-11/dir instead of /tmp/d2020-11/dir
75
+ destination = Pathname.new(destination).dirname.to_s
76
+ end
77
+ cmd = %w[file push] + args + %W[#{source} #{container_id}#{destination}]
78
+ _out, err, stat = execute_local_command(cmd)
79
+ unless stat.exitstatus.zero?
80
+ raise "Error writing to #{container_id}: #{err}"
81
+ end
82
+ rescue StandardError => e
83
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
84
+ end
85
+
86
+ def download_file(source, destination, _download)
87
+ @logger.trace { "Downloading #{source} to #{destination}" }
88
+ FileUtils.mkdir_p(destination)
89
+ _out, err, stat = execute_local_command(%W[file pull --recursive #{container_id}#{source} #{destination}])
90
+ unless stat.exitstatus.zero?
91
+ raise "Error downloading content from container #{container_id}: #{err}"
92
+ end
93
+ rescue StandardError => e
94
+ raise Bolt::Node::FileError.new(e.message, 'WRITE_ERROR')
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -59,6 +59,18 @@ module Bolt
59
59
  # the result otherwise make sure an error is generated
60
60
  if state == 'finished' || (result && result['_error'])
61
61
  if result['_error']
62
+ unless result['_error'].is_a?(Hash)
63
+ result['_error'] = { 'kind' => 'puppetlabs.tasks/task-error',
64
+ 'issue_code' => 'TASK_ERROR',
65
+ 'msg' => result['_error'],
66
+ 'details' => {} }
67
+ end
68
+
69
+ result['_error']['details'] ||= {}
70
+ unless result['_error']['details'].is_a?(Hash)
71
+ deets = result['_error']['details']
72
+ result['_error']['details'] = { 'msg' => deets }
73
+ end
62
74
  file_line = %w[file line].zip(position).to_h.compact
63
75
  result['_error']['details'].merge!(file_line) unless result['_error']['details']['file']
64
76
  end
@@ -252,11 +264,7 @@ module Bolt
252
264
 
253
265
  # If we get here, there's no error so we don't need the file or line
254
266
  # number
255
- Bolt::Result.for_command(target,
256
- result.value['stdout'],
257
- result.value['stderr'],
258
- result.value['exit_code'],
259
- action, obj, [])
267
+ Bolt::Result.for_command(target, result.value, action, obj, [])
260
268
  end
261
269
  end
262
270
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'shellwords'
5
+ require 'bolt/transport/base'
6
+
7
+ module Bolt
8
+ module Transport
9
+ class Podman < Docker
10
+ def with_connection(target)
11
+ conn = Connection.new(target)
12
+ conn.connect
13
+ yield conn
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ require 'bolt/transport/podman/connection'
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logging'
4
+ require 'bolt/node/errors'
5
+
6
+ module Bolt
7
+ module Transport
8
+ class Podman < Docker
9
+ class Connection < Connection
10
+ attr_reader :user, :target
11
+
12
+ def initialize(target)
13
+ raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
14
+ @target = target
15
+ @user = ENV['USER'] || Etc.getlogin
16
+ @logger = Bolt::Logger.logger(target.safe_name)
17
+ @container_info = {}
18
+ @logger.trace("Initializing podman connection to #{target.safe_name}")
19
+ end
20
+
21
+ def run_cmd(cmd, env_vars)
22
+ Bolt::Util.exec_podman(cmd, env_vars)
23
+ end
24
+
25
+ def shell
26
+ @shell ||= if Bolt::Util.windows?
27
+ Bolt::Shell::Powershell.new(target, self)
28
+ else
29
+ Bolt::Shell::Bash.new(target, self)
30
+ end
31
+ end
32
+
33
+ def connect
34
+ # We don't actually have a connection, but we do need to
35
+ # check that the container exists and is running.
36
+ ps = execute_local_json_command('ps')
37
+ container = Array(ps).find { |item|
38
+ item["ID"].to_s.eql?(@target.host) ||
39
+ item["Id"].to_s.start_with?(@target.host) ||
40
+ Array(item["Names"]).include?(@target.host)
41
+ }
42
+ raise "Could not find a container with name or ID matching '#{@target.host}'" if container.nil?
43
+ # Now find the indepth container information
44
+ id = container["ID"] || container["Id"]
45
+ output = execute_local_json_command('inspect', [id])
46
+ # Store the container information for later
47
+ @container_info = output.first
48
+ @logger.trace { "Opened session" }
49
+ true
50
+ rescue StandardError => e
51
+ raise Bolt::Node::ConnectError.new(
52
+ "Failed to connect to #{target.safe_name}: #{e.message}",
53
+ 'CONNECT_ERROR'
54
+ )
55
+ end
56
+
57
+ # Executes a command inside the target container. This is called from the shell class.
58
+ #
59
+ # @param command [string] The command to run
60
+ def execute(command)
61
+ args = []
62
+ args += %w[--interactive]
63
+ args += %w[--tty] if target.options['tty']
64
+ args += @env_vars if @env_vars
65
+
66
+ if target.options['shell-command'] && !target.options['shell-command'].empty?
67
+ # escape any double quotes in command
68
+ command = command.gsub('"', '\"')
69
+ command = "#{target.options['shell-command']} \"#{command}\""
70
+ end
71
+
72
+ podman_command = %w[podman exec] + args + [container_id] + Shellwords.split(command)
73
+ @logger.trace { "Executing: #{podman_command.join(' ')}" }
74
+
75
+ Open3.popen3(*podman_command)
76
+ rescue StandardError
77
+ @logger.trace { "Command aborted" }
78
+ raise
79
+ end
80
+
81
+ # Converts the JSON encoded STDOUT string from the podman cli into ruby objects
82
+ #
83
+ # @param stdout [String] The string to convert
84
+ # @return [Object] Ruby object representation of the JSON string
85
+ private def extract_json(stdout)
86
+ # Podman renders the output in pretty JSON, which results in a newline
87
+ # appearing in the output before the closing bracket.
88
+ # should we only get a single line with no newline at all, we also
89
+ # assume it is a single minified JSON object
90
+ stdout.strip!
91
+ newline = stdout.index("\n") || -1
92
+ bracket = stdout.index('}') || -1
93
+ JSON.parse(stdout) if bracket > newline
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -230,7 +230,7 @@ module Bolt
230
230
  end
231
231
  [in_wr, out_rd, err_rd, th]
232
232
  rescue Errno::EMFILE => e
233
- msg = "#{e.message}. This may be resolved by increasing your user limit "\
233
+ msg = "#{e.message}. This might be resolved by increasing your user limit "\
234
234
  "with 'ulimit -n 1024'. See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
235
235
  raise Bolt::Error.new(msg, 'bolt/too-many-files')
236
236
  end
@@ -130,7 +130,7 @@ module Bolt
130
130
 
131
131
  [inp, out_rd, err_rd, th]
132
132
  rescue Errno::EMFILE => e
133
- msg = "#{e.message}. This may be resolved by increasing your user limit "\
133
+ msg = "#{e.message}. This might be resolved by increasing your user limit "\
134
134
  "with 'ulimit -n 1024'. See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
135
135
  raise Bolt::Error.new(msg, 'bolt/too-many-files')
136
136
  rescue StandardError
data/lib/bolt/util.rb CHANGED
@@ -77,6 +77,14 @@ module Bolt
77
77
  File.exist?(path) ? read_yaml_hash(path, file_name) : {}
78
78
  end
79
79
 
80
+ def first_runs_free
81
+ Bolt::Config.user_path + '.first_runs_free'
82
+ end
83
+
84
+ def first_run?
85
+ Bolt::Config.user_path && !File.exist?(first_runs_free)
86
+ end
87
+
80
88
  # Accepts a path with either 'plans' or 'tasks' in it and determines
81
89
  # the name of the module
82
90
  def module_name(path)
@@ -324,6 +332,40 @@ module Bolt
324
332
  end
325
333
  end
326
334
 
335
+ # Executes a Docker CLI command. This is useful for running commands as
336
+ # part of this class without having to go through the `execute`
337
+ # function and manage pipes.
338
+ #
339
+ # @param cmd [String] The docker command and arguments to run
340
+ # e.g. 'cp <src> <dest>' for `docker cp <src> <dest>`
341
+ # @return [String, String, Process::Status] The output of the command: STDOUT, STDERR, Process Status
342
+ def exec_docker(cmd, env = {})
343
+ Open3.capture3(env, 'docker', *cmd, { binmode: true })
344
+ end
345
+
346
+ # Executes a Podman CLI command. This is useful for running commands as
347
+ # part of this class without having to go through the `execute`
348
+ # function and manage pipes.
349
+ #
350
+ # @param cmd [String] The podman command and arguments to run
351
+ # e.g. 'cp <src> <dest>' for `podman cp <src> <dest>`
352
+ # @return [String, String, Process::Status] The output of the command: STDOUT, STDERR, Process Status
353
+ def exec_podman(cmd, env = {})
354
+ Open3.capture3(env, 'podman', *cmd, { binmode: true })
355
+ end
356
+
357
+ # Formats a map of environment variables to be passed to a command that
358
+ # accepts repeated `--env` flags
359
+ #
360
+ # @param env_vars [Hash] A map of environment variables keys and their values
361
+ # @return [String]
362
+ def format_env_vars_for_cli(env_vars)
363
+ @env_vars = env_vars.each_with_object([]) do |(key, value), acc|
364
+ acc << "--env"
365
+ acc << "#{key}=#{value}"
366
+ end
367
+ end
368
+
327
369
  def unix_basename(path)
328
370
  raise Bolt::ValidationError, "path must be a String, received #{path.class} #{path}" unless path.is_a?(String)
329
371
  path.split('/').last
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.0.1'
4
+ VERSION = '3.6.0'
5
5
  end
@@ -133,7 +133,14 @@ module BoltServer
133
133
  task_data = body['task']
134
134
  task = Bolt::Task::PuppetServer.new(task_data['name'], task_data['metadata'], task_data['files'], @file_cache)
135
135
  parameters = body['parameters'] || {}
136
- [@executor.run_task(target, task, parameters), nil]
136
+ task_result = @executor.run_task(target, task, parameters)
137
+ task_result.each do |result|
138
+ value = result.value
139
+ next unless value.is_a?(Hash)
140
+ next unless value.key?('_sensitive')
141
+ value['_sensitive'] = value['_sensitive'].unwrap
142
+ end
143
+ [task_result, nil]
137
144
  end
138
145
 
139
146
  def run_command(target, body)
@@ -275,15 +282,19 @@ module BoltServer
275
282
  Bolt::Config.from_project(project, { log: { 'bolt-debug.log' => 'disable' } })
276
283
  end
277
284
 
285
+ def pal_from_project_bolt_config(bolt_config)
286
+ modulepath_object = Bolt::Config::Modulepath.new(
287
+ bolt_config.modulepath,
288
+ boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH],
289
+ builtin_content_path: @config['builtin-content-dir']
290
+ )
291
+ Bolt::PAL.new(modulepath_object, nil, nil, nil, nil, nil, bolt_config.project)
292
+ end
293
+
278
294
  def in_bolt_project(versioned_project)
279
295
  @pal_mutex.synchronize do
280
296
  bolt_config = config_from_project(versioned_project)
281
- modulepath_object = Bolt::Config::Modulepath.new(
282
- bolt_config.modulepath,
283
- boltlib_path: [PE_BOLTLIB_PATH, Bolt::Config::Modulepath::BOLTLIB_PATH],
284
- builtin_content_path: @config['builtin-content-dir']
285
- )
286
- pal = Bolt::PAL.new(modulepath_object, nil, nil, nil, nil, nil, bolt_config.project)
297
+ pal = pal_from_project_bolt_config(bolt_config)
287
298
  context = {
288
299
  pal: pal,
289
300
  config: bolt_config
@@ -351,8 +362,8 @@ module BoltServer
351
362
  }
352
363
  end
353
364
 
354
- def allowed_helper(metadata, allowlist)
355
- allowed = allowlist.nil? || allowlist.include?(metadata['name'])
365
+ def allowed_helper(pal, metadata, allowlist)
366
+ allowed = !pal.filter_content([metadata['name']], allowlist).empty?
356
367
  metadata.merge({ 'allowed' => allowed })
357
368
  end
358
369
 
@@ -366,21 +377,27 @@ module BoltServer
366
377
  plans.map { |plan_name| { 'name' => plan_name } }
367
378
  end
368
379
 
369
- def file_metadatas(pal, module_name, file)
370
- pal.in_bolt_compiler do
371
- mod = Puppet.lookup(:current_environment).module(module_name)
372
- raise ArgumentError, "`module_name`: #{module_name} does not exist" unless mod
373
- abs_file_path = mod.file(file)
374
- raise ArgumentError, "`file`: #{file} does not exist inside the module's 'files' directory" unless abs_file_path
375
- fileset = Puppet::FileServing::Fileset.new(abs_file_path, 'recurse' => 'yes')
376
- Puppet::FileServing::Fileset.merge(fileset).collect do |relative_file_path, base_path|
377
- metadata = Puppet::FileServing::Metadata.new(base_path, relative_path: relative_file_path)
378
- metadata.checksum_type = 'sha256'
379
- metadata.links = 'follow'
380
- metadata.collect
381
- metadata.to_data_hash
380
+ def file_metadatas(versioned_project, module_name, file)
381
+ abs_file_path = @pal_mutex.synchronize do
382
+ bolt_config = config_from_project(versioned_project)
383
+ pal = pal_from_project_bolt_config(bolt_config)
384
+ pal.in_bolt_compiler do
385
+ mod = Puppet.lookup(:current_environment).module(module_name)
386
+ raise ArgumentError, "`module_name`: #{module_name} does not exist" unless mod
387
+ mod.file(file)
382
388
  end
383
389
  end
390
+
391
+ raise ArgumentError, "`file`: #{file} does not exist inside the module's 'files' directory" unless abs_file_path
392
+
393
+ fileset = Puppet::FileServing::Fileset.new(abs_file_path, 'recurse' => 'yes')
394
+ Puppet::FileServing::Fileset.merge(fileset).collect do |relative_file_path, base_path|
395
+ metadata = Puppet::FileServing::Metadata.new(base_path, relative_path: relative_file_path)
396
+ metadata.checksum_type = 'sha256'
397
+ metadata.links = 'follow'
398
+ metadata.collect
399
+ metadata.to_data_hash
400
+ end
384
401
  end
385
402
 
386
403
  get '/' do
@@ -520,7 +537,7 @@ module BoltServer
520
537
  return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
521
538
  in_bolt_project(params['versioned_project']) do |context|
522
539
  plan_info = pe_plan_info(context[:pal], params[:module_name], params[:plan_name])
523
- plan_info = allowed_helper(plan_info, context[:config].project.plans)
540
+ plan_info = allowed_helper(context[:pal], plan_info, context[:config].project.plans)
524
541
  [200, plan_info.to_json]
525
542
  end
526
543
  rescue Bolt::Error => e
@@ -550,7 +567,7 @@ module BoltServer
550
567
  'versioned_project' => params['versioned_project']
551
568
  }
552
569
  task_info = pe_task_info(context[:pal], params[:module_name], params[:task_name], ps_parameters)
553
- task_info = allowed_helper(task_info, context[:config].project.tasks)
570
+ task_info = allowed_helper(context[:pal], task_info, context[:config].project.tasks)
554
571
  [200, task_info.to_json]
555
572
  end
556
573
  rescue Bolt::Error => e
@@ -590,7 +607,7 @@ module BoltServer
590
607
  plans_response = plan_list(context[:pal])
591
608
 
592
609
  # Dig in context for the allowlist of plans from project object
593
- plans_response.map! { |metadata| allowed_helper(metadata, context[:config].project.plans) }
610
+ plans_response.map! { |metadata| allowed_helper(context[:pal], metadata, context[:config].project.plans) }
594
611
 
595
612
  # We structure this array of plans to be an array of hashes so that it matches the structure
596
613
  # returned by the puppetserver API that serves data like this. Structuring the output this way
@@ -626,7 +643,7 @@ module BoltServer
626
643
  tasks_response = task_list(context[:pal])
627
644
 
628
645
  # Dig in context for the allowlist of tasks from project object
629
- tasks_response.map! { |metadata| allowed_helper(metadata, context[:config].project.tasks) }
646
+ tasks_response.map! { |metadata| allowed_helper(context[:pal], metadata, context[:config].project.tasks) }
630
647
 
631
648
  # We structure this array of tasks to be an array of hashes so that it matches the structure
632
649
  # returned by the puppetserver API that serves data like this. Structuring the output this way
@@ -642,12 +659,11 @@ module BoltServer
642
659
  #
643
660
  # @param versioned_project [String] the versioned_project to fetch the file metadatas from
644
661
  get '/project_file_metadatas/:module_name/*' do
645
- return MISSING_VERSIONED_PROJECT_RESPONSE if params['versioned_project'].nil?
646
- in_bolt_project(params['versioned_project']) do |context|
647
- file = params[:splat].first
648
- metadatas = file_metadatas(context[:pal], params[:module_name], file)
649
- [200, metadatas.to_json]
650
- end
662
+ versioned_project = params['versioned_project']
663
+ return MISSING_VERSIONED_PROJECT_RESPONSE if versioned_project.nil?
664
+ file = params[:splat].first
665
+ metadatas = file_metadatas(versioned_project, params[:module_name], file)
666
+ [200, metadatas.to_json]
651
667
  rescue Bolt::Error => e
652
668
  [400, e.to_json]
653
669
  rescue ArgumentError => e
@@ -674,11 +690,26 @@ module BoltServer
674
690
  Bolt::Util.validate_file('inventory file', context[:config].project.inventory_file)
675
691
 
676
692
  begin
693
+ # Set the default puppet_library plugin hook if it has not already been
694
+ # set
695
+ context[:config].data['plugin-hooks']['puppet_library'] ||= {
696
+ 'plugin' => 'task',
697
+ 'task' => 'puppet_agent::install',
698
+ 'parameters' => {
699
+ 'stop_service' => true
700
+ }
701
+ }
702
+
677
703
  connect_plugin = BoltServer::Plugin::PuppetConnectData.new(body['puppet_connect_data'])
678
704
  plugins = Bolt::Plugin.setup(context[:config], context[:pal], load_plugins: false)
679
705
  plugins.add_plugin(connect_plugin)
706
+ %w[aws_inventory azure_inventory gcloud_inventory].each do |plugin_name|
707
+ plugins.add_module_plugin(plugin_name) if plugins.known_plugin?(plugin_name)
708
+ end
680
709
  inventory = Bolt::Inventory.from_config(context[:config], plugins)
681
- target_list = inventory.get_targets('all').map { |targ| targ.to_h.merge({ 'transport' => targ.transport }) }
710
+ target_list = inventory.get_targets('all').map do |targ|
711
+ targ.to_h.merge({ 'transport' => targ.transport, 'plugin_hooks' => targ.plugin_hooks })
712
+ end
682
713
  rescue Bolt::Plugin::PluginError::LoadingDisabled => e
683
714
  msg = "Cannot load plugin #{e.details['plugin_name']}: plugin not supported"
684
715
  raise BoltServer::Plugin::PluginNotSupported.new(msg, e.details['plugin_name'])