bolt 2.12.0 → 2.17.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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +1 -1
  3. data/bolt-modules/boltlib/lib/puppet/datatypes/resourceinstance.rb +3 -2
  4. data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -0
  5. data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
  6. data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +1 -1
  7. data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -0
  8. data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -0
  9. data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -0
  10. data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +2 -1
  11. data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +1 -0
  12. data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -0
  13. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -0
  14. data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -0
  15. data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
  16. data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +1 -0
  17. data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
  18. data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +3 -0
  19. data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +7 -2
  20. data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +7 -4
  21. data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +6 -3
  22. data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +8 -2
  23. data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +1 -0
  24. data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
  25. data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +66 -43
  26. data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -0
  27. data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -0
  28. data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -0
  29. data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -0
  30. data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -0
  31. data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
  32. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
  33. data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +2 -0
  34. data/bolt-modules/file/lib/puppet/functions/file/exists.rb +2 -1
  35. data/bolt-modules/file/lib/puppet/functions/file/join.rb +2 -0
  36. data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -1
  37. data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
  38. data/bolt-modules/file/lib/puppet/functions/file/write.rb +2 -0
  39. data/bolt-modules/out/lib/puppet/functions/out/message.rb +2 -0
  40. data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +1 -0
  41. data/bolt-modules/system/lib/puppet/functions/system/env.rb +2 -0
  42. data/lib/bolt/applicator.rb +20 -7
  43. data/lib/bolt/apply_inventory.rb +4 -0
  44. data/lib/bolt/apply_target.rb +4 -0
  45. data/lib/bolt/bolt_option_parser.rb +20 -13
  46. data/lib/bolt/catalog.rb +81 -68
  47. data/lib/bolt/cli.rb +16 -8
  48. data/lib/bolt/config.rb +150 -138
  49. data/lib/bolt/config/options.rb +473 -0
  50. data/lib/bolt/config/transport/base.rb +16 -16
  51. data/lib/bolt/config/transport/docker.rb +9 -23
  52. data/lib/bolt/config/transport/local.rb +6 -44
  53. data/lib/bolt/config/transport/options.rb +454 -0
  54. data/lib/bolt/config/transport/orch.rb +9 -18
  55. data/lib/bolt/config/transport/remote.rb +3 -6
  56. data/lib/bolt/config/transport/ssh.rb +59 -114
  57. data/lib/bolt/config/transport/winrm.rb +18 -47
  58. data/lib/bolt/executor.rb +14 -1
  59. data/lib/bolt/inventory/group.rb +1 -1
  60. data/lib/bolt/inventory/inventory.rb +4 -14
  61. data/lib/bolt/inventory/target.rb +22 -5
  62. data/lib/bolt/logger.rb +3 -1
  63. data/lib/bolt/outputter.rb +3 -0
  64. data/lib/bolt/outputter/rainbow.rb +84 -0
  65. data/lib/bolt/pal.rb +24 -9
  66. data/lib/bolt/project.rb +64 -55
  67. data/lib/bolt/resource_instance.rb +10 -3
  68. data/lib/bolt/shell/bash.rb +30 -42
  69. data/lib/bolt/shell/powershell.rb +13 -8
  70. data/lib/bolt/shell/powershell/snippets.rb +8 -0
  71. data/lib/bolt/transport/docker.rb +9 -5
  72. data/lib/bolt/transport/local/connection.rb +2 -1
  73. data/lib/bolt/transport/orch.rb +8 -0
  74. data/lib/bolt/transport/ssh/connection.rb +35 -0
  75. data/lib/bolt/version.rb +1 -1
  76. data/lib/bolt_spec/bolt_context.rb +1 -1
  77. data/lib/bolt_spec/run.rb +1 -1
  78. metadata +22 -18
@@ -27,6 +27,7 @@ Puppet::Functions.create_function(:set_var) do
27
27
 
28
28
  inventory = Puppet.lookup(:bolt_inventory)
29
29
  executor = Puppet.lookup(:bolt_executor)
30
+ # Send Analytics Report
30
31
  executor.report_function_call(self.class.name)
31
32
 
32
33
  var_hash = { key => value }
@@ -67,6 +67,7 @@ Puppet::Functions.create_function(:upload_file, Puppet::Functions::InternalFunct
67
67
  executor = Puppet.lookup(:bolt_executor)
68
68
  inventory = Puppet.lookup(:bolt_inventory)
69
69
 
70
+ # Send Analytics Report
70
71
  executor.report_function_call(self.class.name)
71
72
 
72
73
  found = Puppet::Parser::Files.find_file(source, scope.compiler.environment)
@@ -21,6 +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
+ # Send Analytics Report
24
25
  executor&.report_function_call(self.class.name)
25
26
 
26
27
  inventory.vars(target)
@@ -33,6 +33,7 @@ Puppet::Functions.create_function(:wait_until_available) do
33
33
  executor = Puppet.lookup(:bolt_executor)
34
34
  inventory = Puppet.lookup(:bolt_inventory)
35
35
 
36
+ # Send Analytics Report
36
37
  executor.report_function_call(self.class.name)
37
38
 
38
39
  # Ensure that given targets are all Target instances
@@ -29,6 +29,7 @@ Puppet::Functions.create_function(:without_default_logging) do
29
29
  end
30
30
 
31
31
  executor = Puppet.lookup(:bolt_executor)
32
+ # Send Analytics Report
32
33
  executor.report_function_call(self.class.name)
33
34
 
34
35
  executor.without_default_logging do
@@ -31,6 +31,7 @@ Puppet::Functions.create_function(:write_file) do
31
31
  end
32
32
 
33
33
  executor = Puppet.lookup(:bolt_executor)
34
+ # Send Analytics Report
34
35
  executor.report_function_call(self.class.name)
35
36
 
36
37
  inventory = Puppet.lookup(:bolt_inventory)
@@ -20,7 +20,9 @@ Puppet::Functions.create_function(:'ctrl::do_until') do
20
20
  end
21
21
 
22
22
  def do_until(options = { 'limit' => 0 })
23
+ # Send Analytics Report
23
24
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
25
+
24
26
  limit = options['limit']
25
27
  i = 0
26
28
  until (x = yield)
@@ -11,7 +11,9 @@ Puppet::Functions.create_function(:'ctrl::sleep') do
11
11
  end
12
12
 
13
13
  def sleeper(period)
14
+ # Send Analytics Report
14
15
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
16
+
15
17
  sleep(period)
16
18
  nil
17
19
  end
@@ -9,7 +9,7 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
9
9
  # @example Check a file on disk
10
10
  # file::exists('/tmp/i_dumped_this_here')
11
11
  # @example check a file from the modulepath
12
- # file::exists('example/files/VERSION')
12
+ # file::exists('example/VERSION')
13
13
  dispatch :exists do
14
14
  scope_param
15
15
  required_param 'String', :filename
@@ -17,6 +17,7 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
17
17
  end
18
18
 
19
19
  def exists(scope, filename)
20
+ # Send Analytics Report
20
21
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
21
22
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
22
23
  found ? Puppet::FileSystem.exist?(found) : false
@@ -12,7 +12,9 @@ Puppet::Functions.create_function(:'file::join') do
12
12
  end
13
13
 
14
14
  def join(*paths)
15
+ # Send Analytics Report
15
16
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
17
+
16
18
  File.join(paths)
17
19
  end
18
20
  end
@@ -8,7 +8,7 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
8
8
  # @example Read a file from disk
9
9
  # file::read('/tmp/i_dumped_this_here')
10
10
  # @example Read a file from the modulepath
11
- # file::read('example/files/VERSION')
11
+ # file::read('example/VERSION')
12
12
  dispatch :read do
13
13
  scope_param
14
14
  required_param 'String', :filename
@@ -16,7 +16,9 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
16
16
  end
17
17
 
18
18
  def read(scope, filename)
19
+ # Send Analytics Report
19
20
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
21
+
20
22
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
21
23
  unless found && Puppet::FileSystem.exist?(found)
22
24
  raise Puppet::ParseErrorWithIssue.from_issue_and_stack(
@@ -9,7 +9,7 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
9
9
  # @example Check a file on disk
10
10
  # file::readable('/tmp/i_dumped_this_here')
11
11
  # @example check a file from the modulepath
12
- # file::readable('example/files/VERSION')
12
+ # file::readable('example/VERSION')
13
13
  dispatch :readable do
14
14
  scope_param
15
15
  required_param 'String', :filename
@@ -17,7 +17,9 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
17
17
  end
18
18
 
19
19
  def readable(scope, filename)
20
+ # Send Analytics Report
20
21
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
22
+
21
23
  found = Puppet::Parser::Files.find_file(filename, scope.compiler.environment)
22
24
  found ? File.readable?(found) : false
23
25
  end
@@ -15,7 +15,9 @@ Puppet::Functions.create_function(:'file::write') do
15
15
  end
16
16
 
17
17
  def write(filename, content)
18
+ # Send Analytics Report
18
19
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
20
+
19
21
  File.write(filename, content)
20
22
  nil
21
23
  end
@@ -23,7 +23,9 @@ Puppet::Functions.create_function(:'out::message') do
23
23
  end
24
24
 
25
25
  executor = Puppet.lookup(:bolt_executor)
26
+ # Send Analytics Report
26
27
  executor.report_function_call(self.class.name)
28
+
27
29
  executor.publish_event(type: :message, message: message)
28
30
 
29
31
  nil
@@ -30,6 +30,7 @@ Puppet::Functions.create_function(:prompt) do
30
30
  options = options.transform_keys(&:to_sym)
31
31
 
32
32
  executor = Puppet.lookup(:bolt_executor)
33
+ # Send analytics report
33
34
  executor.report_function_call(self.class.name)
34
35
 
35
36
  response = executor.prompt(prompt, options)
@@ -12,7 +12,9 @@ Puppet::Functions.create_function(:'system::env') do
12
12
  end
13
13
 
14
14
  def env(name)
15
+ # Send analytics report
15
16
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
17
+
16
18
  ENV[name]
17
19
  end
18
20
  end
@@ -14,14 +14,16 @@ require 'open3'
14
14
 
15
15
  module Bolt
16
16
  class Applicator
17
- def initialize(inventory, executor, modulepath, plugin_dirs, pdb_client, hiera_config, max_compiles, apply_settings)
17
+ def initialize(inventory, executor, modulepath, plugin_dirs, project,
18
+ pdb_client, hiera_config, max_compiles, apply_settings)
18
19
  # lazy-load expensive gem code
19
20
  require 'concurrent'
20
21
 
21
22
  @inventory = inventory
22
23
  @executor = executor
23
- @modulepath = modulepath
24
+ @modulepath = modulepath || []
24
25
  @plugin_dirs = plugin_dirs
26
+ @project = project
25
27
  @pdb_client = pdb_client
26
28
  @hiera_config = hiera_config ? validate_hiera_config(hiera_config) : nil
27
29
  @apply_settings = apply_settings || {}
@@ -104,6 +106,16 @@ module Bolt
104
106
  out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_input.to_json)
105
107
  ENV['PATH'] = old_path
106
108
 
109
+ # If bolt_catalog does not return valid JSON, we should print stderr to
110
+ # see what happened
111
+ print_logs = stat.success?
112
+ result = begin
113
+ JSON.parse(out)
114
+ rescue JSON::ParserError
115
+ print_logs = true
116
+ { 'message' => "Something's gone terribly wrong! STDERR is logged." }
117
+ end
118
+
107
119
  # Any messages logged by Puppet will be on stderr as JSON hashes, so we
108
120
  # parse those and store them here. Any message on stderr that is not
109
121
  # properly JSON formatted is assumed to be an error message. If
@@ -117,17 +129,15 @@ module Bolt
117
129
  { 'level' => 'err', 'message' => line }
118
130
  end
119
131
 
120
- result = JSON.parse(out)
121
- if stat.success?
132
+ if print_logs
122
133
  logs.each do |log|
123
134
  bolt_level = Bolt::Util::PuppetLogLevel::MAPPING[log['level'].to_sym]
124
135
  message = log['message'].chomp
125
136
  @logger.send(bolt_level, "#{target.name}: #{message}")
126
137
  end
127
- result
128
- else
129
- raise ApplyError.new(target.name, result['message'])
130
138
  end
139
+ raise ApplyError.new(target.name, result['message']) unless stat.success?
140
+ result
131
141
  end
132
142
 
133
143
  def validate_hiera_config(hiera_config)
@@ -144,6 +154,7 @@ module Bolt
144
154
 
145
155
  def apply(args, apply_body, scope)
146
156
  raise(ArgumentError, 'apply requires a TargetSpec') if args.empty?
157
+ raise(ArgumentError, 'apply requires at least one statement in the apply block') if apply_body.nil?
147
158
  type0 = Puppet.lookup(:pal_script_compiler).type('TargetSpec')
148
159
  Puppet::Pal.assert_type(type0, args[0], 'apply targets')
149
160
 
@@ -185,9 +196,11 @@ module Bolt
185
196
  type_by_reference: true,
186
197
  local_reference: true)
187
198
 
199
+ bolt_project = @project if @project&.name
188
200
  scope = {
189
201
  code_ast: ast,
190
202
  modulepath: @modulepath,
203
+ project: bolt_project.to_h,
191
204
  pdb_config: @pdb_client.config.to_hash,
192
205
  hiera_config: @hiera_config,
193
206
  plan_vars: plan_vars,
@@ -70,6 +70,10 @@ module Bolt
70
70
  @targets[target.name].features
71
71
  end
72
72
 
73
+ def resource(target, type, title)
74
+ @targets[target.name].resource(type, title)
75
+ end
76
+
73
77
  def add_to_group(*_params)
74
78
  raise InvalidFunctionCall, 'add_to_group'
75
79
  end
@@ -62,6 +62,10 @@ module Bolt
62
62
  @safe_name
63
63
  end
64
64
 
65
+ def resource(type, title)
66
+ resources[Bolt::ResourceInstance.format_reference(type, title)]
67
+ end
68
+
65
69
  def parse_uri(string)
66
70
  require 'addressable/uri'
67
71
  if string.nil?
@@ -10,10 +10,10 @@ module Bolt
10
10
  authentication: %w[user password password-prompt private-key host-key-check ssl ssl-verify],
11
11
  escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
12
12
  run_context: %w[concurrency inventoryfile save-rerun cleanup],
13
- global_config_setters: %w[modulepath boltdir configfile],
13
+ global_config_setters: %w[modulepath project configfile],
14
14
  transports: %w[transport connect-timeout tty ssh-command copy-command],
15
15
  display: %w[format color verbose trace],
16
- global: %w[help version debug] }.freeze
16
+ global: %w[help version debug log-level] }.freeze
17
17
 
18
18
  ACTION_OPTS = OPTIONS.values.flatten.freeze
19
19
 
@@ -422,7 +422,7 @@ module Bolt
422
422
 
423
423
  ACTIONS
424
424
  generate-types Generate type references to register in plans
425
- install Install modules from a Puppetfile into a Boltdir
425
+ install Install modules from a Puppetfile into a project
426
426
  show-modules List modules available to the Bolt project
427
427
  HELP
428
428
 
@@ -445,7 +445,7 @@ module Bolt
445
445
  bolt puppetfile install [options]
446
446
 
447
447
  DESCRIPTION
448
- Install modules from a Puppetfile into a Boltdir
448
+ Install modules from a Puppetfile into a project
449
449
  HELP
450
450
 
451
451
  PUPPETFILE_SHOWMODULES_HELP = <<~HELP
@@ -709,29 +709,29 @@ module Bolt
709
709
  File.expand_path(moduledir)
710
710
  end
711
711
  end
712
- define('--boltdir FILEPATH',
713
- 'Specify what Boltdir to load config from (default: autodiscovered from current working dir)') do |path|
712
+ define('--project PATH', '--boltdir PATH',
713
+ 'Specify what project to load config from (default: autodiscovered from current working dir)') do |path|
714
714
  @options[:boltdir] = path
715
715
  end
716
- define('--configfile FILEPATH',
716
+ define('--configfile PATH',
717
717
  'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml).',
718
- 'Directory containing bolt.yaml will be used as the Boltdir.') do |path|
718
+ 'Directory containing bolt.yaml will be used as the project directory.') do |path|
719
719
  @options[:configfile] = path
720
720
  end
721
- define('--hiera-config FILEPATH',
721
+ define('--hiera-config PATH',
722
722
  'Specify where to load Hiera config from (default: ~/.puppetlabs/bolt/hiera.yaml)') do |path|
723
723
  @options[:'hiera-config'] = File.expand_path(path)
724
724
  end
725
- define('-i', '--inventoryfile FILEPATH',
725
+ define('-i', '--inventoryfile PATH',
726
726
  'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
727
727
  if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
728
728
  raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
729
729
  end
730
730
  @options[:inventoryfile] = Pathname.new(File.expand_path(path))
731
731
  end
732
- define('--puppetfile FILEPATH',
732
+ define('--puppetfile PATH',
733
733
  'Specify a Puppetfile to use when installing modules. (default: ~/.puppetlabs/bolt/Puppetfile)',
734
- 'Modules are installed in the current Boltdir.') do |path|
734
+ 'Modules are installed in the current project.') do |path|
735
735
  @options[:puppetfile_path] = Pathname.new(File.expand_path(path))
736
736
  end
737
737
  define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save|
@@ -803,8 +803,15 @@ module Bolt
803
803
  end
804
804
  define('--debug', 'Display debug logging') do |_|
805
805
  @options[:debug] = true
806
+ # We don't actually set '--log-level debug' here, but once the options are evaluated by
807
+ # the config class the end result is the same.
808
+ @warnings << { msg: "Command line option '--debug' is deprecated, set '--log-level debug' instead." }
809
+ end
810
+ define('--log-level LEVEL',
811
+ "Set the log level for the console. Available options are",
812
+ "debug, info, notice, warn, error, fatal, any.") do |level|
813
+ @options[:log] = { 'console' => { 'level' => level } }
806
814
  end
807
-
808
815
  define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
809
816
  @options[:plugin] = plug
810
817
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bolt/apply_inventory'
3
4
  require 'bolt/apply_target'
4
5
  require 'bolt/config'
5
6
  require 'bolt/error'
6
7
  require 'bolt/inventory'
7
- require 'bolt/apply_inventory'
8
8
  require 'bolt/pal'
9
9
  require 'bolt/puppetdb'
10
10
  require 'bolt/util'
@@ -19,7 +19,7 @@ module Bolt
19
19
  @log_level = log_level
20
20
  end
21
21
 
22
- def with_puppet_settings(hiera_config = {})
22
+ def with_puppet_settings(overrides = {})
23
23
  Dir.mktmpdir('bolt') do |dir|
24
24
  cli = []
25
25
  Puppet::Settings::REQUIRED_APP_SETTINGS.each do |setting|
@@ -31,7 +31,9 @@ module Bolt
31
31
  Puppet.settings.override_default(:vendormoduledir, '')
32
32
 
33
33
  Puppet.initialize_settings(cli)
34
- Puppet.settings[:hiera_config] = hiera_config
34
+ overrides.each do |setting, value|
35
+ Puppet.settings[setting] = value
36
+ end
35
37
 
36
38
  # Use a special logdest that serializes all log messages and their level to stderr.
37
39
  Puppet::Util::Log.newdestination(:stderr)
@@ -54,80 +56,51 @@ module Bolt
54
56
  end
55
57
 
56
58
  def compile_catalog(request)
57
- pal_main = request['code_ast'] || request['code_string']
58
- target = request['target']
59
59
  pdb_client = Bolt::PuppetDB::Client.new(Bolt::PuppetDB::Config.new(request['pdb_config']))
60
- options = request['puppet_config'] || {}
61
- with_puppet_settings(request['hiera_config']) do
62
- Puppet[:rich_data] = true
63
- Puppet[:node_name_value] = target['name']
64
- env_conf = { modulepath: request['modulepath'] || [],
65
- facts: target['facts'] || {} }
66
- env_conf[:variables] = {}
60
+ project = request['project'] || {}
61
+ bolt_project = Struct.new(:name, :path).new(project['name'], project['path']) unless project.empty?
62
+ inv = Bolt::ApplyInventory.new(request['config'])
63
+ puppet_overrides = {
64
+ bolt_pdb_client: pdb_client,
65
+ bolt_inventory: inv,
66
+ bolt_project: bolt_project
67
+ }
68
+
69
+ # Facts will be set by the catalog compiler, so we need to ensure
70
+ # that any plan or target variables with the same name are not
71
+ # passed into the apply block to avoid a redefinition error.
72
+ # Filter out plan and target vars separately and raise a Puppet
73
+ # warning if there are any collisions for either. Puppet warning
74
+ # is the only way to log a message that will make it back to Bolt
75
+ # to be printed.
76
+ target = request['target']
77
+ plan_vars = shadow_vars('plan', request['plan_vars'], target['facts'])
78
+ target_vars = shadow_vars('target', target['variables'], target['facts'])
79
+ topscope_vars = target_vars.merge(plan_vars)
80
+ env_conf = { modulepath: request['modulepath'],
81
+ facts: target['facts'],
82
+ variables: topscope_vars }
83
+
84
+ puppet_settings = {
85
+ node_name_value: target['name'],
86
+ hiera_config: request['hiera_config']
87
+ }
88
+
89
+ with_puppet_settings(puppet_settings) do
67
90
  Puppet::Pal.in_tmp_environment('bolt_catalog', env_conf) do |pal|
68
- inv = Bolt::ApplyInventory.new(request['config'])
69
- Puppet.override(bolt_pdb_client: pdb_client,
70
- bolt_inventory: inv) do
91
+ Puppet.override(puppet_overrides) do
71
92
  Puppet.lookup(:pal_current_node).trusted_data = target['trusted']
72
93
  pal.with_catalog_compiler do |compiler|
73
- # Deserializing needs to happen inside the catalog compiler so
74
- # loaders are initialized for loading
75
- plan_vars = Puppet::Pops::Serialization::FromDataConverter.convert(request['plan_vars'])
76
-
77
- # Facts will be set by the catalog compiler, so we need to ensure
78
- # that any plan or target variables with the same name are not
79
- # passed into the apply block to avoid a redefinition error.
80
- # Filter out plan and target vars separately and raise a Puppet
81
- # warning if there are any collisions for either. Puppet warning
82
- # is the only way to log a message that will make it back to Bolt
83
- # to be printed.
84
- pv_collisions, pv_filtered = plan_vars.partition do |k, _|
85
- target['facts'].keys.include?(k)
86
- end.map(&:to_h)
87
- unless pv_collisions.empty?
88
- print_pv = pv_collisions.keys.map { |k| "$#{k}" }.join(', ')
89
- plural = pv_collisions.keys.length == 1 ? '' : 's'
90
- Puppet.warning("Plan variable#{plural} #{print_pv} will be overridden by fact#{plural} " \
91
- "of the same name in the apply block")
92
- end
93
-
94
- tv_collisions, tv_filtered = target['variables'].partition do |k, _|
95
- target['facts'].keys.include?(k)
96
- end.map(&:to_h)
97
- unless tv_collisions.empty?
98
- print_tv = tv_collisions.keys.map { |k| "$#{k}" }.join(', ')
99
- plural = tv_collisions.keys.length == 1 ? '' : 's'
100
- Puppet.warning("Target variable#{plural} #{print_tv} " \
101
- "will be overridden by fact#{plural} of the same name in the apply block")
102
- end
103
-
104
- pal.send(:add_variables, compiler.send(:topscope), tv_filtered.merge(pv_filtered))
105
-
94
+ options = request['puppet_config'] || {}
106
95
  # Configure language strictness in the CatalogCompiler. We want Bolt to be able
107
96
  # to compile most Puppet 4+ manifests, so we default to allowing deprecated functions.
108
97
  Puppet[:strict] = options['strict'] || :warning
109
98
  Puppet[:strict_variables] = options['strict_variables'] || false
110
- ast = Puppet::Pops::Serialization::FromDataConverter.convert(pal_main)
111
- # This will be a Program when running via `bolt apply`, but will
112
- # only be a subset of the AST when compiling an apply block in a
113
- # plan. In that case, we need to discover the definitions (which
114
- # would ordinarily be stored on the Program) and construct a Program object.
115
- unless ast.is_a?(Puppet::Pops::Model::Program)
116
- # Node definitions must be at the top level of the apply block.
117
- # That means the apply body either a) consists of just a
118
- # NodeDefinition, b) consists of a BlockExpression which may
119
- # contain NodeDefinitions, or c) doesn't contain NodeDefinitions.
120
- definitions = if ast.is_a?(Puppet::Pops::Model::BlockExpression)
121
- ast.statements.select { |st| st.is_a?(Puppet::Pops::Model::NodeDefinition) }
122
- elsif ast.is_a?(Puppet::Pops::Model::NodeDefinition)
123
- [ast]
124
- else
125
- []
126
- end
127
- ast = Puppet::Pops::Model::Factory.PROGRAM(ast, definitions, ast.locator).model
128
- end
99
+
100
+ pal_main = request['code_ast'] || request['code_string']
101
+ ast = build_program(pal_main)
129
102
  compiler.evaluate(ast)
130
- compiler.instance_variable_get(:@internal_compiler).send(:evaluate_ast_node)
103
+ compiler.evaluate_ast_node
131
104
  compiler.compile_additions
132
105
  compiler.with_json_encoding(&:encode)
133
106
  end
@@ -135,5 +108,45 @@ module Bolt
135
108
  end
136
109
  end
137
110
  end
111
+
112
+ # Warn and remove variables that will be shadowed by facts of the same
113
+ # name, which are set in scope earlier.
114
+ def shadow_vars(type, vars, facts)
115
+ collisions, valid = vars.partition do |k, _|
116
+ facts.include?(k)
117
+ end
118
+ if collisions.any?
119
+ names = collisions.map { |k, _| "$#{k}" }.join(', ')
120
+ plural = collisions.length == 1 ? '' : 's'
121
+ Puppet.warning("#{type.capitalize} variable#{plural} #{names} will be overridden by fact#{plural} " \
122
+ "of the same name in the apply block")
123
+ end
124
+ valid.to_h
125
+ end
126
+
127
+ def build_program(code)
128
+ ast = Puppet::Pops::Serialization::FromDataConverter.convert(code)
129
+
130
+ # This will be a Program when running via `bolt apply`, but will
131
+ # only be a subset of the AST when compiling an apply block in a
132
+ # plan. In that case, we need to discover the definitions (which
133
+ # would ordinarily be stored on the Program) and construct a Program object.
134
+ if ast.is_a?(Puppet::Pops::Model::Program)
135
+ ast
136
+ else
137
+ # Node definitions must be at the top level of the apply block.
138
+ # That means the apply body either a) consists of just a
139
+ # NodeDefinition, b) consists of a BlockExpression which may
140
+ # contain NodeDefinitions, or c) doesn't contain NodeDefinitions.
141
+ definitions = if ast.is_a?(Puppet::Pops::Model::BlockExpression)
142
+ ast.statements.select { |st| st.is_a?(Puppet::Pops::Model::NodeDefinition) }
143
+ elsif ast.is_a?(Puppet::Pops::Model::NodeDefinition)
144
+ [ast]
145
+ else
146
+ []
147
+ end
148
+ Puppet::Pops::Model::Factory.PROGRAM(ast, definitions, ast.locator).model
149
+ end
150
+ end
138
151
  end
139
152
  end