bolt 2.16.0 → 2.21.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 +3 -1
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +20 -9
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +2 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_plan.rb +6 -0
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +6 -4
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +35 -0
- data/lib/bolt/applicator.rb +19 -14
- data/lib/bolt/apply_result.rb +1 -1
- data/lib/bolt/bolt_option_parser.rb +60 -16
- data/lib/bolt/catalog.rb +3 -2
- data/lib/bolt/cli.rb +121 -43
- data/lib/bolt/config.rb +37 -34
- data/lib/bolt/config/options.rb +340 -173
- data/lib/bolt/config/transport/options.rb +315 -160
- data/lib/bolt/config/transport/ssh.rb +24 -10
- data/lib/bolt/executor.rb +21 -0
- data/lib/bolt/inventory/group.rb +3 -2
- data/lib/bolt/inventory/inventory.rb +4 -3
- data/lib/bolt/logger.rb +24 -1
- data/lib/bolt/outputter.rb +1 -1
- data/lib/bolt/outputter/rainbow.rb +14 -3
- data/lib/bolt/pal.rb +28 -10
- data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -2
- data/lib/bolt/pal/yaml_plan/step.rb +24 -2
- data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
- data/lib/bolt/pal/yaml_plan/step/message.rb +30 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
- data/lib/bolt/plugin/module.rb +2 -4
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +20 -6
- data/lib/bolt/puppetdb/client.rb +2 -0
- data/lib/bolt/puppetdb/config.rb +16 -0
- data/lib/bolt/result.rb +7 -0
- data/lib/bolt/shell/bash.rb +45 -37
- data/lib/bolt/shell/powershell.rb +21 -11
- data/lib/bolt/shell/powershell/snippets.rb +15 -6
- data/lib/bolt/transport/base.rb +24 -0
- data/lib/bolt/transport/docker.rb +16 -4
- data/lib/bolt/transport/docker/connection.rb +20 -2
- data/lib/bolt/transport/local/connection.rb +14 -1
- data/lib/bolt/transport/orch.rb +20 -0
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh.rb +7 -1
- data/lib/bolt/transport/ssh/connection.rb +9 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +23 -2
- data/lib/bolt/transport/winrm/connection.rb +118 -8
- data/lib/bolt/util.rb +26 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/transport_app.rb +3 -2
- data/lib/bolt_spec/bolt_context.rb +7 -2
- data/lib/bolt_spec/plans.rb +15 -2
- data/lib/bolt_spec/plans/action_stubs.rb +2 -1
- data/lib/bolt_spec/plans/action_stubs/download_stub.rb +66 -0
- data/lib/bolt_spec/plans/mock_executor.rb +14 -1
- data/lib/bolt_spec/run.rb +22 -0
- data/libexec/bolt_catalog +3 -2
- data/modules/secure_env_vars/plans/init.pp +20 -0
- metadata +21 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef2991c1d3979e03b9070dc168be0e8c44c309914fd7eb02badc60e5b5d907bf
|
4
|
+
data.tar.gz: 9508d434488486b8b16d062f910f6d3bcf8d7b3e89121ccb8033dd393a7a21db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 351084ca010d6f3d051a588e0d216f07d32473026703378dd7003c9f71f411fa2499926d4e1c6272df55cffc8eeb360fbc6fff4c5a0930c14cba006dfbf3874c
|
7
|
+
data.tar.gz: eef7d1b6b0dfe5584bd216bb5ebe421b61b9e25a4131509cf310f33fc75f7a3afa072e919215eefadb43ac7415cdd51546e6b634daf53d8a7a568366bb31ee68
|
data/Puppetfile
CHANGED
@@ -5,7 +5,7 @@ forge "http://forge.puppetlabs.com"
|
|
5
5
|
moduledir File.join(File.dirname(__FILE__), 'modules')
|
6
6
|
|
7
7
|
# Core modules used by 'apply'
|
8
|
-
mod 'puppetlabs-service', '1.
|
8
|
+
mod 'puppetlabs-service', '1.3.0'
|
9
9
|
mod 'puppetlabs-puppet_agent', '3.2.0'
|
10
10
|
mod 'puppetlabs-facts', '1.0.0'
|
11
11
|
|
@@ -28,6 +28,7 @@ mod 'puppetlabs-python_task_helper', '0.4.3'
|
|
28
28
|
mod 'puppetlabs-reboot', '3.0.0'
|
29
29
|
mod 'puppetlabs-ruby_task_helper', '0.5.1'
|
30
30
|
mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
|
31
|
+
mod 'puppetlabs-stdlib', '6.3.0'
|
31
32
|
|
32
33
|
# Plugin modules
|
33
34
|
mod 'puppetlabs-aws_inventory', '0.5.0'
|
@@ -42,3 +43,4 @@ mod 'puppetlabs-yaml', '0.2.0'
|
|
42
43
|
mod 'canary', local: true
|
43
44
|
mod 'aggregate', local: true
|
44
45
|
mod 'puppetdb_fact', local: true
|
46
|
+
mod 'secure_env_vars', local: true
|
@@ -13,10 +13,13 @@ require 'bolt/task'
|
|
13
13
|
# > **Note:** Not available in apply block
|
14
14
|
Puppet::Functions.create_function(:apply_prep) do
|
15
15
|
# @param targets A pattern or array of patterns identifying a set of targets.
|
16
|
+
# @param options Options hash.
|
17
|
+
# @option options [Array] _required_modules An array of modules to sync to the target.
|
16
18
|
# @example Prepare targets by name.
|
17
19
|
# apply_prep('target1,target2')
|
18
20
|
dispatch :apply_prep do
|
19
21
|
param 'Boltlib::TargetSpec', :targets
|
22
|
+
optional_param 'Hash[String, Data]', :options
|
20
23
|
end
|
21
24
|
|
22
25
|
def script_compiler
|
@@ -60,18 +63,34 @@ Puppet::Functions.create_function(:apply_prep) do
|
|
60
63
|
@executor ||= Puppet.lookup(:bolt_executor)
|
61
64
|
end
|
62
65
|
|
63
|
-
def apply_prep(target_spec)
|
66
|
+
def apply_prep(target_spec, options = {})
|
64
67
|
unless Puppet[:tasks]
|
65
68
|
raise Puppet::ParseErrorWithIssue
|
66
69
|
.from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'apply_prep')
|
67
70
|
end
|
68
71
|
|
72
|
+
options = options.transform_keys { |k| k.sub(/^_/, '').to_sym }
|
73
|
+
|
69
74
|
applicator = Puppet.lookup(:apply_executor)
|
70
75
|
|
71
76
|
executor.report_function_call(self.class.name)
|
72
77
|
|
73
78
|
targets = inventory.get_targets(target_spec)
|
74
79
|
|
80
|
+
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
81
|
+
if required_modules&.any?
|
82
|
+
Puppet.debug("Syncing only required modules: #{required_modules.join(',')}.")
|
83
|
+
end
|
84
|
+
|
85
|
+
# Gather facts, including custom facts
|
86
|
+
plugins = applicator.build_plugin_tarball do |mod|
|
87
|
+
next unless required_modules.nil? || required_modules.include?(mod.name)
|
88
|
+
search_dirs = []
|
89
|
+
search_dirs << mod.plugins if mod.plugins?
|
90
|
+
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
91
|
+
search_dirs
|
92
|
+
end
|
93
|
+
|
75
94
|
executor.log_action('install puppet and gather facts', targets) do
|
76
95
|
executor.without_default_logging do
|
77
96
|
# Skip targets that include the puppet-agent feature, as we know an agent will be available.
|
@@ -109,14 +128,6 @@ Puppet::Functions.create_function(:apply_prep) do
|
|
109
128
|
need_install_targets.each { |target| set_agent_feature(target) }
|
110
129
|
end
|
111
130
|
|
112
|
-
# Gather facts, including custom facts
|
113
|
-
plugins = applicator.build_plugin_tarball do |mod|
|
114
|
-
search_dirs = []
|
115
|
-
search_dirs << mod.plugins if mod.plugins?
|
116
|
-
search_dirs << mod.pluginfacts if mod.pluginfacts?
|
117
|
-
search_dirs
|
118
|
-
end
|
119
|
-
|
120
131
|
task = applicator.custom_facts_task
|
121
132
|
arguments = { 'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins) }
|
122
133
|
results = executor.run_task(targets, task, arguments)
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
require 'bolt/error'
|
5
|
+
|
6
|
+
# Downloads the given file or directory from the given set of targets and saves it to a directory
|
7
|
+
# matching the target's name under the given destination directory. Returns the result from each
|
8
|
+
# download. This does nothing if the list of targets is empty.
|
9
|
+
#
|
10
|
+
# > **Note:** Existing content in the destination directory is deleted before downloading from
|
11
|
+
# > the targets.
|
12
|
+
#
|
13
|
+
# > **Note:** Not available in apply block
|
14
|
+
Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFunction) do
|
15
|
+
# Download a file or directory.
|
16
|
+
# @param source The absolute path to the file or directory on the target(s).
|
17
|
+
# @param destination The relative path to the destination directory on the local system. Expands
|
18
|
+
# relative to `<project>/downloads/`.
|
19
|
+
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
20
|
+
# @param options A hash of additional options.
|
21
|
+
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
22
|
+
# @option options [String] _run_as User to run as using privilege escalation.
|
23
|
+
# @return A list of results, one entry per target, with the path to the downloaded file under the
|
24
|
+
# `path` key.
|
25
|
+
# @example Download a file from multiple Linux targets to a destination directory
|
26
|
+
# download_file('/etc/ssh/ssh_config', '~/Downloads', $targets)
|
27
|
+
# @example Download a directory from multiple Linux targets to a project downloads directory
|
28
|
+
# download_file('/etc/ssh', 'ssh', $targets)
|
29
|
+
# @example Download a file from multiple Linux targets and compare its contents to a local file
|
30
|
+
# $results = download_file($source, $destination, $targets)
|
31
|
+
#
|
32
|
+
# $local_content = file::read($source)
|
33
|
+
#
|
34
|
+
# $mismatched_files = $results.filter |$result| {
|
35
|
+
# $remote_content = file::read($result['path'])
|
36
|
+
# $remote_content == $local_content
|
37
|
+
# }
|
38
|
+
dispatch :download_file do
|
39
|
+
param 'String[1]', :source
|
40
|
+
param 'String[1]', :destination
|
41
|
+
param 'Boltlib::TargetSpec', :targets
|
42
|
+
optional_param 'Hash[String[1], Any]', :options
|
43
|
+
return_type 'ResultSet'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Download a file or directory, logging the provided description.
|
47
|
+
# @param source The absolute path to the file or directory on the target(s).
|
48
|
+
# @param destination The relative path to the destination directory on the local system. Expands
|
49
|
+
# relative to `<project>/downloads/`.
|
50
|
+
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
51
|
+
# @param description A description to be output when calling this function.
|
52
|
+
# @param options A hash of additional options.
|
53
|
+
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
54
|
+
# @option options [String] _run_as User to run as using privilege escalation.
|
55
|
+
# @return A list of results, one entry per target, with the path to the downloaded file under the
|
56
|
+
# `path` key.
|
57
|
+
# @example Download a file from multiple Linux targets to a destination directory
|
58
|
+
# download_file('/etc/ssh/ssh_config', '~/Downloads', $targets, 'Downloading remote SSH config')
|
59
|
+
dispatch :download_file_with_description do
|
60
|
+
param 'String[1]', :source
|
61
|
+
param 'String[1]', :destination
|
62
|
+
param 'Boltlib::TargetSpec', :targets
|
63
|
+
param 'String', :description
|
64
|
+
optional_param 'Hash[String[1], Any]', :options
|
65
|
+
return_type 'ResultSet'
|
66
|
+
end
|
67
|
+
|
68
|
+
def download_file(source, destination, targets, options = {})
|
69
|
+
download_file_with_description(source, destination, targets, nil, options)
|
70
|
+
end
|
71
|
+
|
72
|
+
def download_file_with_description(source, destination, targets, description = nil, options = {})
|
73
|
+
unless Puppet[:tasks]
|
74
|
+
raise Puppet::ParseErrorWithIssue
|
75
|
+
.from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING, action: 'download_file')
|
76
|
+
end
|
77
|
+
|
78
|
+
options = options.select { |opt| opt.start_with?('_') }.transform_keys { |k| k.sub(/^_/, '').to_sym }
|
79
|
+
options[:description] = description if description
|
80
|
+
|
81
|
+
executor = Puppet.lookup(:bolt_executor)
|
82
|
+
inventory = Puppet.lookup(:bolt_inventory)
|
83
|
+
|
84
|
+
if (destination = destination.strip).empty?
|
85
|
+
raise Bolt::ValidationError, "Destination cannot be an empty string"
|
86
|
+
end
|
87
|
+
|
88
|
+
if (destination = Pathname.new(destination)).absolute?
|
89
|
+
raise Bolt::ValidationError, "Destination must be a relative path, received absolute path #{destination}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# Prevent path traversal so downloads can't be saved outside of the project downloads directory
|
93
|
+
if (destination.each_filename.to_a & %w[. ..]).any?
|
94
|
+
raise Bolt::ValidationError, "Destination must not include path traversal, received #{destination}"
|
95
|
+
end
|
96
|
+
|
97
|
+
# Paths expand relative to the default downloads directory for the project
|
98
|
+
# e.g. ~/.puppetlabs/bolt/downloads/
|
99
|
+
destination = Puppet.lookup(:bolt_project_data).downloads + destination
|
100
|
+
|
101
|
+
# If the destination directory already exists, delete any existing contents
|
102
|
+
if Dir.exist?(destination)
|
103
|
+
FileUtils.rm_r(Dir.glob(destination + '*'), secure: true)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Send Analytics Report
|
107
|
+
executor.report_function_call(self.class.name)
|
108
|
+
|
109
|
+
# Ensure that that given targets are all Target instances
|
110
|
+
targets = inventory.get_targets(targets)
|
111
|
+
if targets.empty?
|
112
|
+
call_function('debug', "Simulating file download of '#{source}' - no targets given - no action taken")
|
113
|
+
r = Bolt::ResultSet.new([])
|
114
|
+
else
|
115
|
+
r = executor.download_file(targets, source, destination, options)
|
116
|
+
end
|
117
|
+
|
118
|
+
if !r.ok && !options[:catch_errors]
|
119
|
+
raise Bolt::RunFailure.new(r, 'download_file', source)
|
120
|
+
end
|
121
|
+
r
|
122
|
+
end
|
123
|
+
end
|
@@ -13,6 +13,7 @@ Puppet::Functions.create_function(:run_command) do
|
|
13
13
|
# @param options A hash of additional options.
|
14
14
|
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
15
15
|
# @option options [String] _run_as User to run as using privilege escalation.
|
16
|
+
# @option options [Hash] _env_vars Map of environment variables to set
|
16
17
|
# @return A list of results, one entry per target.
|
17
18
|
# @example Run a command on targets
|
18
19
|
# run_command('hostname', $targets, '_catch_errors' => true)
|
@@ -30,6 +31,7 @@ Puppet::Functions.create_function(:run_command) do
|
|
30
31
|
# @param options A hash of additional options.
|
31
32
|
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
32
33
|
# @option options [String] _run_as User to run as using privilege escalation.
|
34
|
+
# @option options [Hash] _env_vars Map of environment variables to set
|
33
35
|
# @return A list of results, one entry per target.
|
34
36
|
# @example Run a command on targets
|
35
37
|
# run_command('hostname', $targets, 'Get hostname')
|
@@ -11,6 +11,9 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
|
|
11
11
|
# @param args A hash of arguments to the plan. Can also include additional options.
|
12
12
|
# @option args [Boolean] _catch_errors Whether to catch raised errors.
|
13
13
|
# @option args [String] _run_as User to run as using privilege escalation.
|
14
|
+
# This option sets the [run-as user](privilege_escalation.md) for all
|
15
|
+
# targets whenever Bolt connects to a target. This is set for all functions
|
16
|
+
# in the called plan, including `run_plan()`.
|
14
17
|
# @return [PlanResult] The result of running the plan. Undef if plan does not explicitly return results.
|
15
18
|
# @example Run a plan
|
16
19
|
# run_plan('canary', 'command' => 'false', 'targets' => $targets, '_catch_errors' => true)
|
@@ -31,6 +34,9 @@ Puppet::Functions.create_function(:run_plan, Puppet::Functions::InternalFunction
|
|
31
34
|
# @param args A hash of arguments to the plan. Can also include additional options.
|
32
35
|
# @option args [Boolean] _catch_errors Whether to catch raised errors.
|
33
36
|
# @option args [String] _run_as User to run as using privilege escalation.
|
37
|
+
# This option sets the [run-as user](privilege_escalation.md) for all
|
38
|
+
# targets whenever Bolt connects to a target. This is set for all functions
|
39
|
+
# in the called plan, including `run_plan()`.
|
34
40
|
# @return [PlanResult] The result of running the plan. Undef if plan does not explicitly return results.
|
35
41
|
# @example Run a plan
|
36
42
|
# run_plan('canary', $targets, 'command' => 'false')
|
@@ -11,8 +11,9 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
|
|
11
11
|
# @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
|
12
12
|
# @param options A hash of additional options.
|
13
13
|
# @option options [Array[String]] arguments An array of arguments to be passed to the script.
|
14
|
-
# @option
|
15
|
-
# @option
|
14
|
+
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
15
|
+
# @option options [String] _run_as User to run as using privilege escalation.
|
16
|
+
# @option options [Hash] _env_vars Map of environment variables to set
|
16
17
|
# @return A list of results, one entry per target.
|
17
18
|
# @example Run a local script on Linux targets as 'root'
|
18
19
|
# run_script('/var/tmp/myscript', $targets, '_run_as' => 'root')
|
@@ -33,8 +34,9 @@ Puppet::Functions.create_function(:run_script, Puppet::Functions::InternalFuncti
|
|
33
34
|
# @param description A description to be output when calling this function.
|
34
35
|
# @param options A hash of additional options.
|
35
36
|
# @option options [Array[String]] arguments An array of arguments to be passed to the script.
|
36
|
-
# @option
|
37
|
-
# @option
|
37
|
+
# @option options [Boolean] _catch_errors Whether to catch raised errors.
|
38
|
+
# @option options [String] _run_as User to run as using privilege escalation.
|
39
|
+
# @option options [Hash] _env_vars Map of environment variables to set
|
38
40
|
# @return A list of results, one entry per target.
|
39
41
|
# @example Run a script
|
40
42
|
# run_script('/var/tmp/myscript', $targets, 'Downloading my application')
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
# Returns an array containing all of the filenames except for "." and ".." in the given directory.
|
6
|
+
Puppet::Functions.create_function(:'dir::children', Puppet::Functions::InternalFunction) do
|
7
|
+
# @param dirname Absolute path or Puppet module name.
|
8
|
+
# @return Array of files in the given directory.
|
9
|
+
# @example List filenames from an absolute path.
|
10
|
+
# dir::children('/home/user/subdir/')
|
11
|
+
# @example List filenames from a Puppet file path.
|
12
|
+
# dir::children('puppet_agent')
|
13
|
+
dispatch :children do
|
14
|
+
scope_param
|
15
|
+
required_param 'String', :dirname
|
16
|
+
return_type 'Array'
|
17
|
+
end
|
18
|
+
|
19
|
+
def children(scope, dirname)
|
20
|
+
# Send Analytics Report
|
21
|
+
Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
|
22
|
+
modname, subpath = dirname.split(File::SEPARATOR, 2)
|
23
|
+
mod_path = scope.compiler.environment.module(modname)&.path
|
24
|
+
|
25
|
+
full_mod_path = File.join(mod_path, subpath || '') if mod_path
|
26
|
+
|
27
|
+
# Expand relative to the project directory if path is relative
|
28
|
+
project = Puppet.lookup(:bolt_project_data)
|
29
|
+
pathname = Pathname.new(dirname)
|
30
|
+
full_dir = pathname.absolute? ? dirname : File.expand_path(File.join(project.path, dirname))
|
31
|
+
|
32
|
+
# Sort for testability
|
33
|
+
Dir.children(full_mod_path || full_dir).sort
|
34
|
+
end
|
35
|
+
end
|
data/lib/bolt/applicator.rb
CHANGED
@@ -18,7 +18,6 @@ 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
23
|
@modulepath = modulepath || []
|
@@ -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
|
@@ -188,7 +176,6 @@ module Bolt
|
|
188
176
|
|
189
177
|
def apply_ast(raw_ast, targets, options, plan_vars = {})
|
190
178
|
ast = Puppet::Pops::Serialization::ToDataConverter.convert(raw_ast, rich_data: true, symbol_to_string: true)
|
191
|
-
|
192
179
|
# Serialize as pcore for *Result* objects
|
193
180
|
plan_vars = Puppet::Pops::Serialization::ToDataConverter.convert(plan_vars,
|
194
181
|
rich_data: true,
|
@@ -207,9 +194,26 @@ module Bolt
|
|
207
194
|
# This data isn't available on the target config hash
|
208
195
|
config: @inventory.transport_data_get
|
209
196
|
}
|
210
|
-
|
211
197
|
description = options[:description] || 'apply catalog'
|
212
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
|
+
|
213
217
|
r = @executor.log_action(description, targets) do
|
214
218
|
futures = targets.map do |target|
|
215
219
|
Concurrent::Future.execute(executor: @pool) do
|
@@ -236,6 +240,7 @@ module Bolt
|
|
236
240
|
result
|
237
241
|
end
|
238
242
|
else
|
243
|
+
|
239
244
|
arguments = {
|
240
245
|
'catalog' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(catalog),
|
241
246
|
'plugins' => Puppet::Pops::Types::PSensitiveType::Sensitive.new(plugins),
|
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\)/
|
@@ -11,9 +11,9 @@ module Bolt
|
|
11
11
|
escalation: %w[run-as sudo-password sudo-password-prompt sudo-executable],
|
12
12
|
run_context: %w[concurrency inventoryfile save-rerun cleanup],
|
13
13
|
global_config_setters: %w[modulepath project configfile],
|
14
|
-
transports: %w[transport connect-timeout tty ssh-command copy-command],
|
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
|
|
@@ -25,7 +25,7 @@ module Bolt
|
|
25
25
|
when 'command'
|
26
26
|
case action
|
27
27
|
when 'run'
|
28
|
-
{ flags: ACTION_OPTS,
|
28
|
+
{ flags: ACTION_OPTS + %w[env-var],
|
29
29
|
banner: COMMAND_RUN_HELP }
|
30
30
|
else
|
31
31
|
{ flags: OPTIONS[:global],
|
@@ -36,6 +36,9 @@ module Bolt
|
|
36
36
|
when 'upload'
|
37
37
|
{ flags: ACTION_OPTS + %w[tmpdir],
|
38
38
|
banner: FILE_UPLOAD_HELP }
|
39
|
+
when 'download'
|
40
|
+
{ flags: ACTION_OPTS,
|
41
|
+
banner: FILE_DOWNLOAD_HELP }
|
39
42
|
else
|
40
43
|
{ flags: OPTIONS[:global],
|
41
44
|
banner: FILE_HELP }
|
@@ -103,7 +106,7 @@ module Bolt
|
|
103
106
|
when 'script'
|
104
107
|
case action
|
105
108
|
when 'run'
|
106
|
-
{ flags: ACTION_OPTS + %w[tmpdir],
|
109
|
+
{ flags: ACTION_OPTS + %w[tmpdir env-var],
|
107
110
|
banner: SCRIPT_RUN_HELP }
|
108
111
|
else
|
109
112
|
{ flags: OPTIONS[:global],
|
@@ -218,10 +221,30 @@ module Bolt
|
|
218
221
|
bolt file <action> [options]
|
219
222
|
|
220
223
|
DESCRIPTION
|
221
|
-
|
224
|
+
Copy files and directories between the controller and targets
|
222
225
|
|
223
226
|
ACTIONS
|
224
|
-
|
227
|
+
download Download a file or directory to the controller
|
228
|
+
upload Upload a local file or directory from the controller
|
229
|
+
HELP
|
230
|
+
|
231
|
+
FILE_DOWNLOAD_HELP = <<~HELP
|
232
|
+
NAME
|
233
|
+
download
|
234
|
+
|
235
|
+
USAGE
|
236
|
+
bolt file download <src> <dest> [options]
|
237
|
+
|
238
|
+
DESCRIPTION
|
239
|
+
Download a file or directory from one or more targets.
|
240
|
+
|
241
|
+
Downloaded files and directories are saved to the a subdirectory
|
242
|
+
matching the target's name under the destination directory. The
|
243
|
+
destination directory is expanded relative to the downloads
|
244
|
+
subdirectory of the project directory.
|
245
|
+
|
246
|
+
EXAMPLES
|
247
|
+
bolt file download /etc/ssh_config ssh_config -t all
|
225
248
|
HELP
|
226
249
|
|
227
250
|
FILE_UPLOAD_HELP = <<~HELP
|
@@ -594,13 +617,13 @@ module Bolt
|
|
594
617
|
bolt task show canary
|
595
618
|
HELP
|
596
619
|
|
597
|
-
attr_reader :
|
620
|
+
attr_reader :deprecations
|
598
621
|
|
599
622
|
def initialize(options)
|
600
623
|
super()
|
601
624
|
|
602
625
|
@options = options
|
603
|
-
@
|
626
|
+
@deprecations = []
|
604
627
|
|
605
628
|
separator "\nINVENTORY OPTIONS"
|
606
629
|
define('-t', '--targets TARGETS',
|
@@ -709,27 +732,27 @@ module Bolt
|
|
709
732
|
File.expand_path(moduledir)
|
710
733
|
end
|
711
734
|
end
|
712
|
-
define('--project
|
735
|
+
define('--project PATH', '--boltdir PATH',
|
713
736
|
'Specify what project to load config from (default: autodiscovered from current working dir)') do |path|
|
714
737
|
@options[:boltdir] = path
|
715
738
|
end
|
716
|
-
define('--configfile
|
739
|
+
define('--configfile PATH',
|
717
740
|
'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml).',
|
718
741
|
'Directory containing bolt.yaml will be used as the project directory.') do |path|
|
719
742
|
@options[:configfile] = path
|
720
743
|
end
|
721
|
-
define('--hiera-config
|
744
|
+
define('--hiera-config PATH',
|
722
745
|
'Specify where to load Hiera config from (default: ~/.puppetlabs/bolt/hiera.yaml)') do |path|
|
723
746
|
@options[:'hiera-config'] = File.expand_path(path)
|
724
747
|
end
|
725
|
-
define('-i', '--inventoryfile
|
748
|
+
define('-i', '--inventoryfile PATH',
|
726
749
|
'Specify where to load inventory from (default: ~/.puppetlabs/bolt/inventory.yaml)') do |path|
|
727
750
|
if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
|
728
751
|
raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
|
729
752
|
end
|
730
753
|
@options[:inventoryfile] = Pathname.new(File.expand_path(path))
|
731
754
|
end
|
732
|
-
define('--puppetfile
|
755
|
+
define('--puppetfile PATH',
|
733
756
|
'Specify a Puppetfile to use when installing modules. (default: ~/.puppetlabs/bolt/Puppetfile)',
|
734
757
|
'Modules are installed in the current project.') do |path|
|
735
758
|
@options[:puppetfile_path] = Pathname.new(File.expand_path(path))
|
@@ -738,16 +761,29 @@ module Bolt
|
|
738
761
|
@options[:'save-rerun'] = save
|
739
762
|
end
|
740
763
|
|
764
|
+
separator "\nREMOTE ENVIRONMENT OPTIONS"
|
765
|
+
define('--env-var ENVIRONMENT_VARIABLES', 'Environment variables to set on the target') do |envvar|
|
766
|
+
unless envvar.include?('=')
|
767
|
+
raise Bolt::CLIError, "Environment variables must be specified using 'myenvvar=key' format"
|
768
|
+
end
|
769
|
+
@options[:env_vars] ||= {}
|
770
|
+
@options[:env_vars].store(*envvar.split('=', 2))
|
771
|
+
end
|
772
|
+
|
741
773
|
separator "\nTRANSPORT OPTIONS"
|
742
774
|
define('--transport TRANSPORT', TRANSPORTS.keys.map(&:to_s),
|
743
775
|
"Specify a default transport: #{TRANSPORTS.keys.join(', ')}") do |t|
|
744
776
|
@options[:transport] = t
|
745
777
|
end
|
746
|
-
define('--ssh
|
778
|
+
define('--[no-]native-ssh', 'Whether to shell out to native SSH or use the net-ssh Ruby library.',
|
779
|
+
'This option is experimental') do |bool|
|
780
|
+
@options[:'native-ssh'] = bool
|
781
|
+
end
|
782
|
+
define('--ssh-command EXEC', "Executable to use instead of the net-ssh Ruby library. ",
|
747
783
|
"This option is experimental.") do |exec|
|
748
784
|
@options[:'ssh-command'] = exec
|
749
785
|
end
|
750
|
-
define('--copy-command EXEC', "Command to copy files to remote hosts if using
|
786
|
+
define('--copy-command EXEC', "Command to copy files to remote hosts if using native SSH. ",
|
751
787
|
"This option is experimental.") do |exec|
|
752
788
|
@options[:'copy-command'] = exec
|
753
789
|
end
|
@@ -803,8 +839,16 @@ module Bolt
|
|
803
839
|
end
|
804
840
|
define('--debug', 'Display debug logging') do |_|
|
805
841
|
@options[:debug] = true
|
842
|
+
# We don't actually set '--log-level debug' here, but once the options are evaluated by
|
843
|
+
# the config class the end result is the same.
|
844
|
+
msg = "Command line option '--debug' is deprecated, set '--log-level debug' instead."
|
845
|
+
@deprecations << { type: 'Using --debug instead of --log-level debug', msg: msg }
|
846
|
+
end
|
847
|
+
define('--log-level LEVEL',
|
848
|
+
"Set the log level for the console. Available options are",
|
849
|
+
"debug, info, notice, warn, error, fatal, any.") do |level|
|
850
|
+
@options[:log] = { 'console' => { 'level' => level } }
|
806
851
|
end
|
807
|
-
|
808
852
|
define('--plugin PLUGIN', 'Select the plugin to use') do |plug|
|
809
853
|
@options[:plugin] = plug
|
810
854
|
end
|