bolt 1.37.0 → 1.38.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: 4e9167735253231222010ffb141c42048039e6b02c13061dbc6394db8876a6d7
4
- data.tar.gz: b10298a67bb280b58c265ce2238f91ac611b672eb3c303e9f507e15db6e05e7e
3
+ metadata.gz: 56eab83c9278b21bfe1241b6fcbbfac9a06adc976d77a7d5900a4940393ea5cd
4
+ data.tar.gz: c1389de9cad81f4f8ba9b2af9b3aa74bffdbc92e0f9c88ae6f018e7fa43ded1a
5
5
  SHA512:
6
- metadata.gz: 857e83019439072c518e50ad9c9d1c0dc93cc0e8b536f53348824091d6367fdc6df4b08c2d6c9dbf0cb45fb7fda29051c2743d470e4bba82ec7b708d7ed997cc
7
- data.tar.gz: f91623e0811677b7c9c9f643f3a512120e7af52edbaff7d74381b0d7a40556d77dc0f7c98cf7117fdd69db504b310681157e3c44367a27fe57089e6a3dc3dc41
6
+ metadata.gz: 104b2eda64ad81d4fc6bbf054309498866b6e8f3e256f455c06b19bb5a2c6e50192dc2c480ffa7c885e2afb2bebc0648e5feb295f22f7a9cc6b5f250b548ea22
7
+ data.tar.gz: 6115db7ac02b54224e346d12c2bd2fa98d6eee3031b4ae5093f89d0c2883e000cccb63428ace63b6e9a6ed173afafe327274b0b829109c9d3bd1c6fd75b9f699
data/Puppetfile CHANGED
@@ -7,7 +7,7 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
7
7
  # Core modules used by 'apply'
8
8
  mod 'puppetlabs-service', '1.1.0'
9
9
  mod 'puppetlabs-facts', '0.6.0'
10
- mod 'puppetlabs-puppet_agent', '2.2.1'
10
+ mod 'puppetlabs-puppet_agent', '2.2.2'
11
11
 
12
12
  # Core types and providers for Puppet 6
13
13
  mod 'puppetlabs-augeas_core', '1.0.5'
@@ -33,6 +33,7 @@ mod 'puppetlabs-azure_inventory', '0.2.0'
33
33
  mod 'puppetlabs-terraform', '0.2.0'
34
34
  mod 'puppetlabs-vault', '0.2.2'
35
35
  mod 'puppetlabs-aws_inventory', '0.2.0'
36
+ mod 'puppetlabs-yaml', '0.1.0'
36
37
 
37
38
  # If we don't list these modules explicitly, r10k will purge them
38
39
  mod 'canary', local: true
@@ -50,8 +50,8 @@ Puppet::Functions.create_function(:apply_prep) do
50
50
  end
51
51
  # rubocop:enable Naming/AccessorMethodName
52
52
 
53
- def run_task(targets, task, args = {})
54
- executor.run_task(targets, task, args)
53
+ def run_task(targets, task, args = {}, options = {})
54
+ executor.run_task(targets, task, args, options)
55
55
  end
56
56
 
57
57
  # Returns true if the target has the puppet-agent feature defined, either from inventory or transport.
@@ -16,6 +16,6 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
16
16
  def exists(scope, filename)
17
17
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
18
18
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
19
- found && Puppet::FileSystem.exist?(found)
19
+ found ? Puppet::FileSystem.exist?(found) : false
20
20
  end
21
21
  end
@@ -16,6 +16,6 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
16
16
  def readable(scope, filename)
17
17
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
18
18
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
19
- found && File.readable?(found)
19
+ found ? File.readable?(found) : false
20
20
  end
21
21
  end
@@ -7,7 +7,7 @@ Puppet::Functions.create_function(:'system::env') do
7
7
  # system::env('USER')
8
8
  dispatch :env do
9
9
  required_param 'String', :name
10
- return_type 'String'
10
+ return_type 'Optional[String]'
11
11
  end
12
12
 
13
13
  def env(name)
@@ -7,8 +7,8 @@ require 'optparse'
7
7
  module Bolt
8
8
  class BoltOptionParser < OptionParser
9
9
  OPTIONS = { inventory: %w[nodes targets query rerun description],
10
- authentication: %w[user password private-key host-key-check ssl ssl-verify],
11
- escalation: %w[run-as sudo-password],
10
+ authentication: %w[user password password-prompt private-key host-key-check ssl ssl-verify],
11
+ escalation: %w[run-as sudo-password sudo-password-prompt],
12
12
  run_context: %w[concurrency inventoryfile save-rerun],
13
13
  global_config_setters: %w[modulepath boltdir configfile],
14
14
  transports: %w[transport connect-timeout tty],
@@ -54,6 +54,9 @@ module Bolt
54
54
  when 'init'
55
55
  { flags: OPTIONS[:global],
56
56
  banner: PROJECT_INIT_HELP }
57
+ when 'migrate'
58
+ { flags: OPTIONS[:global] + %w[inventoryfile boltdir configfile],
59
+ banner: PROJECT_MIGRATE_HELP }
57
60
  else
58
61
  { flags: OPTIONS[:global],
59
62
  banner: PROJECT_HELP }
@@ -131,6 +134,7 @@ module Bolt
131
134
  bolt inventory show Show the list of targets an action would run on
132
135
  bolt group show Show the list of groups in the inventory
133
136
  bolt project init Create a new Bolt project
137
+ bolt project migrate Migrate a Bolt project to the latest version
134
138
 
135
139
  Run `bolt <subcommand> --help` to view specific examples.
136
140
 
@@ -325,6 +329,7 @@ module Bolt
325
329
 
326
330
  Available actions are:
327
331
  init Create a new Bolt project
332
+ migrate Migrate a Bolt project to the latest version
328
333
 
329
334
  Available options are:
330
335
  PROJECT_HELP
@@ -338,10 +343,22 @@ module Bolt
338
343
  Available options are:
339
344
  PROJECT_INIT_HELP
340
345
 
346
+ PROJECT_MIGRATE_HELP = <<~PROJECT_MIGRATE_HELP
347
+ Usage: bolt project migrate
348
+
349
+ Migrate a Bolt project to the latest version.
350
+ Loads a Bolt project's inventory file and migrates it to the latest version. The
351
+ inventory file is modified in place and will not preserve comments or formatting.
352
+
353
+ Available options are:
354
+ PROJECT_MIGRATE_HELP
355
+
356
+ attr_reader :warnings
341
357
  def initialize(options)
342
358
  super()
343
359
 
344
360
  @options = options
361
+ @warnings = []
345
362
 
346
363
  define('-n', '--nodes NODES',
347
364
  'Alias for --targets',
@@ -397,6 +414,9 @@ module Bolt
397
414
  define('-p', '--password [PASSWORD]',
398
415
  'Password to authenticate with') do |password|
399
416
  if password.nil?
417
+ msg = "Optional parameter for --password is deprecated and will no longer prompt for password. " \
418
+ "Use the prompt plugin or --password-prompt instead to prompt for passwords."
419
+ @warnings << { option: 'password', msg: msg }
400
420
  STDOUT.print "Please enter your password: "
401
421
  @options[:password] = STDIN.noecho(&:gets).chomp
402
422
  STDOUT.puts
@@ -404,6 +424,11 @@ module Bolt
404
424
  @options[:password] = password
405
425
  end
406
426
  end
427
+ define('--password-prompt', 'Prompt for user to input password') do |_password|
428
+ STDERR.print "Please enter your password: "
429
+ @options[:password] = STDIN.noecho(&:gets).chomp
430
+ STDERR.puts
431
+ end
407
432
  define('--private-key KEY', 'Private ssh key to authenticate with') do |key|
408
433
  @options[:'private-key'] = key
409
434
  end
@@ -424,6 +449,9 @@ module Bolt
424
449
  define('--sudo-password [PASSWORD]',
425
450
  'Password for privilege escalation') do |password|
426
451
  if password.nil?
452
+ msg = "Optional parameter for --sudo-password is deprecated and will no longer prompt for password. " \
453
+ "Use the prompt plugin or --sudo-password-prompt instead to prompt for passwords."
454
+ @warnings << { option: 'sudo-password', msg: msg }
427
455
  STDOUT.print "Please enter your privilege escalation password: "
428
456
  @options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
429
457
  STDOUT.puts
@@ -431,6 +459,11 @@ module Bolt
431
459
  @options[:'sudo-password'] = password
432
460
  end
433
461
  end
462
+ define('--sudo-password-prompt', 'Prompt for user to input escalation password') do |_password|
463
+ STDERR.print "Please enter your privilege escalation password: "
464
+ @options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
465
+ STDERR.puts
466
+ end
434
467
 
435
468
  separator "\nRun context:"
436
469
  define('-c', '--concurrency CONCURRENCY', Integer,
@@ -37,7 +37,7 @@ module Bolt
37
37
  'secret' => %w[encrypt decrypt createkeys],
38
38
  'inventory' => %w[show],
39
39
  'group' => %w[show],
40
- 'project' => %w[init],
40
+ 'project' => %w[init migrate],
41
41
  'apply' => %w[] }.freeze
42
42
 
43
43
  attr_reader :config, :options
@@ -129,6 +129,7 @@ module Bolt
129
129
  # Logger must be configured before checking path case, otherwise warnings will not display
130
130
  @config.check_path_case('modulepath', @config.modulepath)
131
131
 
132
+ parser.warnings.each { |warning| @logger.warn(warning[:msg]) }
132
133
  # After validation, initialize inventory and targets. Errors here are better to catch early.
133
134
  # After this step
134
135
  # options[:target_args] will contain a string/array version of the targetting options this is passed to plans
@@ -367,7 +368,11 @@ module Bolt
367
368
 
368
369
  case options[:subcommand]
369
370
  when 'project'
370
- code = initialize_project
371
+ if options[:action] == 'init'
372
+ code = initialize_project
373
+ elsif options[:action] == 'migrate'
374
+ code = migrate_project
375
+ end
371
376
  when 'plan'
372
377
  code = run_plan(options[:object], options[:task_options], options[:target_args], options)
373
378
  when 'puppetfile'
@@ -472,13 +477,27 @@ module Bolt
472
477
 
473
478
  def run_plan(plan_name, plan_arguments, nodes, options)
474
479
  unless nodes.empty?
475
- if plan_arguments['nodes']
480
+ if plan_arguments['nodes'] || plan_arguments['targets']
481
+ key = plan_arguments.include?('nodes') ? 'nodes' : 'targets'
476
482
  raise Bolt::CLIError,
477
- "A plan's 'nodes' parameter may be specified using the --nodes option, but in that " \
478
- "case it must not be specified as a separate nodes=<value> parameter nor included " \
483
+ "A plan's '#{key}' parameter may be specified using the --#{key} option, but in that " \
484
+ "case it must not be specified as a separate #{key}=<value> parameter nor included " \
479
485
  "in the JSON data passed in the --params option"
480
486
  end
481
- plan_arguments['nodes'] = nodes.join(',')
487
+
488
+ plan_params = pal.get_plan_info(plan_name)['parameters']
489
+ target_param = plan_params.dig('targets', 'type') =~ /TargetSpec/
490
+ node_param = plan_params.include?('nodes')
491
+
492
+ if node_param && target_param
493
+ msg = "Plan parameters include both 'nodes' and 'targets' with type 'TargetSpec', " \
494
+ "neither will populated with the value for --nodes or --targets."
495
+ @logger.warn(msg)
496
+ elsif node_param
497
+ plan_arguments['nodes'] = nodes.join(',')
498
+ elsif target_param
499
+ plan_arguments['targets'] = nodes.join(',')
500
+ end
482
501
  end
483
502
 
484
503
  plan_context = { plan_name: plan_name,
@@ -507,6 +526,7 @@ module Bolt
507
526
  end
508
527
 
509
528
  def apply_manifest(code, targets, filename = nil, noop = false)
529
+ Puppet[:tasks] = false
510
530
  ast = pal.parse_manifest(code, filename)
511
531
 
512
532
  executor = Bolt::Executor.new(config.concurrency, analytics, noop)
@@ -559,6 +579,52 @@ module Bolt
559
579
  ok ? 0 : 1
560
580
  end
561
581
 
582
+ def migrate_project
583
+ if inventory.version == 2
584
+ ok = true
585
+ else
586
+ inventory_file = config.inventoryfile || config.boltdir.inventory_file
587
+
588
+ begin
589
+ Bolt::Util.file_stat(inventory_file)
590
+ rescue Errno::ENOENT
591
+ raise Bolt::FileError.new("The inventory file '#{inventory_file}' does not exist", inventory_file)
592
+ end
593
+
594
+ inv = YAML.safe_load(File.open(inventory_file))
595
+ migrate_group(inv)
596
+
597
+ ok = File.write(inventory_file, { 'version' => 2 }.merge(inv).to_yaml)
598
+ end
599
+
600
+ result = if ok
601
+ "Successfully migrated Bolt project to latest version"
602
+ else
603
+ "Could not migrate Bolt project to latest version"
604
+ end
605
+ outputter.print_message result
606
+
607
+ ok ? 0 : 1
608
+ end
609
+
610
+ # Walks an inventory hash and replaces all 'nodes' keys with 'targets' keys
611
+ # and all 'name' keys nested in a 'targets' hash with 'uri' keys. Data is
612
+ # modified in place.
613
+ def migrate_group(group)
614
+ if group.key?('nodes')
615
+ targets = group['nodes'].map do |target|
616
+ target['uri'] = target.delete('name') if target.is_a?(Hash)
617
+ target
618
+ end
619
+ group.delete('nodes')
620
+ group['targets'] = targets
621
+ end
622
+ (group['groups'] || []).each do |subgroup|
623
+ migrate_group(subgroup)
624
+ end
625
+ nil
626
+ end
627
+
562
628
  def install_puppetfile(config, puppetfile, modulepath)
563
629
  require 'r10k/cli'
564
630
  require 'bolt/r10k_log_proxy'
@@ -71,7 +71,7 @@ module Bolt
71
71
  @save_rerun = true
72
72
  @puppetfile_config = {}
73
73
  @plugins = {}
74
- @plugin_hooks = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }
74
+ @plugin_hooks = {}
75
75
 
76
76
  # add an entry for the default console logger
77
77
  @log = { 'console' => {} }
@@ -166,30 +166,14 @@ module Bolt
166
166
 
167
167
  @save_rerun = data['save-rerun'] if data.key?('save-rerun')
168
168
 
169
- # Plugins are only settable from config not inventory so we can overwrite
170
169
  @plugins = data['plugins'] if data.key?('plugins')
171
- @plugin_hooks.merge!(data['plugin_hooks']) if data.key?('plugin_hooks')
170
+ @plugin_hooks = data['plugin_hooks'] if data.key?('plugin_hooks')
172
171
 
173
- %w[concurrency format puppetdb color transport].each do |key|
172
+ %w[concurrency format puppetdb color].each do |key|
174
173
  send("#{key}=", data[key]) if data.key?(key)
175
174
  end
176
175
 
177
- TRANSPORTS.each do |key, impl|
178
- if data[key.to_s]
179
- selected = impl.filter_options(data[key.to_s])
180
- if @future
181
- to_expand = %w[private-key cacert token-file] & selected.keys
182
- to_expand.each do |opt|
183
- selected[opt] = File.expand_path(selected[opt], @boltdir.path) if opt.is_a?(String)
184
- end
185
- end
186
-
187
- @transports[key] = Bolt::Util.deep_merge(@transports[key], selected)
188
- end
189
- if @transports[key]['interpreters']
190
- @transports[key]['interpreters'] = normalize_interpreters(@transports[key]['interpreters'])
191
- end
192
- end
176
+ update_transports(data)
193
177
  end
194
178
  private :update_from_file
195
179
 
@@ -229,11 +213,28 @@ module Bolt
229
213
  end
230
214
 
231
215
  def update_from_inventory(data)
232
- update_from_file(data)
216
+ update_transports(data)
217
+ end
233
218
 
234
- if data['transport']
235
- @transport = data['transport']
219
+ def update_transports(data)
220
+ TRANSPORTS.each do |key, impl|
221
+ if data[key.to_s]
222
+ selected = impl.filter_options(data[key.to_s])
223
+ if @future
224
+ to_expand = %w[private-key cacert token-file] & selected.keys
225
+ to_expand.each do |opt|
226
+ selected[opt] = File.expand_path(selected[opt], @boltdir.path) if opt.is_a?(String)
227
+ end
228
+ end
229
+
230
+ @transports[key] = Bolt::Util.deep_merge(@transports[key], selected)
231
+ end
232
+ if @transports[key]['interpreters']
233
+ @transports[key]['interpreters'] = normalize_interpreters(@transports[key]['interpreters'])
234
+ end
236
235
  end
236
+
237
+ @transport = data['transport'] if data.key?('transport')
237
238
  end
238
239
 
239
240
  def transport_conf
@@ -234,7 +234,7 @@ module Bolt
234
234
  set_facts(target.name, data['facts']) unless @target_facts[target.name]
235
235
  data['features']&.each { |feature| set_feature(target, feature) } unless @target_features[target.name]
236
236
  unless @target_plugin_hooks[target.name]
237
- set_plugin_hooks(target.name, @config.plugin_hooks.merge(data['plugin_hooks'] || {}))
237
+ set_plugin_hooks(target.name, (@plugins&.plugin_hooks || {}).merge(data['plugin_hooks'] || {}))
238
238
  end
239
239
 
240
240
  # Use Config object to ensure config section is treated consistently with config file
@@ -78,7 +78,7 @@ module Bolt
78
78
  def plugin_hooks
79
79
  # Merge plugin_hooks from the config file with any defined by the group
80
80
  # or assigned dynamically to the target
81
- @inventory.config.plugin_hooks.merge(group_cache['plugin_hooks']).merge(@plugin_hooks)
81
+ @inventory.plugins.plugin_hooks.merge(group_cache['plugin_hooks']).merge(@plugin_hooks)
82
82
  end
83
83
 
84
84
  def set_config(key_or_key_path, value)
@@ -39,9 +39,10 @@ module Bolt
39
39
  end
40
40
 
41
41
  class PluginContext
42
- def initialize(config, pal)
42
+ def initialize(config, pal, plugins)
43
43
  @pal = pal
44
44
  @config = config
45
+ @plugins = plugins
45
46
  end
46
47
 
47
48
  def serial_executor
@@ -50,7 +51,7 @@ module Bolt
50
51
  private :serial_executor
51
52
 
52
53
  def empty_inventory
53
- @empty_inventory ||= Bolt::Inventory.new({}, @config)
54
+ @empty_inventory ||= Bolt::Inventory::Inventory2.new({}, @config, plugins: @plugins)
54
55
  end
55
56
  private :empty_inventory
56
57
 
@@ -120,28 +121,46 @@ module Bolt
120
121
 
121
122
  def self.setup(config, pal, pdb_client, analytics)
122
123
  plugins = new(config, pal, analytics)
123
- # PDB is special do we want to expose the default client to the context?
124
+
125
+ # PDB is special because it needs the PDB client. Since it has no config,
126
+ # we can just add it first.
124
127
  plugins.add_plugin(Bolt::Plugin::Puppetdb.new(pdb_client))
125
128
 
126
- plugins.add_ruby_plugin('Bolt::Plugin::InstallAgent')
127
- plugins.add_ruby_plugin('Bolt::Plugin::Task')
128
- plugins.add_ruby_plugin('Bolt::Plugin::Pkcs7')
129
- plugins.add_ruby_plugin('Bolt::Plugin::Prompt')
129
+ # Initialize any plugins referenced in config. This will also indirectly
130
+ # initialize any plugins they depend on.
131
+ if plugins.reference?(config.plugins)
132
+ msg = "The 'plugins' setting cannot be set by a plugin reference"
133
+ raise PluginError.new(msg, 'bolt/plugin-error')
134
+ end
135
+
136
+ config.plugins.keys.each do |plugin|
137
+ plugins.by_name(plugin)
138
+ end
139
+
140
+ plugins.plugin_hooks.merge!(plugins.resolve_references(config.plugin_hooks))
130
141
 
131
142
  plugins
132
143
  end
133
144
 
134
- BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory].freeze
145
+ RUBY_PLUGINS = %w[install_agent task pkcs7 prompt].freeze
146
+ BUILTIN_PLUGINS = %w[task terraform pkcs7 prompt vault aws_inventory puppetdb azure_inventory yaml].freeze
147
+ DEFAULT_PLUGIN_HOOKS = { 'puppet_library' => { 'plugin' => 'puppet_agent', 'stop_service' => true } }.freeze
135
148
 
136
149
  attr_reader :pal, :plugin_context
150
+ attr_accessor :plugin_hooks
151
+
152
+ private_class_method :new
137
153
 
138
154
  def initialize(config, pal, analytics)
139
155
  @config = config
140
156
  @analytics = analytics
141
- @plugin_context = PluginContext.new(config, pal)
157
+ @plugin_context = PluginContext.new(config, pal, self)
142
158
  @plugins = {}
143
159
  @pal = pal
144
160
  @unknown = Set.new
161
+ @resolution_stack = []
162
+ @unresolved_plugin_configs = config.plugins.dup
163
+ @plugin_hooks = DEFAULT_PLUGIN_HOOKS.dup
145
164
  end
146
165
 
147
166
  def modules
@@ -153,11 +172,11 @@ module Bolt
153
172
  @plugins[plugin.name] = plugin
154
173
  end
155
174
 
156
- def add_ruby_plugin(cls_name)
157
- snake_name = Bolt::Util.class_name_to_file_name(cls_name)
158
- require snake_name
159
- cls = Kernel.const_get(cls_name)
160
- plugin_name = snake_name.split('/').last
175
+ def add_ruby_plugin(plugin_name)
176
+ cls_name = Bolt::Util.snake_name_to_class_name(plugin_name)
177
+ filename = "bolt/plugin/#{plugin_name}"
178
+ require filename
179
+ cls = Kernel.const_get("Bolt::Plugin::#{cls_name}")
161
180
  opts = {
162
181
  context: @plugin_context,
163
182
  config: config_for_plugin(plugin_name)
@@ -177,14 +196,18 @@ module Bolt
177
196
  add_plugin(plugin)
178
197
  end
179
198
 
180
- def add_from_config
181
- @config.plugins.keys.each do |plugin_name|
182
- by_name(plugin_name)
183
- end
184
- end
185
-
186
199
  def config_for_plugin(plugin_name)
187
- @config.plugins[plugin_name] || {}
200
+ return {} unless @unresolved_plugin_configs.include?(plugin_name)
201
+ if @resolution_stack.include?(plugin_name)
202
+ msg = "Configuration for plugin '#{plugin_name}' depends on the plugin itself"
203
+ raise PluginError.new(msg, 'bolt/plugin-error')
204
+ else
205
+ @resolution_stack.push(plugin_name)
206
+ config = resolve_references(@unresolved_plugin_configs[plugin_name])
207
+ @unresolved_plugin_configs.delete(plugin_name)
208
+ @resolution_stack.pop
209
+ config
210
+ end
188
211
  end
189
212
 
190
213
  def get_hook(plugin_name, hook)
@@ -200,7 +223,9 @@ module Bolt
200
223
  def by_name(plugin_name)
201
224
  return @plugins[plugin_name] if @plugins.include?(plugin_name)
202
225
  begin
203
- unless @unknown.include?(plugin_name)
226
+ if RUBY_PLUGINS.include?(plugin_name)
227
+ add_ruby_plugin(plugin_name)
228
+ elsif !@unknown.include?(plugin_name)
204
229
  add_module_plugin(plugin_name)
205
230
  end
206
231
  rescue PluginError::Unknown
@@ -218,6 +243,9 @@ module Bolt
218
243
  Bolt::Util.postwalk_vals(data) do |value|
219
244
  reference?(value) ? resolve_references(resolve_single_reference(value)) : value
220
245
  end
246
+ rescue SystemStackError
247
+ raise Bolt::Error.new("Stack depth exceeded while recursively resolving references.",
248
+ "bolt/recursive-reference-loop")
221
249
  end
222
250
 
223
251
  # Iteratively resolves "top-level" references until the result no longer
@@ -158,20 +158,21 @@ module Bolt
158
158
  # opts are passed directly from inventory but all of the _ options are
159
159
  # handled previously. That may not always be the case so filter them
160
160
  # out now.
161
- _meta, params = opts.partition { |key, _val| key.start_with?('_') }.map(&:to_h)
162
- metaparams = {}
161
+ meta, params = opts.partition { |key, _val| key.start_with?('_') }.map(&:to_h)
162
+
163
163
  # Send config with `_config` when config is defined in bolt_plugin.json
164
164
  # Otherwise, merge config with params
165
165
  # TODO: remove @send_config when deprecated
166
166
  if @send_config
167
- metaparams['_config'] = config if config?
167
+ validate_params(task, params)
168
+ params['_config'] = config if config?
168
169
  else
169
170
  params = @config ? config.merge(params) : params
171
+ validate_params(task, params)
170
172
  end
171
- metaparams['_boltdir'] = @context.boltdir
173
+ params['_boltdir'] = @context.boltdir.to_s
172
174
 
173
- validate_params(task, params)
174
- [params, metaparams]
175
+ [params, meta]
175
176
  end
176
177
 
177
178
  def extract_task_parameter_schema
@@ -199,6 +200,7 @@ module Bolt
199
200
  end
200
201
 
201
202
  def run_task(task, opts)
203
+ opts = opts.reject { |key, _val| key.start_with?('_') }
202
204
  params, metaparams = process_params(task, opts)
203
205
  params = params.merge(metaparams)
204
206
 
@@ -271,11 +273,11 @@ module Bolt
271
273
 
272
274
  params, meta_params = process_params(task, opts)
273
275
 
274
- # our metaparams are meant for the task not the executor
275
- params = params.merge(meta_params)
276
+ options = {}
277
+ options[:run_as] = meta_params['_run_as'] if meta_params['_run_as']
276
278
 
277
279
  proc do
278
- apply_prep.run_task([target], task, params).first
280
+ apply_prep.run_task([target], task, params, options).first
279
281
  end
280
282
  end
281
283
  end
@@ -20,7 +20,7 @@ module Bolt
20
20
 
21
21
  def resolve_reference(opts)
22
22
  # rubocop:disable Style/GlobalVars
23
- $future ? STDERR.print("#{opts['message']}:") : STDOUT.print("#{opts['message']}:")
23
+ $future ? STDERR.print("#{opts['message']}: ") : STDOUT.print("#{opts['message']}: ")
24
24
  value = STDIN.noecho(&:gets).chomp
25
25
  $future ? STDERR.puts : STDOUT.puts
26
26
  # rubocop:enable Style/GlobalVars
@@ -48,13 +48,15 @@ module Bolt
48
48
 
49
49
  def puppet_library(opts, target, apply_prep)
50
50
  params = opts['parameters'] || {}
51
+ run_opts = {}
52
+ run_opts[:run_as] = opts['_run_as'] if opts['_run_as']
51
53
  begin
52
54
  task = apply_prep.get_task(opts['task'], params)
53
55
  rescue Bolt::Error => e
54
56
  raise Bolt::Plugin::PluginError::ExecutionError.new(e.message, name, 'puppet_library')
55
57
  end
56
58
  proc do
57
- apply_prep.run_task([target], task, params).first
59
+ apply_prep.run_task([target], task, params, run_opts).first
58
60
  end
59
61
  end
60
62
  end
@@ -204,6 +204,10 @@ module Bolt
204
204
  File.stat(File.expand_path(path))
205
205
  end
206
206
 
207
+ def snake_name_to_class_name(snake_name)
208
+ snake_name.split('_').map(&:capitalize).join
209
+ end
210
+
207
211
  def class_name_to_file_name(cls_name)
208
212
  # Note this turns Bolt::CLI -> 'bolt/cli' not 'bolt/c_l_i'
209
213
  # this won't handle Bolt::Inventory2Foo
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '1.37.0'
4
+ VERSION = '1.38.0'
5
5
  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: 1.37.0
4
+ version: 1.38.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-08 00:00:00.000000000 Z
11
+ date: 2019-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable