bolt 3.22.0 → 3.23.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: f5ed54ef60335c8ef24f0e5c76b3dbb669d2e4c83a36744d8d2efe090c2b72b0
4
- data.tar.gz: 703e8adaa6f23ae4ac180370f4905844cb573b37c5449066e110167095fe15e3
3
+ metadata.gz: 67f08b10db67ef79aadf22a542f6564aa502743f53619f41c40aff4761e2e0c0
4
+ data.tar.gz: 81d8b927c1349299ba568358a2f155d624fb28f7b65ae5985bcfc8b6fe24eb6b
5
5
  SHA512:
6
- metadata.gz: 2dfb2cf0b51cf238193d4e7b86933010d05a4985bc4df010c80dbcb9f8117d6dbb4359e81bacde3d567f61aae3b986d1d2e8c0c76a466c26a298bdc41c925bbf
7
- data.tar.gz: fad359e9877901bebf721f2945425c62815bccfd5564125c45a545f3e45907d7fcb81a7d670c8cc54585596e173a4ebc960db5601495d69cebc0b5f449b368e1
6
+ metadata.gz: a3e66f8d5adc727228b09e212de34c7de796c556a0d91153e1599cbf619b30b98c9358ea3683c2e00650375ecac8745fdf7a79977a42d6761967ce98c5c2ac71
7
+ data.tar.gz: a6dedd218bc2db8e5365906366a1cbeef5e99b02f606c80677081eccc71172fa7f15f3798630847239228feafc861278938044e98b2107429c8e34cea7143f9c
data/Puppetfile CHANGED
@@ -5,14 +5,14 @@ forge "http://forge.puppetlabs.com"
5
5
  moduledir File.join(File.dirname(__FILE__), 'modules')
6
6
 
7
7
  # Core modules used by 'apply'
8
- mod 'puppetlabs-service', '2.1.0'
9
- mod 'puppetlabs-puppet_agent', '4.9.0'
8
+ mod 'puppetlabs-service', '2.2.0'
9
+ mod 'puppetlabs-puppet_agent', '4.11.0'
10
10
  mod 'puppetlabs-facts', '1.4.0'
11
11
 
12
12
  # Core types and providers for Puppet 6
13
13
  mod 'puppetlabs-augeas_core', '1.2.0'
14
14
  mod 'puppetlabs-host_core', '1.1.0'
15
- mod 'puppetlabs-scheduled_task', '3.0.1'
15
+ mod 'puppetlabs-scheduled_task', '3.1.0'
16
16
  mod 'puppetlabs-sshkeys_core', '2.3.0'
17
17
  mod 'puppetlabs-zfs_core', '1.3.0'
18
18
  mod 'puppetlabs-cron_core', '1.1.0'
@@ -22,14 +22,14 @@ mod 'puppetlabs-yumrepo_core', '1.1.0'
22
22
  mod 'puppetlabs-zone_core', '1.0.3'
23
23
 
24
24
  # Useful additional modules
25
- mod 'puppetlabs-package', '2.1.0'
25
+ mod 'puppetlabs-package', '2.2.0'
26
26
  mod 'puppetlabs-powershell_task_helper', '0.1.0'
27
- mod 'puppetlabs-puppet_conf', '1.2.0'
27
+ mod 'puppetlabs-puppet_conf', '1.3.0'
28
28
  mod 'puppetlabs-python_task_helper', '0.5.0'
29
29
  mod 'puppetlabs-reboot', '4.1.0'
30
- mod 'puppetlabs-ruby_task_helper', '0.6.0'
30
+ mod 'puppetlabs-ruby_task_helper', '0.6.1'
31
31
  mod 'puppetlabs-ruby_plugin_helper', '0.2.0'
32
- mod 'puppetlabs-stdlib', '8.1.0'
32
+ mod 'puppetlabs-stdlib', '8.2.0'
33
33
 
34
34
  # Plugin modules
35
35
  mod 'puppetlabs-aws_inventory', '0.7.0'
@@ -17,6 +17,8 @@ require 'bolt/error'
17
17
  # > **Note:** Not available in apply block
18
18
  #
19
19
  Puppet::Functions.create_function(:puppetdb_command) do
20
+ # Send a command with a payload to PuppetDB.
21
+ #
20
22
  # @param command The command to invoke.
21
23
  # @param version The version of the command to invoke.
22
24
  # @param payload The payload to the command.
@@ -38,7 +40,36 @@ Puppet::Functions.create_function(:puppetdb_command) do
38
40
  return_type 'String'
39
41
  end
40
42
 
43
+ # Send a command with a payload to a named PuppetDB instance.
44
+ #
45
+ # @param command The command to invoke.
46
+ # @param version The version of the command to invoke.
47
+ # @param payload The payload to the command.
48
+ # @param instance The PuppetDB instance to send the command to.
49
+ # @return The UUID identifying the response sent by PuppetDB.
50
+ # @example Replace facts for a target using a named PuppetDB instance
51
+ # $payload = {
52
+ # 'certname' => 'localhost',
53
+ # 'environment' => 'dev',
54
+ # 'producer' => 'bolt',
55
+ # 'producer_timestamp' => '1970-01-01',
56
+ # 'values' => { 'orchestrator' => 'bolt' }
57
+ # }
58
+ #
59
+ # puppetdb_command('replace_facts', 5, $payload, 'instance-1')
60
+ dispatch :puppetdb_command_with_instance do
61
+ param 'String[1]', :command
62
+ param 'Integer', :version
63
+ param 'Hash[Data, Data]', :payload
64
+ param 'String', :instance
65
+ return_type 'String'
66
+ end
67
+
41
68
  def puppetdb_command(command, version, payload)
69
+ puppetdb_command_with_instance(command, version, payload, nil)
70
+ end
71
+
72
+ def puppetdb_command_with_instance(command, version, payload, instance)
42
73
  # Disallow in apply blocks.
43
74
  unless Puppet[:tasks]
44
75
  raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
@@ -61,6 +92,6 @@ Puppet::Functions.create_function(:puppetdb_command) do
61
92
  )
62
93
  end
63
94
 
64
- puppetdb_client.send_command(command, version, payload)
95
+ puppetdb_client.send_command(command, version, payload, instance)
65
96
  end
66
97
  end
@@ -7,6 +7,8 @@ require 'bolt/error'
7
7
  # If a node is not found in PuppetDB, it's included in the returned hash with an empty facts hash.
8
8
  # Otherwise, the node is included in the hash with a value that is a hash of its facts.
9
9
  Puppet::Functions.create_function(:puppetdb_fact) do
10
+ # Collect facts from PuppetDB.
11
+ #
10
12
  # @param certnames Array of certnames.
11
13
  # @return A hash of certname to facts hash for each matched Target.
12
14
  # @example Get facts for nodes
@@ -16,13 +18,30 @@ Puppet::Functions.create_function(:puppetdb_fact) do
16
18
  return_type 'Hash[String, Data]'
17
19
  end
18
20
 
21
+ # Collects facts from a named PuppetDB instance.
22
+ #
23
+ # @param certnames Array of certnames.
24
+ # @param instance The PuppetDB instance to query.
25
+ # @return A hash of certname to facts hash for each matched Target.
26
+ # @example Get facts for nodes from a named PuppetDB instance
27
+ # puppetdb_fact(['app.example.com', 'db.example.com'], 'instance-1')
28
+ dispatch :puppetdb_fact_with_instance do
29
+ param 'Array[String]', :certnames
30
+ param 'String', :instance
31
+ return_type 'Hash[String, Data]'
32
+ end
33
+
19
34
  def puppetdb_fact(certnames)
35
+ puppetdb_fact_with_instance(certnames, nil)
36
+ end
37
+
38
+ def puppetdb_fact_with_instance(certnames, instance)
20
39
  puppetdb_client = Puppet.lookup(:bolt_pdb_client)
21
40
  # Bolt executor not expected when invoked from apply block
22
41
  executor = Puppet.lookup(:bolt_executor) { nil }
23
42
  # Send Analytics Report
24
43
  executor&.report_function_call(self.class.name)
25
44
 
26
- puppetdb_client.facts_for_node(certnames)
45
+ puppetdb_client.facts_for_node(certnames, instance)
27
46
  end
28
47
  end
@@ -6,6 +6,8 @@ require 'bolt/error'
6
6
  # using Bolt's PuppetDB client.
7
7
  Puppet::Functions.create_function(:puppetdb_query) do
8
8
  # rubocop:disable Layout/LineLength
9
+ # Make a query to PuppetDB.
10
+ #
9
11
  # @param query A PQL query.
10
12
  # Learn more about [Puppet's query language](https://puppet.com/docs/puppetdb/latest/api/query/tutorial-pql.html), PQL.
11
13
  # @return Results of the PuppetDB query.
@@ -16,15 +18,35 @@ Puppet::Functions.create_function(:puppetdb_query) do
16
18
  param 'Variant[String, Array[Data]]', :query
17
19
  return_type 'Array[Data]'
18
20
  end
21
+
22
+ # rubocop:disable Layout/LineLength
23
+ # Make a query to a named PuppetDB instance.
24
+ #
25
+ # @param query A PQL query.
26
+ # Learn more about [Puppet's query language](https://puppet.com/docs/puppetdb/latest/api/query/tutorial-pql.html), PQL.
27
+ # @param instance The PuppetDB instance to query.
28
+ # @return Results of the PuppetDB query.
29
+ # @example Request certnames for all nodes using a named PuppetDB instance
30
+ # puppetdb_query('nodes[certname] {}', 'instance-1')
31
+ # rubocop:enable Layout/LineLength
32
+ dispatch :make_query_with_instance do
33
+ param 'Variant[String, Array[Data]]', :query
34
+ param 'String', :instance
35
+ return_type 'Array[Data]'
36
+ end
19
37
  # The query type could be more specific ASTQuery = Array[Variant[String, ASTQuery]]
20
38
 
21
39
  def make_query(query)
40
+ make_query_with_instance(query, nil)
41
+ end
42
+
43
+ def make_query_with_instance(query, instance)
22
44
  puppetdb_client = Puppet.lookup(:bolt_pdb_client)
23
45
  # Bolt executor not expected when invoked from apply block
24
46
  executor = Puppet.lookup(:bolt_executor) { nil }
25
47
  # Send Analytics Report
26
48
  executor&.report_function_call(self.class.name)
27
49
 
28
- puppetdb_client.make_query(query)
50
+ puppetdb_client.make_query(query, nil, instance)
29
51
  end
30
52
  end
@@ -57,6 +57,7 @@ Puppet::Functions.create_function(:run_task) do
57
57
 
58
58
  options, params = args.partition { |k, _v| k.start_with?('_') }.map(&:to_h)
59
59
  options = options.transform_keys { |k| k.sub(/^_/, '').to_sym }
60
+ options[:description] = description if description
60
61
 
61
62
  executor = Puppet.lookup(:bolt_executor)
62
63
  inventory = Puppet.lookup(:bolt_inventory)
@@ -68,18 +69,24 @@ Puppet::Functions.create_function(:run_task) do
68
69
  executor.report_function_call(self.class.name)
69
70
  end
70
71
 
71
- # Report Analytics for bundled content, this should capture tasks run from both CLI and Plans
72
+ # Report Analytics for bundled content, this should capture tasks run from
73
+ # both CLI and Plans.
72
74
  executor.report_bundled_content('Task', task_name)
73
75
 
74
- # Ensure that given targets are all Target instances
76
+ # Ensure that given targets are all Target instances.
75
77
  targets = inventory.get_targets(targets)
76
78
 
77
- options[:description] = description if description
79
+ # Return early if there are no targets.
80
+ if targets.empty?
81
+ return Bolt::ResultSet.new([])
82
+ end
78
83
 
79
- # Don't bother loading the local task definition if all targets use the 'pcp' transport.
80
- if !targets.empty? && targets.all? { |t| t.transport == 'pcp' }
81
- # create a fake task
82
- task = Bolt::Task.new(task_name, {}, [{ 'name' => '', 'path' => '' }])
84
+ # If all targets use the PCP transport, create a fake task instead of
85
+ # loading the actual task definition.
86
+ if targets.all? { |t| t.transport == 'pcp' }
87
+ task = Bolt::Task.new(task_name,
88
+ { 'supports_noop' => true },
89
+ [{ 'name' => '', 'path' => '' }])
83
90
  else
84
91
  # TODO: use the compiler injection once PUP-8237 lands
85
92
  task_signature = Puppet::Pal::ScriptCompiler.new(closure_scope.compiler).task_signature(task_name)
@@ -89,7 +96,8 @@ Puppet::Functions.create_function(:run_task) do
89
96
 
90
97
  task = Bolt::Task.from_task_signature(task_signature)
91
98
 
92
- # Set the default value for any params that have one and were not provided or are undef
99
+ # Set the default value for any params that have one and were not provided
100
+ # or are undef.
93
101
  params = task.parameter_defaults.merge(params) do |_, default, passed|
94
102
  passed.nil? ? default : passed
95
103
  end
@@ -99,9 +107,9 @@ Puppet::Functions.create_function(:run_task) do
99
107
  end || (raise with_stack(:TYPE_MISMATCH, 'Task parameters do not match'))
100
108
  end
101
109
 
110
+ # Generate a helpful error message about the type-mismatch between the type
111
+ # Data and the actual type of params.
102
112
  unless Puppet::Pops::Types::TypeFactory.data.instance?(params)
103
- # generate a helpful error message about the type-mismatch between the type Data
104
- # and the actual type of params
105
113
  params_t = Puppet::Pops::Types::TypeCalculator.infer_set(params)
106
114
  desc = Puppet::Pops::Types::TypeMismatchDescriber.singleton.describe_mismatch(
107
115
  'Task parameters are not of type Data. run_task()',
@@ -133,23 +141,20 @@ Puppet::Functions.create_function(:run_task) do
133
141
  # Report whether the task was run in noop mode.
134
142
  executor.report_noop_mode(executor.noop || options[:noop])
135
143
 
136
- if targets.empty?
137
- Bolt::ResultSet.new([])
138
- else
139
- file_line = Puppet::Pops::PuppetStack.top_of_stack
140
- result = if executor.in_parallel?
141
- executor.run_in_thread do
142
- executor.run_task(targets, task, params, options, file_line)
143
- end
144
- else
144
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
145
+ result = if executor.in_parallel?
146
+ executor.run_in_thread do
145
147
  executor.run_task(targets, task, params, options, file_line)
146
148
  end
149
+ else
150
+ executor.run_task(targets, task, params, options, file_line)
151
+ end
147
152
 
148
- if !result.ok && !options[:catch_errors]
149
- raise Bolt::RunFailure.new(result, 'run_task', task_name)
150
- end
151
- result
153
+ if !result.ok && !options[:catch_errors]
154
+ raise Bolt::RunFailure.new(result, 'run_task', task_name)
152
155
  end
156
+
157
+ result
153
158
  end
154
159
 
155
160
  def with_stack(kind, msg)
@@ -92,10 +92,17 @@ Puppet::Functions.create_function(:run_task_with) do
92
92
  # Get all the targets
93
93
  targets = Array(inventory.get_targets(targets))
94
94
 
95
+ # Return early if there are no targets.
96
+ if targets.empty?
97
+ return Bolt::ResultSet.new([])
98
+ end
99
+
95
100
  # If all targets use the 'pcp' transport, use a fake task instead of loading the local definition
96
101
  # Otherwise, load the local task definition
97
- if (pcp_only = targets.any? && targets.all? { |t| t.transport == 'pcp' })
98
- task = Bolt::Task.new(task_name, {}, [{ 'name' => '', 'path' => '' }])
102
+ if (pcp_only = targets.all? { |t| t.transport == 'pcp' })
103
+ task = Bolt::Task.new(task_name,
104
+ { 'supports_noop' => true },
105
+ [{ 'name' => '', 'path' => '' }])
99
106
  else
100
107
  task_signature = Puppet::Pal::ScriptCompiler.new(closure_scope.compiler).task_signature(task_name)
101
108
 
@@ -178,27 +185,23 @@ Puppet::Functions.create_function(:run_task_with) do
178
185
  # Report whether the task was run in noop mode.
179
186
  executor.report_noop_mode(executor.noop || options[:noop])
180
187
 
181
- if targets.empty?
182
- Bolt::ResultSet.new([])
183
- else
184
- # Combine the results from the task run with any failing results that were
185
- # generated earlier when creating the target mapping
186
- file_line = Puppet::Pops::PuppetStack.top_of_stack
187
- task_result = if executor.in_parallel?
188
- executor.run_in_thread do
189
- executor.run_task_with(target_mapping, task, options, file_line)
190
- end
191
- else
188
+ # Combine the results from the task run with any failing results that were
189
+ # generated earlier when creating the target mapping
190
+ file_line = Puppet::Pops::PuppetStack.top_of_stack
191
+ task_result = if executor.in_parallel?
192
+ executor.run_in_thread do
192
193
  executor.run_task_with(target_mapping, task, options, file_line)
193
194
  end
194
- result = Bolt::ResultSet.new(task_result.results + error_set)
195
-
196
- if !result.ok && !options[:catch_errors]
197
- raise Bolt::RunFailure.new(result, 'run_task', task_name)
198
- end
195
+ else
196
+ executor.run_task_with(target_mapping, task, options, file_line)
197
+ end
198
+ result = Bolt::ResultSet.new(task_result.results + error_set)
199
199
 
200
- result
200
+ if !result.ok && !options[:catch_errors]
201
+ raise Bolt::RunFailure.new(result, 'run_task', task_name)
201
202
  end
203
+
204
+ result
202
205
  end
203
206
 
204
207
  def with_stack(kind, msg)
@@ -162,7 +162,13 @@ module Bolt
162
162
 
163
163
  def validate_hiera_config(hiera_config)
164
164
  if File.exist?(File.path(hiera_config))
165
- data = File.open(File.path(hiera_config), "r:UTF-8") { |f| YAML.safe_load(f.read, [Symbol]) }
165
+ data = File.open(File.path(hiera_config), "r:UTF-8") do |f|
166
+ if Psych.method(:safe_load).parameters.rassoc(:permitted_classes)
167
+ YAML.safe_load(f.read, permitted_classes: [Symbol])
168
+ else
169
+ YAML.safe_load(f.read, [Symbol])
170
+ end
171
+ end
166
172
  if data.nil?
167
173
  return nil
168
174
  elsif data['version'] != 5
@@ -219,7 +225,7 @@ module Bolt
219
225
  code_ast: ast,
220
226
  modulepath: @modulepath,
221
227
  project: @project.to_h,
222
- pdb_config: @pdb_client.config.to_hash,
228
+ pdb_config: @pdb_client.instance(options[:puppetdb]).config.to_hash,
223
229
  hiera_config: @hiera_config,
224
230
  plan_vars: plan_vars,
225
231
  # This data isn't available on the target config hash
@@ -10,7 +10,7 @@ module Bolt
10
10
  OPTIONS = { inventory: %w[targets query rerun],
11
11
  authentication: %w[user password password-prompt private-key host-key-check ssl ssl-verify],
12
12
  escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
13
- run_context: %w[concurrency inventoryfile save-rerun cleanup],
13
+ run_context: %w[concurrency inventoryfile save-rerun cleanup puppetdb],
14
14
  global_config_setters: PROJECT_PATHS + %w[modulepath],
15
15
  transports: %w[transport connect-timeout tty native-ssh ssh-command copy-command],
16
16
  display: %w[format color verbose trace stream],
@@ -1054,6 +1054,9 @@ module Bolt
1054
1054
  define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save|
1055
1055
  @options[:'save-rerun'] = save
1056
1056
  end
1057
+ define('--puppetdb INSTANCE', 'The named PuppetDB instance to connect to by default.') do |instance|
1058
+ @options[:default_puppetdb] = instance
1059
+ end
1057
1060
 
1058
1061
  separator "\n#{self.class.colorize(:cyan, 'Remote environment options')}"
1059
1062
  define('--env-var ENVIRONMENT_VARIABLES', 'Environment variables to set on the target.') do |envvar|
data/lib/bolt/catalog.rb CHANGED
@@ -56,7 +56,7 @@ module Bolt
56
56
  end
57
57
 
58
58
  def compile_catalog(request)
59
- pdb_client = Bolt::PuppetDB::Client.new(Bolt::PuppetDB::Config.new(request['pdb_config']))
59
+ pdb_client = Bolt::PuppetDB::Client.new(config: request['pdb_config'])
60
60
  project = request['project']
61
61
  bolt_project = Struct.new(:name, :path, :load_as_module?).new(project['name'],
62
62
  project['path'],
@@ -54,6 +54,58 @@ module Bolt
54
54
  }
55
55
  }.freeze
56
56
 
57
+ # PuppetDB options.
58
+ PUPPETDB_OPTIONS = {
59
+ "cacert" => {
60
+ description: "The path to the ca certificate for PuppetDB.",
61
+ type: String,
62
+ _example: "/etc/puppetlabs/puppet/ssl/certs/ca.pem",
63
+ _plugin: true
64
+ },
65
+ "cert" => {
66
+ description: "The path to the client certificate file to use for authentication.",
67
+ type: String,
68
+ _example: "/etc/puppetlabs/puppet/ssl/certs/my-host.example.com.pem",
69
+ _plugin: true
70
+ },
71
+ "connect_timeout" => {
72
+ description: "How long to wait in seconds when establishing connections with PuppetDB.",
73
+ type: Integer,
74
+ minimum: 1,
75
+ _default: 60,
76
+ _example: 120,
77
+ _plugin: true
78
+ },
79
+ "key" => {
80
+ description: "The private key for the certificate.",
81
+ type: String,
82
+ _example: "/etc/puppetlabs/puppet/ssl/private_keys/my-host.example.com.pem",
83
+ _plugin: true
84
+ },
85
+ "read_timeout" => {
86
+ description: "How long to wait in seconds for a response from PuppetDB.",
87
+ type: Integer,
88
+ minimum: 1,
89
+ _default: 60,
90
+ _example: 120,
91
+ _plugin: true
92
+ },
93
+ "server_urls" => {
94
+ description: "An array containing the PuppetDB host to connect to. Include the protocol `https` "\
95
+ "and the port, which is usually `8081`. For example, "\
96
+ "`https://my-puppetdb-server.com:8081`.",
97
+ type: Array,
98
+ _example: ["https://puppet.example.com:8081"],
99
+ _plugin: true
100
+ },
101
+ "token" => {
102
+ description: "The path to the PE RBAC Token.",
103
+ type: String,
104
+ _example: "~/.puppetlabs/token",
105
+ _plugin: true
106
+ }
107
+ }.freeze
108
+
57
109
  # Definitions used to validate config options.
58
110
  # https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
59
111
  OPTIONS = {
@@ -409,55 +461,17 @@ module Bolt
409
461
  description: "A map containing options for [configuring the Bolt PuppetDB "\
410
462
  "client](bolt_connect_puppetdb.md).",
411
463
  type: Hash,
412
- properties: {
413
- "cacert" => {
414
- description: "The path to the ca certificate for PuppetDB.",
415
- type: String,
416
- _example: "/etc/puppetlabs/puppet/ssl/certs/ca.pem",
417
- _plugin: true
418
- },
419
- "cert" => {
420
- description: "The path to the client certificate file to use for authentication.",
421
- type: String,
422
- _example: "/etc/puppetlabs/puppet/ssl/certs/my-host.example.com.pem",
423
- _plugin: true
424
- },
425
- "connect_timeout" => {
426
- description: "How long to wait in seconds when establishing connections with PuppetDB.",
427
- type: Integer,
428
- minimum: 1,
429
- _default: 60,
430
- _example: 120,
431
- _plugin: true
432
- },
433
- "key" => {
434
- description: "The private key for the certificate.",
435
- type: String,
436
- _example: "/etc/puppetlabs/puppet/ssl/private_keys/my-host.example.com.pem",
437
- _plugin: true
438
- },
439
- "read_timeout" => {
440
- description: "How long to wait in seconds for a response from PuppetDB.",
441
- type: Integer,
442
- minimum: 1,
443
- _default: 60,
444
- _example: 120,
445
- _plugin: true
446
- },
447
- "server_urls" => {
448
- description: "An array containing the PuppetDB host to connect to. Include the protocol `https` "\
449
- "and the port, which is usually `8081`. For example, "\
450
- "`https://my-puppetdb-server.com:8081`.",
451
- type: Array,
452
- _example: ["https://puppet.example.com:8081"],
453
- _plugin: true
454
- },
455
- "token" => {
456
- description: "The path to the PE RBAC Token.",
457
- type: String,
458
- _example: "~/.puppetlabs/token",
459
- _plugin: true
460
- }
464
+ properties: PUPPETDB_OPTIONS,
465
+ _plugin: true
466
+ },
467
+ "puppetdb-instances" => {
468
+ description: "A map of named PuppetDB instances and their configuration, where keys are the name "\
469
+ "of a PuppetDB instance and values are maps of configuration options. For more "\
470
+ "information, see [Connecting Bolt to PuppetDB](bolt_connect_puppetdb.md).",
471
+ type: Hash,
472
+ additionalProperties: {
473
+ type: Hash,
474
+ properties: PUPPETDB_OPTIONS
461
475
  },
462
476
  _plugin: true
463
477
  },
@@ -610,6 +624,7 @@ module Bolt
610
624
  plugin-hooks
611
625
  plugins
612
626
  puppetdb
627
+ puppetdb-instances
613
628
  save-rerun
614
629
  spinner
615
630
  stream
@@ -637,6 +652,7 @@ module Bolt
637
652
  plugins
638
653
  policies
639
654
  puppetdb
655
+ puppetdb-instances
640
656
  rerunfile
641
657
  save-rerun
642
658
  spinner
@@ -10,6 +10,7 @@ module Bolt
10
10
  WINDOWS_OPTIONS = %w[
11
11
  bundled-ruby
12
12
  cleanup
13
+ extensions
13
14
  interpreters
14
15
  tmpdir
15
16
  ].freeze
data/lib/bolt/config.rb CHANGED
@@ -181,6 +181,7 @@ module Bolt
181
181
  'plugin-hooks' => {},
182
182
  'plugins' => {},
183
183
  'puppetdb' => {},
184
+ 'puppetdb-instances' => {},
184
185
  'save-rerun' => true,
185
186
  'spinner' => true,
186
187
  'transport' => 'ssh'
@@ -225,7 +226,7 @@ module Bolt
225
226
 
226
227
  # Pull out config options. We need to add 'transport' and 'inventoryfile' as they're
227
228
  # not part of the OPTIONS hash but are valid options that can be set with CLI options
228
- overrides = opts.slice(*OPTIONS.keys, 'inventoryfile', 'transport')
229
+ overrides = opts.slice(*OPTIONS.keys, 'inventoryfile', 'transport', 'default_puppetdb')
229
230
 
230
231
  # Pull out transport config options
231
232
  TRANSPORT_CONFIG.each do |transport, config|
@@ -252,14 +253,14 @@ module Bolt
252
253
  config_data.inject({}) do |acc, config|
253
254
  acc.merge(config) do |key, val1, val2|
254
255
  case key
255
- # Plugin config is shallow merged for each plugin
256
+ # Shallow merge config for each plugin
256
257
  when 'plugins'
257
258
  val1.merge(val2) { |_, v1, v2| v1.merge(v2) }
258
259
  # Transports are deep merged
259
260
  when *TRANSPORT_CONFIG.keys
260
261
  Bolt::Util.deep_merge(val1, val2)
261
262
  # Hash values are shallow merged
262
- when 'apply-settings', 'log', 'plugin-hooks', 'puppetdb'
263
+ when 'apply-settings', 'log', 'plugin-hooks', 'puppetdb', 'puppetdb-instances'
263
264
  val1.merge(val2)
264
265
  # Disabled warnings are concatenated
265
266
  when 'disable-warnings'
@@ -407,6 +408,14 @@ module Bolt
407
408
  @data['puppetdb']
408
409
  end
409
410
 
411
+ def puppetdb_instances
412
+ @data['puppetdb-instances']
413
+ end
414
+
415
+ def default_puppetdb
416
+ @data['default_puppetdb']
417
+ end
418
+
410
419
  def color
411
420
  @data['color']
412
421
  end
@@ -405,10 +405,15 @@ module Bolt
405
405
 
406
406
  # Add plan name and description
407
407
  info << colorize(:cyan, "#{plan['name']}\n")
408
- info << if plan['description']
409
- indent(2, plan['description'].chomp)
410
- else
408
+
409
+ description = +''
410
+ description << "#{plan['summary']}\n\n" if plan['summary']
411
+ description << plan['docstring'] if plan['docstring']
412
+
413
+ info << if description.empty?
411
414
  indent(2, 'No description available.')
415
+ else
416
+ indent(2, description.strip)
412
417
  end
413
418
  info << "\n\n"
414
419
 
@@ -50,7 +50,7 @@ module Bolt
50
50
  def self.parse_plan(yaml_string, source_ref)
51
51
  # This passes the filename as the second arg for compatibility with Psych used with ruby < 2.6
52
52
  # This can be removed when we remove support for ruby 2.5
53
- parse_tree = if Psych.method(:parse).parameters.include?('legacy_filename')
53
+ parse_tree = if Psych.method(:parse).parameters.rassoc(:filename) == %i[key filename]
54
54
  Psych.parse(yaml_string, filename: source_ref)
55
55
  else
56
56
  Psych.parse(yaml_string, source_ref)
data/lib/bolt/pal.rb CHANGED
@@ -531,7 +531,9 @@ module Bolt
531
531
  'description' => description,
532
532
  'parameters' => parameters,
533
533
  'module' => mod,
534
- 'private' => private_plan?(plan)
534
+ 'private' => private_plan?(plan),
535
+ 'summary' => plan.tag(:summary)&.text,
536
+ 'docstring' => (plan.docstring unless plan.docstring.empty?)
535
537
  }
536
538
 
537
539
  pp_info.merge!(get_plan_mtime(plan.file)) if with_mtime
@@ -564,7 +566,9 @@ module Bolt
564
566
  'description' => plan.description,
565
567
  'parameters' => parameters,
566
568
  'module' => mod,
567
- 'private' => !!plan.private
569
+ 'private' => !!plan.private,
570
+ 'docstring' => plan.description,
571
+ 'summary' => nil
568
572
  }
569
573
 
570
574
  yaml_info.merge!(get_plan_mtime(yaml_path)) if with_mtime