bolt 2.13.0 → 2.18.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 +4 -4
- data/Puppetfile +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/add_facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/add_to_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/bolt-modules/boltlib/lib/puppet/functions/catch_errors.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_target.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/get_targets.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/remove_from_group.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resolve_references.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/resource.rb +53 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +3 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +7 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +7 -4
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/set_config.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_feature.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_resources.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/set_var.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/vars.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb +1 -0
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +1 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +2 -0
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/sleep.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/exists.rb +2 -1
- data/bolt-modules/file/lib/puppet/functions/file/join.rb +2 -0
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +3 -1
- data/bolt-modules/file/lib/puppet/functions/file/write.rb +2 -0
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +2 -0
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +1 -0
- data/bolt-modules/system/lib/puppet/functions/system/env.rb +2 -0
- data/lib/bolt/applicator.rb +36 -21
- data/lib/bolt/apply_inventory.rb +4 -0
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/apply_target.rb +4 -0
- data/lib/bolt/bolt_option_parser.rb +27 -16
- data/lib/bolt/catalog.rb +79 -69
- data/lib/bolt/cli.rb +78 -58
- data/lib/bolt/config.rb +163 -136
- data/lib/bolt/config/options.rb +474 -0
- data/lib/bolt/config/transport/base.rb +16 -16
- data/lib/bolt/config/transport/docker.rb +9 -23
- data/lib/bolt/config/transport/local.rb +6 -44
- data/lib/bolt/config/transport/options.rb +460 -0
- data/lib/bolt/config/transport/orch.rb +9 -18
- data/lib/bolt/config/transport/remote.rb +3 -6
- data/lib/bolt/config/transport/ssh.rb +78 -119
- data/lib/bolt/config/transport/winrm.rb +18 -47
- data/lib/bolt/executor.rb +14 -1
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/inventory/inventory.rb +4 -14
- data/lib/bolt/inventory/target.rb +22 -5
- data/lib/bolt/logger.rb +15 -1
- data/lib/bolt/outputter.rb +3 -0
- data/lib/bolt/outputter/rainbow.rb +84 -0
- data/lib/bolt/pal.rb +23 -9
- data/lib/bolt/project.rb +75 -55
- data/lib/bolt/resource_instance.rb +5 -1
- data/lib/bolt/shell/bash.rb +30 -42
- data/lib/bolt/shell/powershell.rb +13 -8
- data/lib/bolt/shell/powershell/snippets.rb +23 -6
- data/lib/bolt/transport/docker.rb +9 -5
- data/lib/bolt/transport/local/connection.rb +2 -1
- data/lib/bolt/transport/orch.rb +8 -0
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/connection.rb +35 -0
- data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_spec/bolt_context.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +22 -18
@@ -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/
|
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/
|
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
|
@@ -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)
|
data/lib/bolt/applicator.rb
CHANGED
@@ -18,10 +18,9 @@ module Bolt
|
|
18
18
|
pdb_client, hiera_config, max_compiles, apply_settings)
|
19
19
|
# lazy-load expensive gem code
|
20
20
|
require 'concurrent'
|
21
|
-
|
22
21
|
@inventory = inventory
|
23
22
|
@executor = executor
|
24
|
-
@modulepath = modulepath
|
23
|
+
@modulepath = modulepath || []
|
25
24
|
@plugin_dirs = plugin_dirs
|
26
25
|
@project = project
|
27
26
|
@pdb_client = pdb_client
|
@@ -30,17 +29,6 @@ module Bolt
|
|
30
29
|
|
31
30
|
@pool = Concurrent::ThreadPoolExecutor.new(max_threads: max_compiles)
|
32
31
|
@logger = Logging.logger[self]
|
33
|
-
@plugin_tarball = Concurrent::Delay.new do
|
34
|
-
build_plugin_tarball do |mod|
|
35
|
-
search_dirs = []
|
36
|
-
search_dirs << mod.plugins if mod.plugins?
|
37
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
38
|
-
search_dirs << mod.files if mod.files?
|
39
|
-
type_files = "#{mod.path}/types"
|
40
|
-
search_dirs << type_files if File.exist?(type_files)
|
41
|
-
search_dirs
|
42
|
-
end
|
43
|
-
end
|
44
32
|
end
|
45
33
|
|
46
34
|
private def libexec
|
@@ -106,6 +94,16 @@ module Bolt
|
|
106
94
|
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_input.to_json)
|
107
95
|
ENV['PATH'] = old_path
|
108
96
|
|
97
|
+
# If bolt_catalog does not return valid JSON, we should print stderr to
|
98
|
+
# see what happened
|
99
|
+
print_logs = stat.success?
|
100
|
+
result = begin
|
101
|
+
JSON.parse(out)
|
102
|
+
rescue JSON::ParserError
|
103
|
+
print_logs = true
|
104
|
+
{ 'message' => "Something's gone terribly wrong! STDERR is logged." }
|
105
|
+
end
|
106
|
+
|
109
107
|
# Any messages logged by Puppet will be on stderr as JSON hashes, so we
|
110
108
|
# parse those and store them here. Any message on stderr that is not
|
111
109
|
# properly JSON formatted is assumed to be an error message. If
|
@@ -119,17 +117,15 @@ module Bolt
|
|
119
117
|
{ 'level' => 'err', 'message' => line }
|
120
118
|
end
|
121
119
|
|
122
|
-
|
123
|
-
if stat.success?
|
120
|
+
if print_logs
|
124
121
|
logs.each do |log|
|
125
122
|
bolt_level = Bolt::Util::PuppetLogLevel::MAPPING[log['level'].to_sym]
|
126
123
|
message = log['message'].chomp
|
127
124
|
@logger.send(bolt_level, "#{target.name}: #{message}")
|
128
125
|
end
|
129
|
-
result
|
130
|
-
else
|
131
|
-
raise ApplyError.new(target.name, result['message'])
|
132
126
|
end
|
127
|
+
raise ApplyError.new(target.name, result['message']) unless stat.success?
|
128
|
+
result
|
133
129
|
end
|
134
130
|
|
135
131
|
def validate_hiera_config(hiera_config)
|
@@ -146,6 +142,7 @@ module Bolt
|
|
146
142
|
|
147
143
|
def apply(args, apply_body, scope)
|
148
144
|
raise(ArgumentError, 'apply requires a TargetSpec') if args.empty?
|
145
|
+
raise(ArgumentError, 'apply requires at least one statement in the apply block') if apply_body.nil?
|
149
146
|
type0 = Puppet.lookup(:pal_script_compiler).type('TargetSpec')
|
150
147
|
Puppet::Pal.assert_type(type0, args[0], 'apply targets')
|
151
148
|
|
@@ -179,7 +176,6 @@ module Bolt
|
|
179
176
|
|
180
177
|
def apply_ast(raw_ast, targets, options, plan_vars = {})
|
181
178
|
ast = Puppet::Pops::Serialization::ToDataConverter.convert(raw_ast, rich_data: true, symbol_to_string: true)
|
182
|
-
|
183
179
|
# Serialize as pcore for *Result* objects
|
184
180
|
plan_vars = Puppet::Pops::Serialization::ToDataConverter.convert(plan_vars,
|
185
181
|
rich_data: true,
|
@@ -187,19 +183,37 @@ module Bolt
|
|
187
183
|
type_by_reference: true,
|
188
184
|
local_reference: true)
|
189
185
|
|
186
|
+
bolt_project = @project if @project&.name
|
190
187
|
scope = {
|
191
188
|
code_ast: ast,
|
192
189
|
modulepath: @modulepath,
|
193
|
-
project:
|
190
|
+
project: bolt_project.to_h,
|
194
191
|
pdb_config: @pdb_client.config.to_hash,
|
195
192
|
hiera_config: @hiera_config,
|
196
193
|
plan_vars: plan_vars,
|
197
194
|
# This data isn't available on the target config hash
|
198
195
|
config: @inventory.transport_data_get
|
199
196
|
}
|
200
|
-
|
201
197
|
description = options[:description] || 'apply catalog'
|
202
198
|
|
199
|
+
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
200
|
+
if required_modules&.any?
|
201
|
+
@logger.debug("Syncing only required modules: #{required_modules.join(',')}.")
|
202
|
+
end
|
203
|
+
|
204
|
+
@plugin_tarball = Concurrent::Delay.new do
|
205
|
+
build_plugin_tarball do |mod|
|
206
|
+
next unless required_modules.nil? || required_modules.include?(mod.name)
|
207
|
+
search_dirs = []
|
208
|
+
search_dirs << mod.plugins if mod.plugins?
|
209
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
210
|
+
search_dirs << mod.files if mod.files?
|
211
|
+
type_files = "#{mod.path}/types"
|
212
|
+
search_dirs << type_files if File.exist?(type_files)
|
213
|
+
search_dirs
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
203
217
|
r = @executor.log_action(description, targets) do
|
204
218
|
futures = targets.map do |target|
|
205
219
|
Concurrent::Future.execute(executor: @pool) do
|
@@ -226,6 +240,7 @@ module Bolt
|
|
226
240
|
result
|
227
241
|
end
|
228
242
|
else
|
243
|
+
|
229
244
|
arguments = {
|
230
245
|
'catalog' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(catalog),
|
231
246
|
'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins),
|
data/lib/bolt/apply_inventory.rb
CHANGED
data/lib/bolt/apply_result.rb
CHANGED
@@ -20,7 +20,7 @@ module Bolt
|
|
20
20
|
error_hash['msg'] =~ /The term 'ruby.exe' is not recognized as the name of a cmdlet/)
|
21
21
|
# Windows does not have Ruby present
|
22
22
|
{
|
23
|
-
'msg' => "Puppet
|
23
|
+
'msg' => "Puppet was not found on the target or in $env:ProgramFiles, please install it to enable 'apply'",
|
24
24
|
'kind' => 'bolt/apply-error'
|
25
25
|
}
|
26
26
|
elsif exit_code == 1 && error_hash['msg'] =~ /cannot load such file -- puppet \(LoadError\)/
|
data/lib/bolt/apply_target.rb
CHANGED
@@ -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
|
14
|
-
transports: %w[transport connect-timeout tty ssh-command copy-command],
|
13
|
+
global_config_setters: %w[modulepath project configfile],
|
14
|
+
transports: %w[transport connect-timeout tty native-ssh 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
|
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
|
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
|
713
|
-
'Specify what
|
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
|
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
|
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
|
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
|
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
|
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
|
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|
|
@@ -743,11 +743,15 @@ module Bolt
|
|
743
743
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
744
744
|
@options[:transport] = t
|
745
745
|
end
|
746
|
-
define('--ssh
|
746
|
+
define('--[no-]native-ssh', 'Whether to shell out to native SSH or use the net-ssh Ruby library.',
|
747
|
+
'This option is experimental') do |bool|
|
748
|
+
@options[:'native-ssh'] = bool
|
749
|
+
end
|
750
|
+
define('--ssh-command EXEC', "Executable to use instead of the net-ssh Ruby library. ",
|
747
751
|
"This option is experimental.") do |exec|
|
748
752
|
@options[:'ssh-command'] = exec
|
749
753
|
end
|
750
|
-
define('--copy-command EXEC', "Command to copy files to remote hosts if using
|
754
|
+
define('--copy-command EXEC', "Command to copy files to remote hosts if using native SSH. ",
|
751
755
|
"This option is experimental.") do |exec|
|
752
756
|
@options[:'copy-command'] = exec
|
753
757
|
end
|
@@ -803,8 +807,15 @@ module Bolt
|
|
803
807
|
end
|
804
808
|
define('--debug', 'Display debug logging') do |_|
|
805
809
|
@options[:debug] = true
|
810
|
+
# We don't actually set '--log-level debug' here, but once the options are evaluated by
|
811
|
+
# the config class the end result is the same.
|
812
|
+
@warnings << { msg: "Command line option '--debug' is deprecated, set '--log-level debug' instead." }
|
813
|
+
end
|
814
|
+
define('--log-level LEVEL',
|
815
|
+
"Set the log level for the console. Available options are",
|
816
|
+
"debug, info, notice, warn, error, fatal, any.") do |level|
|
817
|
+
@options[:log] = { 'console' => { 'level' => level } }
|
806
818
|
end
|
807
|
-
|
808
819
|
define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
|
809
820
|
@options[:plugin] = plug
|
810
821
|
end
|
data/lib/bolt/catalog.rb
CHANGED
@@ -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(
|
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
|
-
|
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,83 +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
60
|
project = request['project'] || {}
|
62
61
|
bolt_project = Struct.new(:name, :path).new(project['name'], project['path']) unless project.empty?
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
69
90
|
Puppet::Pal.in_tmp_environment('bolt_catalog', env_conf) do |pal|
|
70
|
-
|
71
|
-
Puppet.override(bolt_pdb_client: pdb_client,
|
72
|
-
bolt_inventory: inv,
|
73
|
-
bolt_project: bolt_project) do
|
91
|
+
Puppet.override(puppet_overrides) do
|
74
92
|
Puppet.lookup(:pal_current_node).trusted_data = target['trusted']
|
75
93
|
pal.with_catalog_compiler do |compiler|
|
76
|
-
|
77
|
-
# loaders are initialized for loading
|
78
|
-
plan_vars = Puppet::Pops::Serialization::FromDataConverter.convert(request['plan_vars'])
|
79
|
-
|
80
|
-
# Facts will be set by the catalog compiler, so we need to ensure
|
81
|
-
# that any plan or target variables with the same name are not
|
82
|
-
# passed into the apply block to avoid a redefinition error.
|
83
|
-
# Filter out plan and target vars separately and raise a Puppet
|
84
|
-
# warning if there are any collisions for either. Puppet warning
|
85
|
-
# is the only way to log a message that will make it back to Bolt
|
86
|
-
# to be printed.
|
87
|
-
pv_collisions, pv_filtered = plan_vars.partition do |k, _|
|
88
|
-
target['facts'].keys.include?(k)
|
89
|
-
end.map(&:to_h)
|
90
|
-
unless pv_collisions.empty?
|
91
|
-
print_pv = pv_collisions.keys.map { |k| "$#{k}" }.join(', ')
|
92
|
-
plural = pv_collisions.keys.length == 1 ? '' : 's'
|
93
|
-
Puppet.warning("Plan variable#{plural} #{print_pv} will be overridden by fact#{plural} " \
|
94
|
-
"of the same name in the apply block")
|
95
|
-
end
|
96
|
-
|
97
|
-
tv_collisions, tv_filtered = target['variables'].partition do |k, _|
|
98
|
-
target['facts'].keys.include?(k)
|
99
|
-
end.map(&:to_h)
|
100
|
-
unless tv_collisions.empty?
|
101
|
-
print_tv = tv_collisions.keys.map { |k| "$#{k}" }.join(', ')
|
102
|
-
plural = tv_collisions.keys.length == 1 ? '' : 's'
|
103
|
-
Puppet.warning("Target variable#{plural} #{print_tv} " \
|
104
|
-
"will be overridden by fact#{plural} of the same name in the apply block")
|
105
|
-
end
|
106
|
-
|
107
|
-
pal.send(:add_variables, compiler.send(:topscope), tv_filtered.merge(pv_filtered))
|
108
|
-
|
94
|
+
options = request['puppet_config'] || {}
|
109
95
|
# Configure language strictness in the CatalogCompiler. We want Bolt to be able
|
110
96
|
# to compile most Puppet 4+ manifests, so we default to allowing deprecated functions.
|
111
97
|
Puppet[:strict] = options['strict'] || :warning
|
112
98
|
Puppet[:strict_variables] = options['strict_variables'] || false
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
# plan. In that case, we need to discover the definitions (which
|
117
|
-
# would ordinarily be stored on the Program) and construct a Program object.
|
118
|
-
unless ast.is_a?(Puppet::Pops::Model::Program)
|
119
|
-
# Node definitions must be at the top level of the apply block.
|
120
|
-
# That means the apply body either a) consists of just a
|
121
|
-
# NodeDefinition, b) consists of a BlockExpression which may
|
122
|
-
# contain NodeDefinitions, or c) doesn't contain NodeDefinitions.
|
123
|
-
definitions = if ast.is_a?(Puppet::Pops::Model::BlockExpression)
|
124
|
-
ast.statements.select { |st| st.is_a?(Puppet::Pops::Model::NodeDefinition) }
|
125
|
-
elsif ast.is_a?(Puppet::Pops::Model::NodeDefinition)
|
126
|
-
[ast]
|
127
|
-
else
|
128
|
-
[]
|
129
|
-
end
|
130
|
-
ast = Puppet::Pops::Model::Factory.PROGRAM(ast, definitions, ast.locator).model
|
131
|
-
end
|
99
|
+
|
100
|
+
pal_main = request['code_ast'] || request['code_string']
|
101
|
+
ast = build_program(pal_main)
|
132
102
|
compiler.evaluate(ast)
|
133
|
-
compiler.
|
103
|
+
compiler.evaluate_ast_node
|
134
104
|
compiler.compile_additions
|
135
105
|
compiler.with_json_encoding(&:encode)
|
136
106
|
end
|
@@ -138,5 +108,45 @@ module Bolt
|
|
138
108
|
end
|
139
109
|
end
|
140
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
|
141
151
|
end
|
142
152
|
end
|