bolt 1.39.0 → 1.40.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 99b27a1cfbd744e85a99efe9c4785b4ab3a15e9a6fe15af9ac0496bd463736c7
4
- data.tar.gz: 530cbd2525700cdaebf9920aebf1ea3b104a2867dfb030a695e0046e162152a2
3
+ metadata.gz: f796af64ec2860ec9e3fc3809e246bca8ebafa4199c50757c3c63749069c4017
4
+ data.tar.gz: 7a11411a59cd4c5eb50c9ddc9ed02c5b0838aa6cab860f15ef46d76be7f4a34f
5
5
  SHA512:
6
- metadata.gz: cf7e5a175d04bf27ad2e2e0b4e464f334be20e4d08e9f0d072a51da695eb00698623eada2153865ec68fe125ace42e4c4939a5ef24513ec9139b1d87d302a6b0
7
- data.tar.gz: fedbec2378ba33c83b3cbbb1fce8e3a610c2dbbe66c200ddc72f2e6728b49a2f38a7f60ecebbc4ebb0959227d4402175108ad0da5d15220b3ddc91b52d5ae725
6
+ metadata.gz: f1365e9a864d04685b3e922d810fbdad173e8f703b28c004c1005032b9666f56bcc3b251c25292e8d3239417a5a9a1b3c53ed49619e23d8f89fed341b6bde2c0
7
+ data.tar.gz: 2c1700287f8a9879b510d8d2cf91b9f044e44546ef5ec2f72b8e5a6a4ad1871e0f21737b67743971cdb1f8c5df9d9321352726aadaaad5367cd2f7702b89e1f1
@@ -44,9 +44,9 @@ Puppet::Functions.create_function(:catch_errors) do
44
44
  rescue Puppet::PreformattedError => e
45
45
  if e.cause.is_a?(Bolt::Error)
46
46
  if error_types.nil?
47
- return e.cause.to_puppet_error
47
+ e.cause.to_puppet_error
48
48
  elsif error_types.include?(e.cause.to_h['kind'])
49
- return e.cause.to_puppet_error
49
+ e.cause.to_puppet_error
50
50
  else
51
51
  raise e
52
52
  end
@@ -212,7 +212,7 @@ module Bolt
212
212
  }
213
213
  data = Bolt::Util.deep_merge(defaults, data)
214
214
  # If features is an empty array deep_merge won't add the puppet-agent
215
- data['features'] << 'puppet-agent' if data['features'].empty?
215
+ data['features'] += ['puppet-agent'] if data['features'].empty?
216
216
  data
217
217
  end
218
218
 
@@ -257,15 +257,18 @@ module Bolt
257
257
  plan_info = +""
258
258
  usage = +"bolt plan run #{plan['name']}"
259
259
 
260
- plan['parameters']&.each do |name, p|
260
+ plan['parameters'].each do |name, p|
261
261
  pretty_params << "- #{name}: #{p['type']}\n"
262
+ pretty_params << " Default: #{p['default_value']}\n" unless p['default_value'].nil?
263
+ pretty_params << " #{p['description']}\n" if p['description']
262
264
  usage << (p.include?('default_value') ? " [#{name}=<value>]" : " #{name}=<value>")
263
265
  end
264
266
 
265
267
  plan_info << "\n#{plan['name']}"
268
+ plan_info << " - #{plan['description']}" if plan['description']
266
269
  plan_info << "\n\n"
267
270
  plan_info << "USAGE:\n#{usage}\n\n"
268
- plan_info << "PARAMETERS:\n#{pretty_params}\n" if plan['parameters']
271
+ plan_info << "PARAMETERS:\n#{pretty_params}\n" unless plan['parameters'].empty?
269
272
  plan_info << "MODULE:\n"
270
273
 
271
274
  path = plan['module']
data/lib/bolt/pal.rb CHANGED
@@ -299,38 +299,78 @@ module Bolt
299
299
  end
300
300
  end
301
301
 
302
- # This converts a plan signature object into a format used by the outputter.
303
- # Must be called from within bolt compiler to pickup type aliases used in the plan signature.
304
- def plan_hash(plan_name, plan)
305
- elements = plan.params_type.elements || []
306
- parameters = elements.each_with_object({}) do |param, acc|
307
- type = if param.value_type.is_a?(Puppet::Pops::Types::PTypeAliasType)
308
- param.value_type.name
309
- else
310
- param.value_type.to_s
311
- end
312
- acc[param.name] = { 'type' => type }
313
- acc[param.name]['default_value'] = nil if param.key_type.is_a?(Puppet::Pops::Types::POptionalType)
314
- end
315
- {
316
- 'name' => plan_name,
317
- 'parameters' => parameters
318
- }
319
- end
320
- private :plan_hash
321
-
322
302
  def get_plan_info(plan_name)
323
- plan_info = in_bolt_compiler do |compiler|
324
- plan = compiler.plan_signature(plan_name)
325
- hash = plan_hash(plan_name, plan) if plan
326
- hash['module'] = plan.instance_variable_get(:@plan_func).loader.parent.path if plan
327
- hash
303
+ plan_sig = in_bolt_compiler do |compiler|
304
+ compiler.plan_signature(plan_name)
328
305
  end
329
306
 
330
- if plan_info.nil?
307
+ if plan_sig.nil?
331
308
  raise Bolt::Error.unknown_plan(plan_name)
332
309
  end
333
- plan_info
310
+
311
+ mod = plan_sig.instance_variable_get(:@plan_func).loader.parent.path
312
+
313
+ # If it's a Puppet language plan, use strings to extract data. The only
314
+ # way to tell is to check which filename exists in the module.
315
+ plan_file = plan_name.split('::', 2)[1] || 'init'
316
+ pp_path = File.join(mod, 'plans', "#{plan_file}.pp")
317
+ if File.exist?(pp_path)
318
+ require 'puppet-strings'
319
+ require 'puppet-strings/yard'
320
+ PuppetStrings::Yard.setup!
321
+ YARD::Logger.instance.level = :error
322
+ YARD.parse(pp_path)
323
+
324
+ plan = YARD::Registry.at("puppet_plans::#{plan_name}")
325
+
326
+ description = if plan.tag(:summary)
327
+ plan.tag(:summary).text
328
+ elsif !plan.docstring.empty?
329
+ plan.docstring
330
+ end
331
+
332
+ defaults = plan.parameters.reject { |_, value| value.nil? }.to_h
333
+ parameters = plan.tags(:param).each_with_object({}) do |param, params|
334
+ name = param.name
335
+ params[name] = { 'type' => param.types.first }
336
+ params[name]['default_value'] = defaults[name] if defaults.key?(name)
337
+ params[name]['description'] = param.text unless param.text.empty?
338
+ end
339
+
340
+ {
341
+ 'name' => plan_name,
342
+ 'description' => description,
343
+ 'parameters' => parameters,
344
+ 'module' => mod
345
+ }
346
+
347
+ # If it's a YAML plan, fall back to limited data
348
+ else
349
+ yaml_path = File.join(mod, 'plans', "#{plan_file}.yaml")
350
+ plan_content = File.read(yaml_path)
351
+ plan = Bolt::PAL::YamlPlan::Loader.from_string(plan_name, plan_content, yaml_path)
352
+
353
+ parameters = plan.parameters.each_with_object({}) do |param, params|
354
+ name = param.name
355
+ type_str = case param.type_expr
356
+ when Puppet::Pops::Types::PTypeReferenceType
357
+ param.type_expr.type_string
358
+ when nil
359
+ 'Any'
360
+ else
361
+ param.type_expr
362
+ end
363
+ params[name] = { 'type' => type_str }
364
+ params[name]['default_value'] = param.value
365
+ params[name]['description'] = param.description if param.description
366
+ end
367
+ {
368
+ 'name' => plan_name,
369
+ 'description' => plan.description,
370
+ 'parameters' => parameters,
371
+ 'module' => mod
372
+ }
373
+ end
334
374
  end
335
375
 
336
376
  def convert_plan(plan_path)
@@ -6,16 +6,17 @@ require 'bolt/pal/yaml_plan/step'
6
6
  module Bolt
7
7
  class PAL
8
8
  class YamlPlan
9
- PLAN_KEYS = Set['parameters', 'steps', 'return', 'version']
9
+ PLAN_KEYS = Set['parameters', 'steps', 'return', 'version', 'description']
10
10
  VAR_NAME_PATTERN = /\A[a-z_][a-z0-9_]*\z/.freeze
11
11
 
12
- attr_reader :name, :parameters, :steps, :return
12
+ attr_reader :name, :parameters, :steps, :return, :description
13
13
 
14
14
  def initialize(name, plan)
15
15
  # Top-level plan keys aren't allowed to be Puppet code, so force them
16
16
  # all to strings.
17
17
  plan = Bolt::Util.walk_keys(plan) { |key| stringify(key) }
18
18
  @name = name.freeze
19
+ @description = stringify(plan['description']) if plan['description']
19
20
 
20
21
  params_hash = stringify(plan.fetch('parameters', {}))
21
22
  # Ensure params is a hash
@@ -53,7 +53,7 @@ module Bolt
53
53
  PuppetVisitor.create_visitor.accept(parse_tree)
54
54
  end
55
55
 
56
- def self.create(loader, typed_name, source_ref, yaml_string)
56
+ def self.from_string(name, yaml_string, source_ref)
57
57
  result = parse_plan(yaml_string, source_ref)
58
58
  unless result.is_a?(Hash)
59
59
  type = result.class.name
@@ -61,11 +61,14 @@ module Bolt
61
61
  end
62
62
 
63
63
  begin
64
- plan_definition = YamlPlan.new(typed_name, result).freeze
64
+ YamlPlan.new(name, result).freeze
65
65
  rescue Bolt::Error => e
66
66
  raise Puppet::ParseError.new(e.message, source_ref)
67
67
  end
68
+ end
68
69
 
70
+ def self.create(loader, typed_name, source_ref, yaml_string)
71
+ plan_definition = from_string(typed_name.name, yaml_string, source_ref)
69
72
  created = create_function_class(plan_definition)
70
73
  closure_scope = nil
71
74
 
@@ -4,7 +4,7 @@ module Bolt
4
4
  class PAL
5
5
  class YamlPlan
6
6
  class Parameter
7
- attr_reader :name, :value, :type_expr
7
+ attr_reader :name, :value, :type_expr, :description
8
8
 
9
9
  PARAMETER_KEYS = Set['type', 'default', 'description']
10
10
 
@@ -15,6 +15,7 @@ module Bolt
15
15
  @name = param
16
16
  @value = definition['default']
17
17
  @type_expr = Puppet::Pops::Types::TypeParser.singleton.parse(definition['type']) if definition['type']
18
+ @description = definition['description']
18
19
  end
19
20
 
20
21
  def validate_param(param, definition)
@@ -48,7 +48,6 @@ module Bolt
48
48
  plan_string
49
49
  end
50
50
 
51
- # Save me from all these rescue statements...
52
51
  def parse_plan
53
52
  begin
54
53
  file_contents = File.read(@plan_path)
@@ -57,18 +56,7 @@ module Bolt
57
56
  raise Bolt::FileError.new(msg, @plan_path)
58
57
  end
59
58
 
60
- begin
61
- result = Bolt::PAL::YamlPlan::Loader.parse_plan(file_contents, @plan_path)
62
- rescue Error => e
63
- raise ConvertError.new("Failed to convert yaml plan: #{e.message}", @plan_path)
64
- end
65
-
66
- unless result.is_a?(Hash)
67
- type = result.class.name
68
- raise ArgumentError, "The data loaded from #{source_ref} does not contain an object - its type is #{type}"
69
- end
70
-
71
- Bolt::PAL::YamlPlan.new(@modulename, result).freeze
59
+ Bolt::PAL::YamlPlan::Loader.from_string(@modulename, file_contents, @plan_path)
72
60
  end
73
61
 
74
62
  def validate_path
data/lib/bolt/plugin.rb CHANGED
@@ -63,7 +63,7 @@ module Bolt
63
63
  if defined?(Puppet)
64
64
  begin
65
65
  compiler = Puppet.lookup(:pal_compiler)
66
- rescue Puppet::Context::UndefinedBindingError; end # rubocop:disable Lint/HandleExceptions
66
+ rescue Puppet::Context::UndefinedBindingError; end # rubocop:disable Lint/SuppressedException
67
67
  end
68
68
 
69
69
  if compiler
@@ -11,7 +11,8 @@ module Bolt
11
11
  end
12
12
  end
13
13
 
14
- TARGET_OPTS = %w[uri name config].freeze
14
+ TEMPLATE_OPTS = %w[uri name config].freeze
15
+ PLUGIN_OPTS = %w[_plugin query target_mapping].freeze
15
16
 
16
17
  def initialize(pdb_client)
17
18
  @puppetdb_client = pdb_client
@@ -45,8 +46,19 @@ module Bolt
45
46
  targets = @puppetdb_client.query_certnames(opts['query'])
46
47
  facts = []
47
48
 
48
- target_opts = opts.select { |k, _| TARGET_OPTS.include?(k) }
49
- Bolt::Util.walk_vals(target_opts) do |value|
49
+ template = opts.delete('target_mapping') || {}
50
+
51
+ keys = Set.new(TEMPLATE_OPTS) & opts.keys
52
+ unless keys.empty?
53
+ raise Bolt::ValidationError, "PuppetDB plugin expects keys #{keys.to_a} to be set under 'target_mapping'"
54
+ end
55
+
56
+ keys = Set.new(opts.keys) - PLUGIN_OPTS
57
+ unless keys.empty?
58
+ raise Bolt::ValidationError, "Unknown keys in PuppetDB plugin: #{keys.to_a}"
59
+ end
60
+
61
+ Bolt::Util.walk_vals(template) do |value|
50
62
  # This is done in parts instead of in place so that we only need to
51
63
  # make one puppetDB query
52
64
  if value.is_a?(String)
@@ -61,7 +73,7 @@ module Bolt
61
73
 
62
74
  targets.map do |certname|
63
75
  target_data = fact_values[certname]
64
- target = resolve_facts(target_opts, certname, target_data) || {}
76
+ target = resolve_facts(template, certname, target_data) || {}
65
77
  target['uri'] = certname unless target['uri'] || target['name']
66
78
 
67
79
  target
data/lib/bolt/util.rb CHANGED
@@ -176,7 +176,7 @@ module Bolt
176
176
  rescue TypeError
177
177
  # unclonable (TrueClass, Fixnum, ...)
178
178
  cloned[obj.object_id] = obj
179
- return obj
179
+ obj
180
180
  else
181
181
  cloned[obj.object_id] = cl
182
182
  cloned[cl.object_id] = cl
@@ -195,7 +195,7 @@ module Bolt
195
195
  cl.instance_variable_set(var, v_cl)
196
196
  end
197
197
 
198
- return cl
198
+ cl
199
199
  end
200
200
  end
201
201
 
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.39.0'
4
+ VERSION = '1.40.0'
5
5
  end
@@ -24,7 +24,7 @@
24
24
  #
25
25
  # @example Run a command
26
26
  # run_plan(canary, command => 'whoami', nodes => $mynodes)
27
-
27
+ #
28
28
  plan canary(
29
29
  Optional[String[0]] $task = undef,
30
30
  Optional[String[0]] $command = undef,
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.39.0
4
+ version: 1.40.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-22 00:00:00.000000000 Z
11
+ date: 2019-12-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -184,6 +184,20 @@ dependencies:
184
184
  - - ">="
185
185
  - !ruby/object:Gem::Version
186
186
  version: 1.8.1
187
+ - !ruby/object:Gem::Dependency
188
+ name: puppet-strings
189
+ requirement: !ruby/object:Gem::Requirement
190
+ requirements:
191
+ - - "~>"
192
+ - !ruby/object:Gem::Version
193
+ version: '2.3'
194
+ type: :runtime
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ requirements:
198
+ - - "~>"
199
+ - !ruby/object:Gem::Version
200
+ version: '2.3'
187
201
  - !ruby/object:Gem::Dependency
188
202
  name: r10k
189
203
  requirement: !ruby/object:Gem::Requirement