bolt 1.21.0 → 1.22.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -1
  3. data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -1
  4. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +1 -1
  5. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -1
  6. data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -1
  7. data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -1
  8. data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -1
  9. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -1
  10. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
  12. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +1 -1
  13. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
  14. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
  15. data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -1
  16. data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -1
  17. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
  18. data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -1
  19. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -1
  20. data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -1
  21. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +1 -0
  22. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +1 -0
  23. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +1 -0
  24. data/bolt-modules/file/lib/puppet/functions/file/read.rb +1 -0
  25. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +1 -0
  26. data/bolt-modules/file/lib/puppet/functions/file/write.rb +1 -0
  27. data/bolt-modules/out/lib/puppet/functions/out/message.rb +1 -0
  28. data/bolt-modules/system/lib/puppet/functions/system/env.rb +1 -0
  29. data/lib/bolt/cli.rb +8 -5
  30. data/lib/bolt/config.rb +9 -1
  31. data/lib/bolt/inventory/group2.rb +48 -5
  32. data/lib/bolt/inventory/inventory2.rb +20 -8
  33. data/lib/bolt/plugin.rb +2 -0
  34. data/lib/bolt/plugin/prompt.rb +32 -0
  35. data/lib/bolt/plugin/terraform.rb +56 -21
  36. data/lib/bolt/task.rb +0 -1
  37. data/lib/bolt/version.rb +1 -1
  38. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e5ccf0b9b2f554bd10a8f4b25eedf4049c6b2820925d614be61a20b29ce64f5e
4
- data.tar.gz: 814f77cef6785b3c5978c5b1336f0e8eb6cced7e3e1eb8917c2cbc7c96955deb
3
+ metadata.gz: b5cea3288b0599937b773308e2f7286661c8e325ee2f04cb69b7e5521423d487
4
+ data.tar.gz: c1bffcef93f5167db8ecc2a21ce934a74b59ea40280a91b1bbcfde9b74d1b6dc
5
5
  SHA512:
6
- metadata.gz: '058a15c710a702ea63e4f989b0f96efa3c2c54a074650e0609dd1157f4fe6818fcaa8f2aaadbf145dbf588411cecf0d686e07821d9ad66994cab59de7ec5a9b5'
7
- data.tar.gz: 5292df40ecd9c3db055603aa279db60b2b80ce8ac416756d0c3b5e4fef7f072b7b766e78dd236f2630f00c71d3b27fc59b97681546eb680057e34df18da7dd36
6
+ metadata.gz: 07a86d3b9ab1a6d5d736a00e1384a0955164db3a978f5a3532d9422396116df5c024254c10afeed4af7a9253acd0bc64a78b640531716e5fe917f54b2deca3da
7
+ data.tar.gz: e30621636ccc9a6da616b7b9802a7aee40c99146e6983c89358332f091c004c3430a963beb2ebe3b1bee5685b2fa5e7be578a907be80971fa07fea0362b3ba7b
@@ -25,7 +25,7 @@ Puppet::Functions.create_function(:add_facts) do
25
25
 
26
26
  inventory = Puppet.lookup(:bolt_inventory)
27
27
  executor = Puppet.lookup(:bolt_executor)
28
- executor.report_function_call('add_facts')
28
+ executor.report_function_call(self.class.name)
29
29
 
30
30
  inventory.add_facts(target, facts)
31
31
  end
@@ -29,7 +29,7 @@ Puppet::Functions.create_function(:add_to_group) do
29
29
 
30
30
  inventory = Puppet.lookup(:bolt_inventory)
31
31
  executor = Puppet.lookup(:bolt_executor)
32
- executor.report_function_call('add_to_group')
32
+ executor.report_function_call(self.class.name)
33
33
 
34
34
  inventory.add_to_group(inventory.get_targets(targets), group)
35
35
  end
@@ -50,7 +50,7 @@ Puppet::Functions.create_function(:apply_prep) do
50
50
  executor = Puppet.lookup(:bolt_executor)
51
51
  inventory = Puppet.lookup(:bolt_inventory)
52
52
 
53
- executor.report_function_call('apply_prep')
53
+ executor.report_function_call(self.class.name)
54
54
 
55
55
  targets = inventory.get_targets(target_spec)
56
56
 
@@ -17,7 +17,7 @@ Puppet::Functions.create_function(:facts) do
17
17
  inventory = Puppet.lookup(:bolt_inventory)
18
18
  # Bolt executor not expected when invoked from apply block
19
19
  executor = Puppet.lookup(:bolt_executor) { nil }
20
- executor&.report_function_call('facts')
20
+ executor&.report_function_call(self.class.name)
21
21
 
22
22
  inventory.facts(target)
23
23
  end
@@ -40,7 +40,7 @@ Puppet::Functions.create_function(:fail_plan) do
40
40
  end
41
41
 
42
42
  executor = Puppet.lookup(:bolt_executor)
43
- executor.report_function_call('fail_plan')
43
+ executor.report_function_call(self.class.name)
44
44
 
45
45
  raise Bolt::PlanFailure.new(msg, kind || 'bolt/plan-failure', details, issue_code)
46
46
  end
@@ -50,7 +50,7 @@ Puppet::Functions.create_function(:get_resources) do
50
50
  end
51
51
  end
52
52
 
53
- executor.report_function_call('get_resources')
53
+ executor.report_function_call(self.class.name)
54
54
 
55
55
  targets = inventory.get_targets(target_spec)
56
56
 
@@ -25,7 +25,7 @@ Puppet::Functions.create_function(:get_targets) do
25
25
  inventory = Puppet.lookup(:bolt_inventory)
26
26
  # Bolt executor not expected when invoked from apply block
27
27
  executor = Puppet.lookup(:bolt_executor) { nil }
28
- executor&.report_function_call('get_targets')
28
+ executor&.report_function_call(self.class.name)
29
29
 
30
30
  inventory.get_targets(names)
31
31
  end
@@ -20,7 +20,7 @@ Puppet::Functions.create_function(:puppetdb_fact) do
20
20
  puppetdb_client = Puppet.lookup(:bolt_pdb_client)
21
21
  # Bolt executor not expected when invoked from apply block
22
22
  executor = Puppet.lookup(:bolt_executor) { nil }
23
- executor&.report_function_call('puppetdb_fact')
23
+ executor&.report_function_call(self.class.name)
24
24
 
25
25
  puppetdb_client.facts_for_node(certnames)
26
26
  end
@@ -22,7 +22,7 @@ Puppet::Functions.create_function(:puppetdb_query) do
22
22
  puppetdb_client = Puppet.lookup(:bolt_pdb_client)
23
23
  # Bolt executor not expected when invoked from apply block
24
24
  executor = Puppet.lookup(:bolt_executor) { nil }
25
- executor&.report_function_call('puppetdb_query')
25
+ executor&.report_function_call(self.class.name)
26
26
 
27
27
  puppetdb_client.make_query(query)
28
28
  end
@@ -52,7 +52,7 @@ Puppet::Functions.create_function(:run_command) do
52
52
  executor = Puppet.lookup(:bolt_executor)
53
53
  inventory = Puppet.lookup(:bolt_inventory)
54
54
 
55
- executor.report_function_call('run_command')
55
+ executor.report_function_call(self.class.name)
56
56
 
57
57
  # Ensure that given targets are all Target instances
58
58
  targets = inventory.get_targets(targets)
@@ -29,7 +29,7 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
29
29
  # Bolt calls this function internally to trigger plans from the CLI. We
30
30
  # don't want to count those invocations.
31
31
  unless named_args['_bolt_api_call']
32
- executor.report_function_call('run_plan')
32
+ executor.report_function_call(self.class.name)
33
33
  end
34
34
 
35
35
  # Report bundled content, this should capture plans run from both CLI and Plans
@@ -58,7 +58,7 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
58
58
  executor = Puppet.lookup(:bolt_executor)
59
59
  inventory = Puppet.lookup(:bolt_inventory)
60
60
 
61
- executor.report_function_call('run_script')
61
+ executor.report_function_call(self.class.name)
62
62
 
63
63
  found = Puppet::Parser::Files.find_file(script, scope.compiler.environment)
64
64
  unless found && Puppet::FileSystem.exist?(found)
@@ -56,7 +56,7 @@ Puppet::Functions.create_function(:run_task) do
56
56
  # Bolt calls this function internally to trigger tasks from the CLI. We
57
57
  # don't want to count those invocations.
58
58
  unless task_args['_bolt_api_call']
59
- executor.report_function_call('run_task')
59
+ executor.report_function_call(self.class.name)
60
60
  end
61
61
 
62
62
  # Report bundled content, this should capture tasks run from both CLI and Plans
@@ -32,7 +32,7 @@ Puppet::Functions.create_function(:set_feature) do
32
32
 
33
33
  inventory = Puppet.lookup(:bolt_inventory)
34
34
  executor = Puppet.lookup(:bolt_executor)
35
- executor.report_function_call('set_feature')
35
+ executor.report_function_call(self.class.name)
36
36
 
37
37
  inventory.set_feature(target, feature, value)
38
38
 
@@ -26,7 +26,7 @@ Puppet::Functions.create_function(:set_var) do
26
26
 
27
27
  inventory = Puppet.lookup(:bolt_inventory)
28
28
  executor = Puppet.lookup(:bolt_executor)
29
- executor.report_function_call('set_var')
29
+ executor.report_function_call(self.class.name)
30
30
 
31
31
  inventory.set_var(target, key, value)
32
32
  end
@@ -62,7 +62,7 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
62
62
  executor = Puppet.lookup(:bolt_executor)
63
63
  inventory = Puppet.lookup(:bolt_inventory)
64
64
 
65
- executor.report_function_call('upload_file')
65
+ executor.report_function_call(self.class.name)
66
66
 
67
67
  found = Puppet::Parser::Files.find_file(source, scope.compiler.environment)
68
68
  unless found && Puppet::FileSystem.exist?(found)
@@ -21,7 +21,7 @@ Puppet::Functions.create_function(:vars) do
21
21
  inventory = Puppet.lookup(:bolt_inventory)
22
22
  # Bolt executor not expected when invoked from apply block
23
23
  executor = Puppet.lookup(:bolt_executor) { nil }
24
- executor&.report_function_call('vars')
24
+ executor&.report_function_call(self.class.name)
25
25
 
26
26
  inventory.vars(target)
27
27
  end
@@ -29,7 +29,7 @@ Puppet::Functions.create_function(:wait_until_available) do
29
29
  executor = Puppet.lookup(:bolt_executor)
30
30
  inventory = Puppet.lookup(:bolt_inventory)
31
31
 
32
- executor.report_function_call('wait_until_available')
32
+ executor.report_function_call(self.class.name)
33
33
 
34
34
  # Ensure that given targets are all Target instances
35
35
  targets = inventory.get_targets(targets)
@@ -29,7 +29,7 @@ Puppet::Functions.create_function(:without_default_logging) do
29
29
  end
30
30
 
31
31
  executor = Puppet.lookup(:bolt_executor)
32
- executor.report_function_call('without_default_logging')
32
+ executor.report_function_call(self.class.name)
33
33
 
34
34
  executor.without_default_logging do
35
35
  yield
@@ -11,6 +11,7 @@ Puppet::Functions.create_function(:'ctrl::do_until') do
11
11
  end
12
12
 
13
13
  def do_until
14
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
14
15
  until (x = yield); end
15
16
  x
16
17
  end
@@ -11,6 +11,7 @@ Puppet::Functions.create_function(:'ctrl::sleep') do
11
11
  end
12
12
 
13
13
  def sleeper(period)
14
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
14
15
  sleep(period)
15
16
  nil
16
17
  end
@@ -14,6 +14,7 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
14
14
  end
15
15
 
16
16
  def exists(scope, filename)
17
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
17
18
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
18
19
  found && Puppet::FileSystem.exist?(found)
19
20
  end
@@ -14,6 +14,7 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
14
14
  end
15
15
 
16
16
  def read(scope, filename)
17
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
17
18
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
18
19
  unless found && Puppet::FileSystem.exist?(found)
19
20
  raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
@@ -14,6 +14,7 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
14
14
  end
15
15
 
16
16
  def readable(scope, filename)
17
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
17
18
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
18
19
  found && File.readable?(found)
19
20
  end
@@ -13,6 +13,7 @@ Puppet::Functions.create_function(:'file::write') do
13
13
  end
14
14
 
15
15
  def write(filename, content)
16
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
16
17
  File.write(filename, content)
17
18
  nil
18
19
  end
@@ -22,6 +22,7 @@ Puppet::Functions.create_function(:'out::message') do
22
22
  end
23
23
 
24
24
  executor = Puppet.lookup(:bolt_executor)
25
+ executor.report_function_call(self.class.name)
25
26
  executor.publish_event(type: :message, message: message)
26
27
 
27
28
  nil
@@ -11,6 +11,7 @@ Puppet::Functions.create_function(:'system::env') do
11
11
  end
12
12
 
13
13
  def env(name)
14
+ Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
14
15
  ENV[name]
15
16
  end
16
17
  end
data/lib/bolt/cli.rb CHANGED
@@ -310,7 +310,7 @@ module Bolt
310
310
  when 'plan'
311
311
  code = run_plan(options[:object], options[:task_options], options[:target_args], options)
312
312
  when 'puppetfile'
313
- code = install_puppetfile(@config.puppetfile, @config.modulepath)
313
+ code = install_puppetfile(@config.puppetfile_config, @config.puppetfile, @config.modulepath)
314
314
  when 'apply'
315
315
  if options[:object]
316
316
  validate_file('manifest', options[:object])
@@ -451,18 +451,21 @@ module Bolt
451
451
  outputter.print_module_list(pal.list_modules)
452
452
  end
453
453
 
454
- def install_puppetfile(puppetfile, modulepath)
455
- require 'r10k/action/puppetfile/install'
454
+ def install_puppetfile(config, puppetfile, modulepath)
455
+ require 'r10k/cli'
456
456
  require 'bolt/r10k_log_proxy'
457
457
 
458
458
  if puppetfile.exist?
459
459
  moduledir = modulepath.first.to_s
460
- r10k_config = {
460
+ r10k_opts = {
461
461
  root: puppetfile.dirname.to_s,
462
462
  puppetfile: puppetfile.to_s,
463
463
  moduledir: moduledir
464
464
  }
465
- install_action = R10K::Action::Puppetfile::Install.new(r10k_config, nil)
465
+
466
+ settings = R10K::Settings.global_settings.evaluate(config)
467
+ R10K::Initializers::GlobalInitializer.new(settings).call
468
+ install_action = R10K::Action::Puppetfile::Install.new(r10k_opts, nil)
466
469
 
467
470
  # Override the r10k logger with a proxy to our own logger
468
471
  R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
data/lib/bolt/config.rb CHANGED
@@ -32,13 +32,16 @@ module Bolt
32
32
 
33
33
  class Config
34
34
  attr_accessor :concurrency, :format, :trace, :log, :puppetdb, :color, :save_rerun,
35
- :transport, :transports, :inventoryfile, :compile_concurrency, :boltdir
35
+ :transport, :transports, :inventoryfile, :compile_concurrency, :boltdir,
36
+ :puppetfile_config
36
37
  attr_writer :modulepath
37
38
 
38
39
  TRANSPORT_OPTIONS = %i[password run-as sudo-password extensions
39
40
  private-key tty tmpdir user connect-timeout
40
41
  cacert token-file service-url interpreters file-protocol smb-port].freeze
41
42
 
43
+ PUPPETFILE_OPTIONS = %w[proxy].freeze
44
+
42
45
  def self.default
43
46
  new(Bolt::Boltdir.new('.'), {})
44
47
  end
@@ -66,6 +69,7 @@ module Bolt
66
69
  @puppetdb = {}
67
70
  @color = true
68
71
  @save_rerun = true
72
+ @puppetfile_config = {}
69
73
 
70
74
  # add an entry for the default console logger
71
75
  @log = { 'console' => {} }
@@ -145,6 +149,10 @@ module Bolt
145
149
 
146
150
  @inventoryfile = File.expand_path(data['inventoryfile'], @boltdir.path) if data.key?('inventoryfile')
147
151
 
152
+ if data.key?('puppetfile')
153
+ @puppetfile_config = data['puppetfile'].select { |k, _| PUPPETFILE_OPTIONS.include?(k) }
154
+ end
155
+
148
156
  @hiera_config = File.expand_path(data['hiera-config'], @boltdir.path) if data.key?('hiera-config')
149
157
  @compile_concurrency = data['compile-concurrency'] if data.key?('compile-concurrency')
150
158
 
@@ -16,11 +16,16 @@ module Bolt
16
16
  GROUP_KEYS = DATA_KEYS + %w[groups targets target-lookups]
17
17
  CONFIG_KEYS = Bolt::TRANSPORTS.keys.map(&:to_s) + ['transport']
18
18
 
19
- def initialize(data)
19
+ def initialize(data, plugins)
20
20
  @logger = Logging.logger[self]
21
-
22
21
  raise ValidationError.new("Expected group to be a Hash, not #{data.class}", nil) unless data.is_a?(Hash)
22
+ raise ValidationError.new("Cannot set group with plugin", nil) if data.key?('_plugin')
23
23
  raise ValidationError.new("Group does not have a name", nil) unless data.key?('name')
24
+ @plugins = plugins
25
+
26
+ %w[name vars features facts].each do |key|
27
+ validate_config_plugin(data[key], key, nil)
28
+ end
24
29
 
25
30
  @name = data['name']
26
31
  raise ValidationError.new("Group name must be a String, not #{@name.inspect}", nil) unless @name.is_a?(String)
@@ -34,7 +39,8 @@ module Bolt
34
39
  @vars = fetch_value(data, 'vars', Hash)
35
40
  @facts = fetch_value(data, 'facts', Hash)
36
41
  @features = fetch_value(data, 'features', Array)
37
- @config = fetch_value(data, 'config', Hash)
42
+
43
+ @config = config_only_plugin(fetch_value(data, 'config', Hash))
38
44
 
39
45
  @target_lookups = fetch_value(data, 'target-lookups', Array)
40
46
 
@@ -60,9 +66,41 @@ module Bolt
60
66
  end
61
67
  end
62
68
 
63
- @groups = groups.map { |g| Group2.new(g) }
69
+ @groups = groups.map { |g| Group2.new(g, plugins) }
64
70
  end
65
71
 
72
+ def validate_config_plugin(data, key, group_name = nil)
73
+ if data.is_a?(Hash) && data.include?('_plugin')
74
+ if group_name
75
+ raise ValidationError.new("Cannot set target #{key.inspect} with plugin", group_name)
76
+ else
77
+ raise ValidationError.new("Cannot set group #{key.inspect} with plugin", nil)
78
+ end
79
+ end
80
+ if data.is_a? Hash
81
+ data.each do |_k, v|
82
+ validate_config_plugin(v, key, group_name)
83
+ end
84
+ elsif data.is_a? Array
85
+ data.map { |v| validate_config_plugin(v, key, group_name) }
86
+ end
87
+ end
88
+ private :validate_config_plugin
89
+
90
+ def config_only_plugin(data)
91
+ Bolt::Util.walk_vals(data) do |value|
92
+ if value.is_a?(Hash) && value.include?('_plugin')
93
+ unless (plugin = @plugins.by_name(value['_plugin']))
94
+ raise ValidationError.new("unkown plugin: #{value['_plugin'].inspect}", nil)
95
+ end
96
+ plugin.new.inventory_config_lookup(value)
97
+ else
98
+ value
99
+ end
100
+ end
101
+ end
102
+ private :config_only_plugin
103
+
66
104
  def target_data(target_name)
67
105
  if (data = @targets[target_name])
68
106
  { 'config' => data['config'] || {},
@@ -83,6 +121,11 @@ module Bolt
83
121
  unless target.is_a?(Hash)
84
122
  raise ValidationError.new("Node entry must be a String or Hash, not #{target.class}", @name)
85
123
  end
124
+ raise ValidationError.new("Cannot set target with plugin", @name) if target.key?('_plugin')
125
+ target.each do |k, v|
126
+ next if k == 'config'
127
+ validate_config_plugin(v, k, @name)
128
+ end
86
129
 
87
130
  target['name'] ||= target['uri']
88
131
 
@@ -113,6 +156,7 @@ module Bolt
113
156
  @logger.warn(msg)
114
157
  end
115
158
 
159
+ target['config'] = config_only_plugin(target['config'])
116
160
  unless target.include?('alias')
117
161
  return
118
162
  end
@@ -145,7 +189,6 @@ module Bolt
145
189
 
146
190
  unless (plugin = plugins.by_name(lookup['plugin']))
147
191
  raise ValidationError.new("target-lookup specifies an unkown plugin: '#{lookup['plugin']}'", @name)
148
-
149
192
  end
150
193
 
151
194
  targets = plugin.lookup_targets(lookup)
@@ -16,13 +16,12 @@ module Bolt
16
16
  @logger = Logging.logger[self]
17
17
  # Config is saved to add config options to targets
18
18
  @config = config || Bolt::Config.default
19
- @data = data ||= {}
20
- @groups = Group2.new(data.merge('name' => 'all'))
19
+ @data = data || {}
20
+ @groups = Group2.new(@data.merge('name' => 'all'), plugins)
21
21
  @group_lookup = {}
22
22
  @target_vars = target_vars
23
23
  @target_facts = target_facts
24
24
  @target_features = target_features
25
-
26
25
  @groups.lookup_targets(plugins)
27
26
  @groups.resolve_aliases(@groups.target_aliases, @groups.target_names)
28
27
  collect_groups
@@ -103,13 +102,13 @@ module Bolt
103
102
 
104
103
  def data_hash
105
104
  {
106
- data: @data,
105
+ data: {},
107
106
  target_hash: {
108
- target_vars: @target_vars,
109
- target_facts: @target_facts,
110
- target_features: @target_features
107
+ target_vars: {},
108
+ target_facts: {},
109
+ target_features: {}
111
110
  },
112
- config: @config.transport_data_get
111
+ config: { transports: {} }
113
112
  }
114
113
  end
115
114
 
@@ -121,6 +120,18 @@ module Bolt
121
120
  end
122
121
  private :groups_in
123
122
 
123
+ # Look for _plugins
124
+ def config_plugin(data)
125
+ Bolt::Util.walk_vals(data) do |val|
126
+ if val.is_a?(Concurrent::Delay)
127
+ val.value
128
+ else
129
+ val
130
+ end
131
+ end
132
+ end
133
+ private :config_plugin
134
+
124
135
  # Pass a target to get_targets for a public version of this
125
136
  # Should this reconfigure configured targets?
126
137
  def update_target(target)
@@ -138,6 +149,7 @@ module Bolt
138
149
  set_vars_from_hash(target.name, data['vars']) unless @target_vars[target.name]
139
150
  set_facts(target.name, data['facts']) unless @target_facts[target.name]
140
151
  data['features']&.each { |feature| set_feature(target, feature) } unless @target_features[target.name]
152
+ data['config'] = config_plugin(data['config'])
141
153
 
142
154
  # Use Config object to ensure config section is treated consistently with config file
143
155
  conf = @config.deep_clone
data/lib/bolt/plugin.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'bolt/plugin/puppetdb'
4
4
  require 'bolt/plugin/terraform'
5
+ require 'bolt/plugin/prompt'
5
6
 
6
7
  module Bolt
7
8
  class Plugin
@@ -9,6 +10,7 @@ module Bolt
9
10
  plugins = new(config)
10
11
  plugins.add_plugin(Bolt::Plugin::Puppetdb.new(pdb_client))
11
12
  plugins.add_plugin(Bolt::Plugin::Terraform.new)
13
+ plugins.add_plugin(Bolt::Plugin::Prompt)
12
14
  plugins
13
15
  end
14
16
 
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'concurrent/delay'
4
+ module Bolt
5
+ class Plugin
6
+ class Prompt
7
+ def initialize
8
+ # Might not need this
9
+ @logger = Logging.logger[self]
10
+ end
11
+
12
+ def self.name
13
+ 'prompt'
14
+ end
15
+
16
+ def hooks
17
+ ['inventory_config_lookup']
18
+ end
19
+
20
+ def inventory_config_lookup(opts)
21
+ raise Bolt::ValidationError, "Prompt requires a 'message'" unless opts['message']
22
+ # Return a delay to only be evaluated when needed
23
+ Concurrent::Delay.new do
24
+ STDOUT.print "#{opts['message']}:"
25
+ value = STDIN.noecho(&:gets).chomp
26
+ STDOUT.puts
27
+ value
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -24,29 +24,22 @@ module Bolt
24
24
  def lookup_targets(opts)
25
25
  state = load_statefile(opts)
26
26
 
27
- resources = state.fetch('modules', {}).flat_map do |mod|
28
- mod.fetch('resources', {}).map do |name, resource|
29
- [name, resource.dig('primary', 'attributes')]
30
- end
31
- end
27
+ resources = extract_resources(state)
32
28
 
33
29
  regex = Regexp.new(opts['resource_type'])
34
30
 
35
31
  resources.select do |name, _resource|
36
32
  name.match?(regex)
37
33
  end.map do |name, resource|
38
- unless resource.key?(opts['uri'])
39
- warn_missing_property(name, opts['uri'])
40
- next
41
- end
34
+ target = {}
42
35
 
43
- target = { 'uri' => resource[opts['uri']] }
36
+ if opts.key?('uri')
37
+ uri = lookup(name, resource, opts['uri'])
38
+ target['uri'] = uri if uri
39
+ end
44
40
  if opts.key?('name')
45
- if resource.key?(opts['name'])
46
- target['name'] = resource[opts['name']]
47
- else
48
- warn_missing_property(name, opts['name'])
49
- end
41
+ real_name = lookup(name, resource, opts['name'])
42
+ target['name'] = real_name if real_name
50
43
  end
51
44
  if opts.key?('config')
52
45
  target['config'] = resolve_config(name, resource, opts['config'])
@@ -65,15 +58,57 @@ module Bolt
65
58
  raise Bolt::FileError.new("Could not load Terraform state file #{filename}: #{e}", filename)
66
59
  end
67
60
 
61
+ # Format the list of resources into a list of [name, attribute map]
62
+ # pairs. This method handles both version 4 and earlier statefiles, doing
63
+ # the appropriate munging based on the shape of the data.
64
+ def extract_resources(state)
65
+ if state['version'] >= 4
66
+ state.fetch('resources', []).flat_map do |resource_set|
67
+ prefix = "#{resource_set['type']}.#{resource_set['name']}"
68
+ resource_set['instances'].map do |resource|
69
+ instance_name = prefix
70
+ instance_name += ".#{resource['index_key']}" if resource['index_key']
71
+
72
+ [instance_name, resource['attributes']]
73
+ end
74
+ end
75
+ else
76
+ state.fetch('modules', {}).flat_map do |mod|
77
+ mod.fetch('resources', {}).map do |name, resource|
78
+ [name, resource.dig('primary', 'attributes')]
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ # Look up a nested value from the resource attributes. The key is of the
85
+ # form `foo.bar.0.baz`. For terraform statefile version 3, this will
86
+ # exactly correspond to a key in the resource. In version 4, it will
87
+ # correspond to a nested hash entry at {foo: {bar: [{baz: <value>}]}}
88
+ # For simplicity's sake, we just check both.
89
+ def lookup(name, resource, path)
90
+ segments = path.split('.').map do |segment|
91
+ begin
92
+ Integer(segment)
93
+ rescue ArgumentError
94
+ segment
95
+ end
96
+ end
97
+
98
+ value = resource[path] || resource.dig(*segments)
99
+ unless value
100
+ warn_missing_property(name, path)
101
+ end
102
+ value
103
+ end
104
+
105
+ # Walk the "template" config mapping provided in the plugin config and
106
+ # replace all values with the corresponding value from the resource
107
+ # parameters.
68
108
  def resolve_config(name, resource, config_template)
69
109
  Bolt::Util.walk_vals(config_template) do |value|
70
110
  if value.is_a?(String)
71
- if resource.key?(value)
72
- resource[value]
73
- else
74
- warn_missing_property(name, value)
75
- nil
76
- end
111
+ lookup(name, resource, value)
77
112
  else
78
113
  value
79
114
  end
data/lib/bolt/task.rb CHANGED
@@ -20,7 +20,6 @@ module Bolt
20
20
  :files,
21
21
  :metadata
22
22
  ) do
23
-
24
23
  attr_reader :remote
25
24
 
26
25
  def initialize(task, remote: false)
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.21.0'
4
+ VERSION = '1.22.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.21.0
4
+ version: 1.22.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-05-29 00:00:00.000000000 Z
11
+ date: 2019-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -388,6 +388,7 @@ files:
388
388
  - lib/bolt/pal/yaml_plan/transpiler.rb
389
389
  - lib/bolt/plan_result.rb
390
390
  - lib/bolt/plugin.rb
391
+ - lib/bolt/plugin/prompt.rb
391
392
  - lib/bolt/plugin/puppetdb.rb
392
393
  - lib/bolt/plugin/terraform.rb
393
394
  - lib/bolt/puppetdb.rb