bolt 2.19.0 → 2.20.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/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +123 -0
- data/lib/bolt/bolt_option_parser.rb +25 -2
- data/lib/bolt/catalog.rb +3 -2
- data/lib/bolt/cli.rb +139 -92
- data/lib/bolt/config.rb +1 -1
- data/lib/bolt/config/options.rb +14 -0
- data/lib/bolt/executor.rb +15 -0
- data/lib/bolt/inventory/group.rb +3 -2
- data/lib/bolt/inventory/inventory.rb +4 -3
- data/lib/bolt/outputter/rainbow.rb +3 -2
- data/lib/bolt/pal.rb +8 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +18 -1
- data/lib/bolt/pal/yaml_plan/step.rb +11 -2
- data/lib/bolt/pal/yaml_plan/step/download.rb +38 -0
- data/lib/bolt/pal/yaml_plan/step/upload.rb +3 -3
- data/lib/bolt/plugin/puppetdb.rb +3 -2
- data/lib/bolt/project.rb +2 -1
- 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 +24 -4
- data/lib/bolt/shell/powershell.rb +10 -4
- data/lib/bolt/transport/base.rb +24 -0
- data/lib/bolt/transport/docker.rb +8 -0
- data/lib/bolt/transport/docker/connection.rb +20 -2
- data/lib/bolt/transport/local/connection.rb +14 -1
- data/lib/bolt/transport/orch.rb +12 -0
- data/lib/bolt/transport/simple.rb +6 -0
- data/lib/bolt/transport/ssh/connection.rb +9 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +22 -1
- data/lib/bolt/transport/winrm/connection.rb +109 -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
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a72c179da8e6e3fd3d8f883366e88f98026de00eee08ae2410515b2103a3810d
|
4
|
+
data.tar.gz: 968edc1c0c30a370ed06b1c9bd5498ccaaaa5e15f3c47307cad1e68f2af5dafb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a906282180c5df824978979d8e12da5cbe2f835ccef2ae65f2e2fc892bcfa1514a4f8f76418b866fe9cbfe8ce8f8f28b66306055b7acc67de6a7a2e13bb7edbf
|
7
|
+
data.tar.gz: ee4c33740e8e29ad4b3026e5b75b3f0d29556c92e9277bc34101c5f8acfe80db599389c8544aab5946c6c444502a49ddb515da0a854b5796b0aae54fc0875ffc
|
@@ -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
|
@@ -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 }
|
@@ -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
|
data/lib/bolt/catalog.rb
CHANGED
@@ -138,9 +138,10 @@ module Bolt
|
|
138
138
|
# That means the apply body either a) consists of just a
|
139
139
|
# NodeDefinition, b) consists of a BlockExpression which may
|
140
140
|
# contain NodeDefinitions, or c) doesn't contain NodeDefinitions.
|
141
|
-
definitions =
|
141
|
+
definitions = case ast
|
142
|
+
when Puppet::Pops::Model::BlockExpression
|
142
143
|
ast.statements.select { |st| st.is_a?(Puppet::Pops::Model::NodeDefinition) }
|
143
|
-
|
144
|
+
when Puppet::Pops::Model::NodeDefinition
|
144
145
|
[ast]
|
145
146
|
else
|
146
147
|
[]
|
data/lib/bolt/cli.rb
CHANGED
@@ -32,7 +32,7 @@ module Bolt
|
|
32
32
|
'script' => %w[run],
|
33
33
|
'task' => %w[show run],
|
34
34
|
'plan' => %w[show run convert],
|
35
|
-
'file' => %w[upload],
|
35
|
+
'file' => %w[download upload],
|
36
36
|
'puppetfile' => %w[install show-modules generate-types],
|
37
37
|
'secret' => %w[encrypt decrypt createkeys],
|
38
38
|
'inventory' => %w[show],
|
@@ -75,72 +75,100 @@ module Bolt
|
|
75
75
|
end
|
76
76
|
private :help?
|
77
77
|
|
78
|
+
# Wrapper method that is called by the Bolt executable. Parses the command and
|
79
|
+
# then loads the project and config. Once config is loaded, it completes the
|
80
|
+
# setup process by configuring Bolt and issuing warnings.
|
81
|
+
#
|
82
|
+
# This separation is needed since the Bolt::Outputter class that normally handles
|
83
|
+
# printing errors relies on config being loaded. All setup that happens before
|
84
|
+
# config is loaded will have errors printed directly to stdout, while all errors
|
85
|
+
# raised after config is loaded are handled by the outputter.
|
78
86
|
def parse
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
87
|
+
parse_command
|
88
|
+
load_config
|
89
|
+
finalize_setup
|
90
|
+
end
|
91
|
+
|
92
|
+
# Parses the command and validates options. All errors that are raised here
|
93
|
+
# are not handled by the outputter, as it relies on config being loaded.
|
94
|
+
def parse_command
|
95
|
+
parser = BoltOptionParser.new(options)
|
96
|
+
# This part aims to handle both `bolt <mode> --help` and `bolt help <mode>`.
|
97
|
+
remaining = handle_parser_errors { parser.permute(@argv) } unless @argv.empty?
|
98
|
+
if @argv.empty? || help?(remaining)
|
99
|
+
# Update the parser for the subcommand (or lack thereof)
|
100
|
+
parser.update
|
101
|
+
puts parser.help
|
102
|
+
raise Bolt::CLIExit
|
103
|
+
end
|
104
|
+
|
105
|
+
options[:object] = remaining.shift
|
106
|
+
|
107
|
+
# Only parse task_options for task or plan
|
108
|
+
if %w[task plan].include?(options[:subcommand])
|
109
|
+
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
110
|
+
if options[:task_options]
|
111
|
+
unless task_options.empty?
|
112
|
+
raise Bolt::CLIError,
|
113
|
+
"Parameters must be specified through either the --params " \
|
114
|
+
"option or param=value pairs, not both"
|
115
|
+
end
|
116
|
+
options[:params_parsed] = true
|
117
|
+
elsif task_options.any?
|
118
|
+
options[:params_parsed] = false
|
119
|
+
options[:task_options] = Hash[task_options.map { |a| a.split('=', 2) }]
|
120
|
+
else
|
121
|
+
options[:params_parsed] = true
|
122
|
+
options[:task_options] = {}
|
88
123
|
end
|
124
|
+
end
|
125
|
+
options[:leftovers] = remaining
|
89
126
|
|
90
|
-
|
127
|
+
# Default to verbose for everything except plans
|
128
|
+
unless options.key?(:verbose)
|
129
|
+
options[:verbose] = options[:subcommand] != 'plan'
|
130
|
+
end
|
91
131
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
|
116
|
-
Bolt::Config.from_project(project, options)
|
117
|
-
elsif options[:configfile]
|
118
|
-
Bolt::Config.from_file(options[:configfile], options)
|
119
|
-
else
|
120
|
-
project = if options[:boltdir]
|
121
|
-
dir = Pathname.new(options[:boltdir])
|
122
|
-
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
123
|
-
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
124
|
-
else
|
125
|
-
Bolt::Project.create_project(dir)
|
126
|
-
end
|
132
|
+
validate(options)
|
133
|
+
|
134
|
+
# Deprecation warnings can't be issued until after config is loaded, so
|
135
|
+
# store them for later.
|
136
|
+
@parser_deprecations = parser.deprecations
|
137
|
+
rescue Bolt::Error => e
|
138
|
+
fatal_error(e)
|
139
|
+
raise e
|
140
|
+
end
|
141
|
+
|
142
|
+
# Loads the project and configuration. All errors that are raised here are not
|
143
|
+
# handled by the outputter, as it relies on config being loaded.
|
144
|
+
def load_config
|
145
|
+
@config = if ENV['BOLT_PROJECT']
|
146
|
+
project = Bolt::Project.create_project(ENV['BOLT_PROJECT'], 'environment')
|
147
|
+
Bolt::Config.from_project(project, options)
|
148
|
+
elsif options[:configfile]
|
149
|
+
Bolt::Config.from_file(options[:configfile], options)
|
150
|
+
else
|
151
|
+
project = if options[:boltdir]
|
152
|
+
dir = Pathname.new(options[:boltdir])
|
153
|
+
if (dir + Bolt::Project::BOLTDIR_NAME).directory?
|
154
|
+
Bolt::Project.create_project(dir + Bolt::Project::BOLTDIR_NAME)
|
127
155
|
else
|
128
|
-
Bolt::Project.
|
156
|
+
Bolt::Project.create_project(dir)
|
129
157
|
end
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
158
|
+
else
|
159
|
+
Bolt::Project.find_boltdir(Dir.pwd)
|
160
|
+
end
|
161
|
+
Bolt::Config.from_project(project, options)
|
162
|
+
end
|
163
|
+
rescue Bolt::Error => e
|
164
|
+
fatal_error(e)
|
165
|
+
raise e
|
166
|
+
end
|
167
|
+
|
168
|
+
# Completes the setup process by configuring Bolt and issuing warnings
|
169
|
+
def finalize_setup
|
170
|
+
Bolt::Logger.configure(config.log, config.color)
|
171
|
+
Bolt::Logger.analytics = analytics
|
144
172
|
|
145
173
|
# Logger must be configured before checking path case and project file, otherwise warnings will not display
|
146
174
|
config.check_path_case('modulepath', config.modulepath)
|
@@ -151,28 +179,11 @@ module Bolt
|
|
151
179
|
|
152
180
|
# Display warnings created during parser and config initialization
|
153
181
|
config.warnings.each { |warning| @logger.warn(warning[:msg]) }
|
154
|
-
|
182
|
+
@parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
155
183
|
config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
156
184
|
|
157
|
-
# After validation, initialize inventory and targets. Errors here are better to catch early.
|
158
|
-
# After this step
|
159
|
-
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
160
|
-
# options[:targets] will contain a resolved set of Target objects
|
161
|
-
unless options[:subcommand] == 'puppetfile' ||
|
162
|
-
options[:subcommand] == 'secret' ||
|
163
|
-
options[:subcommand] == 'project' ||
|
164
|
-
options[:action] == 'show' ||
|
165
|
-
options[:action] == 'convert'
|
166
|
-
|
167
|
-
update_targets(options)
|
168
|
-
end
|
169
|
-
|
170
|
-
unless options.key?(:verbose)
|
171
|
-
# Default to verbose for everything except plans
|
172
|
-
options[:verbose] = options[:subcommand] != 'plan'
|
173
|
-
end
|
174
|
-
|
175
185
|
warn_inventory_overrides_cli(options)
|
186
|
+
|
176
187
|
options
|
177
188
|
rescue Bolt::Error => e
|
178
189
|
outputter.fatal_error(e)
|
@@ -329,6 +340,17 @@ module Bolt
|
|
329
340
|
exit!
|
330
341
|
end
|
331
342
|
|
343
|
+
# Initialize inventory and targets. Errors here are better to catch early.
|
344
|
+
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
345
|
+
# options[:targets] will contain a resolved set of Target objects
|
346
|
+
unless options[:subcommand] == 'puppetfile' ||
|
347
|
+
options[:subcommand] == 'secret' ||
|
348
|
+
options[:subcommand] == 'project' ||
|
349
|
+
options[:action] == 'show' ||
|
350
|
+
options[:action] == 'convert'
|
351
|
+
update_targets(options)
|
352
|
+
end
|
353
|
+
|
332
354
|
if options[:action] == 'convert'
|
333
355
|
convert_plan(options[:object])
|
334
356
|
return 0
|
@@ -357,30 +379,32 @@ module Bolt
|
|
357
379
|
|
358
380
|
analytics.screen_view(screen, screen_view_fields)
|
359
381
|
|
360
|
-
|
361
|
-
|
382
|
+
case options[:action]
|
383
|
+
when 'show'
|
384
|
+
case options[:subcommand]
|
385
|
+
when 'task'
|
362
386
|
if options[:object]
|
363
387
|
show_task(options[:object])
|
364
388
|
else
|
365
389
|
list_tasks
|
366
390
|
end
|
367
|
-
|
391
|
+
when 'plan'
|
368
392
|
if options[:object]
|
369
393
|
show_plan(options[:object])
|
370
394
|
else
|
371
395
|
list_plans
|
372
396
|
end
|
373
|
-
|
397
|
+
when 'inventory'
|
374
398
|
if options[:detail]
|
375
399
|
show_targets
|
376
400
|
else
|
377
401
|
list_targets
|
378
402
|
end
|
379
|
-
|
403
|
+
when 'group'
|
380
404
|
list_groups
|
381
405
|
end
|
382
406
|
return 0
|
383
|
-
|
407
|
+
when 'show-modules'
|
384
408
|
list_modules
|
385
409
|
return 0
|
386
410
|
end
|
@@ -393,17 +417,19 @@ module Bolt
|
|
393
417
|
|
394
418
|
case options[:subcommand]
|
395
419
|
when 'project'
|
396
|
-
|
420
|
+
case options[:action]
|
421
|
+
when 'init'
|
397
422
|
code = initialize_project
|
398
|
-
|
423
|
+
when 'migrate'
|
399
424
|
code = migrate_project
|
400
425
|
end
|
401
426
|
when 'plan'
|
402
427
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
403
428
|
when 'puppetfile'
|
404
|
-
|
429
|
+
case options[:action]
|
430
|
+
when 'generate-types'
|
405
431
|
code = generate_types
|
406
|
-
|
432
|
+
when 'install'
|
407
433
|
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath)
|
408
434
|
end
|
409
435
|
when 'secret'
|
@@ -445,11 +471,22 @@ module Bolt
|
|
445
471
|
src = options[:object]
|
446
472
|
dest = options[:leftovers].first
|
447
473
|
|
474
|
+
if src.nil?
|
475
|
+
raise Bolt::CLIError, "A source path must be specified"
|
476
|
+
end
|
477
|
+
|
448
478
|
if dest.nil?
|
449
479
|
raise Bolt::CLIError, "A destination path must be specified"
|
450
480
|
end
|
451
|
-
|
452
|
-
|
481
|
+
|
482
|
+
case options[:action]
|
483
|
+
when 'download'
|
484
|
+
dest = File.expand_path(dest, Dir.pwd)
|
485
|
+
executor.download_file(targets, src, dest, executor_opts)
|
486
|
+
when 'upload'
|
487
|
+
validate_file('source file', src, true)
|
488
|
+
executor.upload_file(targets, src, dest, executor_opts)
|
489
|
+
end
|
453
490
|
end
|
454
491
|
end
|
455
492
|
|
@@ -879,5 +916,15 @@ module Bolt
|
|
879
916
|
def incomplete_install?
|
880
917
|
(Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact]).empty?
|
881
918
|
end
|
919
|
+
|
920
|
+
# Mimicks the output from Outputter::Human#fatal_error. This should be used to print
|
921
|
+
# errors prior to config being loaded, as the outputter relies on config being loaded.
|
922
|
+
def fatal_error(error)
|
923
|
+
if $stdout.isatty
|
924
|
+
$stdout.puts("\033[31m#{error.message}\033[0m")
|
925
|
+
else
|
926
|
+
$stdout.puts(error.message)
|
927
|
+
end
|
928
|
+
end
|
882
929
|
end
|
883
930
|
end
|