bolt 2.24.1 → 2.29.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/datatypes/result.rb +2 -1
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
- data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +1 -1
- data/lib/bolt/analytics.rb +7 -3
- data/lib/bolt/applicator.rb +21 -21
- data/lib/bolt/bolt_option_parser.rb +77 -26
- data/lib/bolt/catalog.rb +4 -2
- data/lib/bolt/cli.rb +135 -147
- data/lib/bolt/config.rb +48 -25
- data/lib/bolt/config/options.rb +34 -2
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +8 -1
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/inventory/inventory.rb +1 -1
- data/lib/bolt/inventory/target.rb +1 -1
- data/lib/bolt/logger.rb +35 -21
- data/lib/bolt/outputter/logger.rb +1 -1
- data/lib/bolt/pal.rb +21 -10
- data/lib/bolt/pal/yaml_plan/evaluator.rb +1 -1
- data/lib/bolt/plugin/puppetdb.rb +1 -1
- data/lib/bolt/project.rb +62 -17
- data/lib/bolt/puppetdb/client.rb +1 -1
- data/lib/bolt/puppetdb/config.rb +1 -1
- data/lib/bolt/puppetfile.rb +160 -0
- data/lib/bolt/puppetfile/installer.rb +43 -0
- data/lib/bolt/puppetfile/module.rb +89 -0
- data/lib/bolt/r10k_log_proxy.rb +1 -1
- data/lib/bolt/rerun.rb +2 -2
- data/lib/bolt/result.rb +23 -0
- data/lib/bolt/shell.rb +1 -1
- data/lib/bolt/task.rb +1 -1
- data/lib/bolt/transport/base.rb +5 -5
- data/lib/bolt/transport/docker/connection.rb +1 -1
- data/lib/bolt/transport/local/connection.rb +1 -1
- data/lib/bolt/transport/ssh.rb +1 -1
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
- data/lib/bolt/transport/winrm.rb +1 -1
- data/lib/bolt/transport/winrm/connection.rb +1 -1
- data/lib/bolt/util.rb +30 -11
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/base_config.rb +1 -1
- data/lib/bolt_server/config.rb +1 -1
- data/lib/bolt_server/file_cache.rb +12 -12
- data/lib/bolt_server/transport_app.rb +125 -26
- data/lib/bolt_spec/bolt_context.rb +4 -4
- data/lib/bolt_spec/run.rb +3 -0
- metadata +11 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec167b23fc4ceec9d9b3048ae64582a3fcf2baa1eceb8100fb26cc3c6df95cb6
|
4
|
+
data.tar.gz: 4621d274f8c14f4986dae819755c0db162cd056fbd9412275a0bcffc2169edee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1af9a8df65a1a95a7404a0b94f74bfeb9086e3a97ba03e1f19a53acbb626757249b69d812f7afe87e2da05a06d698f515b05313018a649621d98269eabfe2f93
|
7
|
+
data.tar.gz: 24f0b0f56451570d5e97d74ddb66556b48fd8c14a79fd7a0c09f10a4ef1c8590684ccee1675c4dd4126cdbcbc781bad6bf83ba2aa4d8ada0cd13279c15f53876
|
data/Puppetfile
CHANGED
@@ -6,7 +6,7 @@ moduledir File.join(File.dirname(__FILE__), 'modules')
|
|
6
6
|
|
7
7
|
# Core modules used by 'apply'
|
8
8
|
mod 'puppetlabs-service', '1.3.0'
|
9
|
-
mod 'puppetlabs-puppet_agent', '
|
9
|
+
mod 'puppetlabs-puppet_agent', '4.1.1'
|
10
10
|
mod 'puppetlabs-facts', '1.0.0'
|
11
11
|
|
12
12
|
# Core types and providers for Puppet 6
|
@@ -9,11 +9,12 @@ Puppet::DataTypes.create_type('Result') do
|
|
9
9
|
functions => {
|
10
10
|
error => Callable[[], Optional[Error]],
|
11
11
|
message => Callable[[], Optional[String]],
|
12
|
+
sensitive => Callable[[], Optional[Sensitive[Data]]],
|
12
13
|
action => Callable[[], String],
|
13
14
|
status => Callable[[], String],
|
14
15
|
to_data => Callable[[], Hash],
|
15
16
|
ok => Callable[[], Boolean],
|
16
|
-
'[]' => Callable[[String[1]], Data]
|
17
|
+
'[]' => Callable[[String[1]], Variant[Data, Sensitive[Data]]]
|
17
18
|
}
|
18
19
|
PUPPET
|
19
20
|
|
@@ -96,7 +96,7 @@ Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFun
|
|
96
96
|
|
97
97
|
# Paths expand relative to the default downloads directory for the project
|
98
98
|
# e.g. ~/.puppetlabs/bolt/downloads/
|
99
|
-
destination = Puppet.lookup(:
|
99
|
+
destination = Puppet.lookup(:bolt_project).downloads + destination
|
100
100
|
|
101
101
|
# If the destination directory already exists, delete any existing contents
|
102
102
|
if Dir.exist?(destination)
|
@@ -25,7 +25,7 @@ Puppet::Functions.create_function(:'dir::children', Puppet::Functions::InternalF
|
|
25
25
|
full_mod_path = File.join(mod_path, subpath || '') if mod_path
|
26
26
|
|
27
27
|
# Expand relative to the project directory if path is relative
|
28
|
-
project = Puppet.lookup(:
|
28
|
+
project = Puppet.lookup(:bolt_project)
|
29
29
|
pathname = Pathname.new(dirname)
|
30
30
|
full_dir = pathname.absolute? ? dirname : File.expand_path(File.join(project.path, dirname))
|
31
31
|
|
data/lib/bolt/analytics.rb
CHANGED
@@ -27,7 +27,7 @@ module Bolt
|
|
27
27
|
}.freeze
|
28
28
|
|
29
29
|
def self.build_client
|
30
|
-
logger =
|
30
|
+
logger = Bolt::Logger.logger(self)
|
31
31
|
begin
|
32
32
|
config_file = config_path(logger)
|
33
33
|
config = load_config(config_file, logger)
|
@@ -93,6 +93,10 @@ module Bolt
|
|
93
93
|
def self.write_config(filename, config)
|
94
94
|
FileUtils.mkdir_p(File.dirname(filename))
|
95
95
|
File.write(filename, config.to_yaml)
|
96
|
+
rescue StandardError => e
|
97
|
+
Bolt::Logger.warn_once('unwriteable_file', "Could not write analytics configuration to #{filename}.")
|
98
|
+
# This will get caught by build_client and create a NoopClient
|
99
|
+
raise e
|
96
100
|
end
|
97
101
|
|
98
102
|
class Client
|
@@ -106,7 +110,7 @@ module Bolt
|
|
106
110
|
require 'httpclient'
|
107
111
|
require 'locale'
|
108
112
|
|
109
|
-
@logger =
|
113
|
+
@logger = Bolt::Logger.logger(self)
|
110
114
|
@http = HTTPClient.new
|
111
115
|
@user_id = user_id
|
112
116
|
@executor = Concurrent.global_io_executor
|
@@ -210,7 +214,7 @@ module Bolt
|
|
210
214
|
attr_accessor :bundled_content
|
211
215
|
|
212
216
|
def initialize
|
213
|
-
@logger =
|
217
|
+
@logger = Bolt::Logger.logger(self)
|
214
218
|
@bundled_content = []
|
215
219
|
end
|
216
220
|
|
data/lib/bolt/applicator.rb
CHANGED
@@ -28,7 +28,7 @@ module Bolt
|
|
28
28
|
@apply_settings = apply_settings || {}
|
29
29
|
|
30
30
|
@pool = Concurrent::ThreadPoolExecutor.new(name: 'apply', max_threads: max_compiles)
|
31
|
-
@logger =
|
31
|
+
@logger = Bolt::Logger.logger(self)
|
32
32
|
end
|
33
33
|
|
34
34
|
private def libexec
|
@@ -50,15 +50,15 @@ module Bolt
|
|
50
50
|
|
51
51
|
def catalog_apply_task
|
52
52
|
@catalog_apply_task ||= begin
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
53
|
+
path = File.join(libexec, 'apply_catalog.rb')
|
54
|
+
file = { 'name' => 'apply_catalog.rb', 'path' => path }
|
55
|
+
metadata = { 'supports_noop' => true, 'input_method' => 'stdin',
|
56
|
+
'implementations' => [
|
57
|
+
{ 'name' => 'apply_catalog.rb' },
|
58
|
+
{ 'name' => 'apply_catalog.rb', 'remote' => true }
|
59
|
+
] }
|
60
|
+
Bolt::Task.new('apply_helpers::apply_catalog', metadata, [file])
|
61
|
+
end
|
62
62
|
end
|
63
63
|
|
64
64
|
def query_resources_task
|
@@ -75,34 +75,35 @@ module Bolt
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
def compile(target,
|
78
|
+
def compile(target, scope)
|
79
79
|
# This simplified Puppet node object is what .local uses to determine the
|
80
80
|
# certname of the target
|
81
81
|
node = Puppet::Node.from_data_hash('name' => target.name,
|
82
82
|
'parameters' => { 'clientcert' => target.name })
|
83
83
|
trusted = Puppet::Context::TrustedInformation.local(node)
|
84
|
-
|
84
|
+
target_data = {
|
85
85
|
name: target.name,
|
86
86
|
facts: @inventory.facts(target).merge('bolt' => true),
|
87
87
|
variables: @inventory.vars(target),
|
88
88
|
trusted: trusted.to_h
|
89
89
|
}
|
90
|
+
catalog_request = scope.merge(target: target_data)
|
90
91
|
|
91
92
|
bolt_catalog_exe = File.join(libexec, 'bolt_catalog')
|
92
93
|
old_path = ENV['PATH']
|
93
94
|
ENV['PATH'] = "#{RbConfig::CONFIG['bindir']}#{File::PATH_SEPARATOR}#{old_path}"
|
94
|
-
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data:
|
95
|
+
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_request.to_json)
|
95
96
|
ENV['PATH'] = old_path
|
96
97
|
|
97
98
|
# If bolt_catalog does not return valid JSON, we should print stderr to
|
98
99
|
# see what happened
|
99
100
|
print_logs = stat.success?
|
100
101
|
result = begin
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
JSON.parse(out)
|
103
|
+
rescue JSON::ParserError
|
104
|
+
print_logs = true
|
105
|
+
{ 'message' => "Something's gone terribly wrong! STDERR is logged." }
|
106
|
+
end
|
106
107
|
|
107
108
|
# Any messages logged by Puppet will be on stderr as JSON hashes, so we
|
108
109
|
# parse those and store them here. Any message on stderr that is not
|
@@ -183,17 +184,16 @@ module Bolt
|
|
183
184
|
type_by_reference: true,
|
184
185
|
local_reference: true)
|
185
186
|
|
186
|
-
bolt_project = @project if @project&.name
|
187
187
|
scope = {
|
188
188
|
code_ast: ast,
|
189
189
|
modulepath: @modulepath,
|
190
|
-
project:
|
190
|
+
project: @project.to_h,
|
191
191
|
pdb_config: @pdb_client.config.to_hash,
|
192
192
|
hiera_config: @hiera_config,
|
193
193
|
plan_vars: plan_vars,
|
194
194
|
# This data isn't available on the target config hash
|
195
195
|
config: @inventory.transport_data_get
|
196
|
-
}
|
196
|
+
}.freeze
|
197
197
|
description = options[:description] || 'apply catalog'
|
198
198
|
|
199
199
|
required_modules = options[:required_modules].nil? ? nil : Array(options[:required_modules])
|
@@ -64,6 +64,21 @@ module Bolt
|
|
64
64
|
when 'guide'
|
65
65
|
{ flags: OPTIONS[:global] + %w[format],
|
66
66
|
banner: GUIDE_HELP }
|
67
|
+
when 'module'
|
68
|
+
case action
|
69
|
+
when 'install'
|
70
|
+
{ flags: OPTIONS[:global] + %w[configfile force project],
|
71
|
+
banner: MODULE_INSTALL_HELP }
|
72
|
+
when 'show'
|
73
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
74
|
+
banner: MODULE_SHOW_HELP }
|
75
|
+
when 'generate-types'
|
76
|
+
{ flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
|
77
|
+
banner: MODULE_GENERATETYPES_HELP }
|
78
|
+
else
|
79
|
+
{ flags: OPTIONS[:global],
|
80
|
+
banner: MODULE_HELP }
|
81
|
+
end
|
67
82
|
when 'plan'
|
68
83
|
case action
|
69
84
|
when 'convert'
|
@@ -341,6 +356,59 @@ module Bolt
|
|
341
356
|
Show the list of targets an action would run on.
|
342
357
|
HELP
|
343
358
|
|
359
|
+
MODULE_HELP = <<~HELP
|
360
|
+
NAME
|
361
|
+
module
|
362
|
+
|
363
|
+
USAGE
|
364
|
+
bolt module <action> [options]
|
365
|
+
|
366
|
+
DESCRIPTION
|
367
|
+
Install and list modules and generate type references
|
368
|
+
|
369
|
+
ACTIONS
|
370
|
+
generate-types Generate type references to register in plans
|
371
|
+
install Install the project's modules
|
372
|
+
show List modules available to the Bolt project
|
373
|
+
HELP
|
374
|
+
|
375
|
+
MODULE_INSTALL_HELP = <<~HELP
|
376
|
+
NAME
|
377
|
+
install
|
378
|
+
|
379
|
+
USAGE
|
380
|
+
bolt module install [options]
|
381
|
+
|
382
|
+
DESCRIPTION
|
383
|
+
Install the project's modules.
|
384
|
+
|
385
|
+
Module declarations are loaded from the project's configuration
|
386
|
+
file. Bolt will automatically resolve all module dependencies,
|
387
|
+
generate a Puppetfile, and install the modules.
|
388
|
+
HELP
|
389
|
+
|
390
|
+
MODULE_GENERATETYPES_HELP = <<~HELP
|
391
|
+
NAME
|
392
|
+
generate-types
|
393
|
+
|
394
|
+
USAGE
|
395
|
+
bolt module generate-types [options]
|
396
|
+
|
397
|
+
DESCRIPTION
|
398
|
+
Generate type references to register in plans.
|
399
|
+
HELP
|
400
|
+
|
401
|
+
MODULE_SHOW_HELP = <<~HELP
|
402
|
+
NAME
|
403
|
+
show
|
404
|
+
|
405
|
+
USAGE
|
406
|
+
bolt module show [options]
|
407
|
+
|
408
|
+
DESCRIPTION
|
409
|
+
List modules available to the Bolt project.
|
410
|
+
HELP
|
411
|
+
|
344
412
|
PLAN_HELP = <<~HELP
|
345
413
|
NAME
|
346
414
|
plan
|
@@ -366,11 +434,11 @@ module Bolt
|
|
366
434
|
bolt plan convert <path> [options]
|
367
435
|
|
368
436
|
DESCRIPTION
|
369
|
-
Convert a YAML plan to a
|
437
|
+
Convert a YAML plan to a Puppet language plan and print the converted plan to stdout.
|
370
438
|
|
371
439
|
Converting a YAML plan may result in a plan that is syntactically
|
372
440
|
correct but has different behavior. Always verify a converted plan's
|
373
|
-
functionality.
|
441
|
+
functionality. Note that the converted plan is not written to a file.
|
374
442
|
|
375
443
|
EXAMPLES
|
376
444
|
bolt plan convert path/to/plan/myplan.yaml
|
@@ -421,9 +489,9 @@ module Bolt
|
|
421
489
|
the plan, including a list of available parameters.
|
422
490
|
|
423
491
|
EXAMPLES
|
424
|
-
Display a list of available
|
492
|
+
Display a list of available plans
|
425
493
|
bolt plan show
|
426
|
-
Display documentation for the
|
494
|
+
Display documentation for the aggregate::count plan
|
427
495
|
bolt plan show aggregate::count
|
428
496
|
HELP
|
429
497
|
|
@@ -678,7 +746,7 @@ module Bolt
|
|
678
746
|
'For SSH, port defaults to `22`',
|
679
747
|
'For WinRM, port defaults to `5985` or `5986` based on the --[no-]ssl setting') do |targets|
|
680
748
|
@options[:targets] ||= []
|
681
|
-
@options[:targets] << get_arg_input(targets)
|
749
|
+
@options[:targets] << Bolt::Util.get_arg_input(targets)
|
682
750
|
end
|
683
751
|
define('-q', '--query QUERY', 'Query PuppetDB to determine the targets') do |query|
|
684
752
|
@options[:query] = query
|
@@ -828,7 +896,7 @@ module Bolt
|
|
828
896
|
"This option is experimental.") do |exec|
|
829
897
|
@options[:'copy-command'] = exec
|
830
898
|
end
|
831
|
-
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout (defaults vary)') do |timeout|
|
899
|
+
define('--connect-timeout TIMEOUT', Integer, 'Connection timeout in seconds (defaults vary)') do |timeout|
|
832
900
|
@options[:'connect-timeout'] = timeout
|
833
901
|
end
|
834
902
|
define('--[no-]tty', 'Request a pseudo TTY on targets that support it') do |tty|
|
@@ -864,9 +932,9 @@ module Bolt
|
|
864
932
|
define('--modules MODULES',
|
865
933
|
'A comma-separated list of modules to install from the Puppet Forge',
|
866
934
|
'when initializing a project. Resolves and installs all dependencies.') do |modules|
|
867
|
-
@options[:modules] = modules.split(',')
|
935
|
+
@options[:modules] = modules.split(',').map { |mod| { 'name' => mod } }
|
868
936
|
end
|
869
|
-
define('--force', '
|
937
|
+
define('--force', 'Force a destructive action') do |_force|
|
870
938
|
@options[:force] = true
|
871
939
|
end
|
872
940
|
|
@@ -917,27 +985,10 @@ module Bolt
|
|
917
985
|
end
|
918
986
|
|
919
987
|
def parse_params(params)
|
920
|
-
json = get_arg_input(params)
|
988
|
+
json = Bolt::Util.get_arg_input(params)
|
921
989
|
JSON.parse(json)
|
922
990
|
rescue JSON::ParserError => e
|
923
991
|
raise Bolt::CLIError, "Unable to parse --params value as JSON: #{e}"
|
924
992
|
end
|
925
|
-
|
926
|
-
def get_arg_input(value)
|
927
|
-
if value.start_with?('@')
|
928
|
-
file = value.sub(/^@/, '')
|
929
|
-
read_arg_file(file)
|
930
|
-
elsif value == '-'
|
931
|
-
$stdin.read
|
932
|
-
else
|
933
|
-
value
|
934
|
-
end
|
935
|
-
end
|
936
|
-
|
937
|
-
def read_arg_file(file)
|
938
|
-
File.read(File.expand_path(file))
|
939
|
-
rescue StandardError => e
|
940
|
-
raise Bolt::FileError.new("Error attempting to read #{file}: #{e}", file)
|
941
|
-
end
|
942
993
|
end
|
943
994
|
end
|
data/lib/bolt/catalog.rb
CHANGED
@@ -57,8 +57,10 @@ module Bolt
|
|
57
57
|
|
58
58
|
def compile_catalog(request)
|
59
59
|
pdb_client = Bolt::PuppetDB::Client.new(Bolt::PuppetDB::Config.new(request['pdb_config']))
|
60
|
-
project = request['project']
|
61
|
-
bolt_project = Struct.new(:name, :path).new(project['name'],
|
60
|
+
project = request['project']
|
61
|
+
bolt_project = Struct.new(:name, :path, :load_as_module?).new(project['name'],
|
62
|
+
project['path'],
|
63
|
+
project['load_as_module?'])
|
62
64
|
inv = Bolt::ApplyInventory.new(request['config'])
|
63
65
|
puppet_overrides = {
|
64
66
|
bolt_pdb_client: pdb_client,
|
data/lib/bolt/cli.rb
CHANGED
@@ -40,13 +40,14 @@ module Bolt
|
|
40
40
|
'group' => %w[show],
|
41
41
|
'project' => %w[init migrate],
|
42
42
|
'apply' => %w[],
|
43
|
-
'guide' => %w[]
|
43
|
+
'guide' => %w[],
|
44
|
+
'module' => %w[install show generate-types] }.freeze
|
44
45
|
|
45
46
|
attr_reader :config, :options
|
46
47
|
|
47
48
|
def initialize(argv)
|
48
49
|
Bolt::Logger.initialize_logging
|
49
|
-
@logger =
|
50
|
+
@logger = Bolt::Logger.logger(self)
|
50
51
|
@argv = argv
|
51
52
|
@options = {}
|
52
53
|
end
|
@@ -79,7 +80,7 @@ module Bolt
|
|
79
80
|
|
80
81
|
# Wrapper method that is called by the Bolt executable. Parses the command and
|
81
82
|
# then loads the project and config. Once config is loaded, it completes the
|
82
|
-
# setup process by configuring Bolt and
|
83
|
+
# setup process by configuring Bolt and logging messages.
|
83
84
|
#
|
84
85
|
# This separation is needed since the Bolt::Outputter class that normally handles
|
85
86
|
# printing errors relies on config being loaded. All setup that happens before
|
@@ -106,6 +107,11 @@ module Bolt
|
|
106
107
|
|
107
108
|
options[:object] = remaining.shift
|
108
109
|
|
110
|
+
# Handle reading a command from a file
|
111
|
+
if options[:subcommand] == 'command' && options[:object]
|
112
|
+
options[:object] = Bolt::Util.get_arg_input(options[:object])
|
113
|
+
end
|
114
|
+
|
109
115
|
# Only parse task_options for task or plan
|
110
116
|
if %w[task plan].include?(options[:subcommand])
|
111
117
|
task_options, remaining = remaining.partition { |s| s =~ /.+=/ }
|
@@ -167,20 +173,17 @@ module Bolt
|
|
167
173
|
raise e
|
168
174
|
end
|
169
175
|
|
170
|
-
# Completes the setup process by configuring Bolt and
|
176
|
+
# Completes the setup process by configuring Bolt and log messages
|
171
177
|
def finalize_setup
|
172
178
|
Bolt::Logger.configure(config.log, config.color)
|
173
179
|
Bolt::Logger.analytics = analytics
|
174
180
|
|
175
|
-
# Logger must be configured before checking path case and project file, otherwise
|
181
|
+
# Logger must be configured before checking path case and project file, otherwise logs will not display
|
176
182
|
config.check_path_case('modulepath', config.modulepath)
|
177
183
|
config.project.check_deprecated_file
|
178
184
|
|
179
|
-
# Log
|
180
|
-
|
181
|
-
|
182
|
-
# Display warnings created during parser and config initialization
|
183
|
-
config.warnings.each { |warning| @logger.warn(warning[:msg]) }
|
185
|
+
# Log messages created during parser and config initialization
|
186
|
+
config.logs.each { |log| @logger.send(log.keys[0], log.values[0]) }
|
184
187
|
@parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
185
188
|
config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
|
186
189
|
|
@@ -213,10 +216,14 @@ module Bolt
|
|
213
216
|
end
|
214
217
|
|
215
218
|
def validate(options)
|
216
|
-
unless
|
219
|
+
# Disables the 'module' subcommand unless the module feature flag is set.
|
220
|
+
commands = COMMANDS.dup
|
221
|
+
commands.delete('module') unless ENV['BOLT_MODULE_FEATURE']
|
222
|
+
|
223
|
+
unless commands.include?(options[:subcommand])
|
217
224
|
raise Bolt::CLIError,
|
218
225
|
"Expected subcommand '#{options[:subcommand]}' to be one of " \
|
219
|
-
"#{
|
226
|
+
"#{commands.keys.join(', ')}"
|
220
227
|
end
|
221
228
|
|
222
229
|
actions = COMMANDS[options[:subcommand]]
|
@@ -356,7 +363,7 @@ module Bolt
|
|
356
363
|
# Initialize inventory and targets. Errors here are better to catch early.
|
357
364
|
# options[:target_args] will contain a string/array version of the targetting options this is passed to plans
|
358
365
|
# options[:targets] will contain a resolved set of Target objects
|
359
|
-
unless %w[project puppetfile secret
|
366
|
+
unless %w[guide module project puppetfile secret].include?(options[:subcommand]) ||
|
360
367
|
%w[convert new show].include?(options[:action])
|
361
368
|
update_targets(options)
|
362
369
|
end
|
@@ -407,6 +414,8 @@ module Bolt
|
|
407
414
|
end
|
408
415
|
when 'group'
|
409
416
|
list_groups
|
417
|
+
when 'module'
|
418
|
+
list_modules
|
410
419
|
end
|
411
420
|
return 0
|
412
421
|
when 'show-modules'
|
@@ -446,12 +455,19 @@ module Bolt
|
|
446
455
|
when 'run'
|
447
456
|
code = run_plan(options[:object], options[:task_options], options[:target_args], options)
|
448
457
|
end
|
458
|
+
when 'module'
|
459
|
+
case options[:action]
|
460
|
+
when 'install'
|
461
|
+
code = install_project_modules
|
462
|
+
when 'generate-types'
|
463
|
+
code = generate_types
|
464
|
+
end
|
449
465
|
when 'puppetfile'
|
450
466
|
case options[:action]
|
451
467
|
when 'generate-types'
|
452
468
|
code = generate_types
|
453
469
|
when 'install'
|
454
|
-
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath)
|
470
|
+
code = install_puppetfile(config.puppetfile_config, config.puppetfile, config.modulepath.first)
|
455
471
|
end
|
456
472
|
when 'secret'
|
457
473
|
code = Bolt::Secret.execute(plugins, outputter, options)
|
@@ -801,36 +817,38 @@ module Bolt
|
|
801
817
|
old_config = project + 'bolt.yaml'
|
802
818
|
config = project + 'bolt-project.yaml'
|
803
819
|
puppetfile = project + 'Puppetfile'
|
804
|
-
|
820
|
+
moduledir = project + 'modules'
|
821
|
+
|
822
|
+
# Warn the user if the project directory already exists. We don't error
|
823
|
+
# here since users might not have installed any modules yet. If both
|
824
|
+
# bolt.yaml and bolt-project.yaml exist, this will just warn about
|
825
|
+
# bolt-project.yaml and subsequent Bolt actions will warn about both files
|
826
|
+
# existing.
|
827
|
+
if config.exist?
|
828
|
+
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
829
|
+
elsif old_config.exist?
|
830
|
+
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
831
|
+
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
832
|
+
end
|
805
833
|
|
806
|
-
# If modules were specified, first check if there is already a Puppetfile
|
807
|
-
# directory, erroring if there is. If there is no
|
808
|
-
#
|
809
|
-
#
|
810
|
-
# dependencies are caught early and do not create a project directory.
|
834
|
+
# If modules were specified, first check if there is already a Puppetfile
|
835
|
+
# at the project directory, erroring if there is. If there is no
|
836
|
+
# Puppetfile, install the specified modules. The module installer will
|
837
|
+
# resolve dependencies, generate a Puppetfile, and install the modules.
|
811
838
|
if options[:modules]
|
812
839
|
if puppetfile.exist?
|
813
840
|
raise Bolt::CLIError,
|
814
|
-
"Found existing Puppetfile at #{puppetfile}, unable to initialize
|
815
|
-
"
|
816
|
-
else
|
817
|
-
puppetfile_specs = resolve_puppetfile_specs
|
841
|
+
"Found existing Puppetfile at #{puppetfile}, unable to initialize "\
|
842
|
+
"project with modules."
|
818
843
|
end
|
844
|
+
|
845
|
+
install_modules(puppetfile, {}, moduledir, options[:modules])
|
819
846
|
end
|
820
847
|
|
821
|
-
#
|
822
|
-
#
|
823
|
-
#
|
824
|
-
|
825
|
-
# both files existing
|
826
|
-
if config.exist?
|
827
|
-
@logger.warn "Found existing project directory at #{project}. Skipping file creation."
|
828
|
-
# This won't get called if bolt-project.yaml exists
|
829
|
-
elsif old_config.exist?
|
830
|
-
@logger.warn "Found existing #{old_config.basename} at #{project}. "\
|
831
|
-
"#{old_config.basename} is deprecated, please rename to #{config.basename}."
|
832
|
-
# Bless the project directory as a...wait for it...project
|
833
|
-
else
|
848
|
+
# If either bolt.yaml or bolt-project.yaml exist, the user has already
|
849
|
+
# been warned and we can just finish project creation. Otherwise, create a
|
850
|
+
# bolt-project.yaml with the project name in it.
|
851
|
+
unless config.exist? || old_config.exist?
|
834
852
|
begin
|
835
853
|
content = { 'name' => name }
|
836
854
|
File.write(config.to_path, content.to_yaml)
|
@@ -840,109 +858,86 @@ module Bolt
|
|
840
858
|
end
|
841
859
|
end
|
842
860
|
|
843
|
-
# Write the generated Puppetfile to the fancy new project
|
844
|
-
if puppetfile_specs
|
845
|
-
File.write(puppetfile, puppetfile_specs.join("\n"))
|
846
|
-
outputter.print_message "Successfully created Puppetfile at #{puppetfile}"
|
847
|
-
# Install the modules from our shiny new Puppetfile
|
848
|
-
if install_puppetfile({}, puppetfile, modulepath)
|
849
|
-
outputter.print_message "Successfully installed #{options[:modules].join(', ')}"
|
850
|
-
else
|
851
|
-
raise Bolt::CLIError, "Could not install #{options[:modules].join(', ')}"
|
852
|
-
end
|
853
|
-
end
|
854
|
-
|
855
861
|
0
|
856
862
|
end
|
857
863
|
|
858
|
-
#
|
859
|
-
#
|
860
|
-
def
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
options[:modules].each do |mod_name|
|
866
|
-
model.add_module(
|
867
|
-
PuppetfileResolver::Puppetfile::ForgeModule.new(mod_name).tap { |mod| mod.version = :latest }
|
868
|
-
)
|
869
|
-
end
|
870
|
-
|
871
|
-
# Make sure the Puppetfile model is valid
|
872
|
-
unless model.valid?
|
873
|
-
raise Bolt::ValidationError,
|
874
|
-
"Unable to resolve dependencies for #{options[:modules].join(', ')}"
|
864
|
+
# Installs modules declared in the project configuration file.
|
865
|
+
#
|
866
|
+
def install_project_modules
|
867
|
+
if config.project.modules.nil?
|
868
|
+
outputter.print_message "Project configuration file '#{config.project.project_file}' "\
|
869
|
+
"does not specify any module dependencies. Nothing to do."
|
870
|
+
return 0
|
875
871
|
end
|
876
872
|
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
cache: nil,
|
883
|
-
ui: nil,
|
884
|
-
module_paths: [],
|
885
|
-
allow_missing_modules: true
|
873
|
+
install_modules(
|
874
|
+
config.puppetfile,
|
875
|
+
config.puppetfile_config,
|
876
|
+
config.project.path + '.modules',
|
877
|
+
config.project.modules
|
886
878
|
)
|
879
|
+
end
|
887
880
|
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
|
892
|
-
|
893
|
-
|
894
|
-
|
895
|
-
|
881
|
+
# Installs modules declared in the project configuration file.
|
882
|
+
#
|
883
|
+
def install_modules(puppetfile_path, config, moduledir, modules)
|
884
|
+
require 'bolt/puppetfile'
|
885
|
+
require 'bolt/puppetfile/installer'
|
886
|
+
|
887
|
+
puppetfile = Bolt::Puppetfile.new(modules)
|
888
|
+
|
889
|
+
# If the Puppetfile exists, check if it includes specs for each declared
|
890
|
+
# module, erroring if there are any missing. Otherwise, resolve the
|
891
|
+
# module dependencies and write a new Puppetfile. Users can forcibly
|
892
|
+
# overwrite an existing Puppetfile with the '--force' option.
|
893
|
+
if puppetfile_path.exist? && !options[:force]
|
894
|
+
outputter.print_message "Parsing existing Puppetfile at #{puppetfile_path}"
|
895
|
+
existing = Bolt::Puppetfile.parse(puppetfile_path)
|
896
|
+
|
897
|
+
unless existing.modules.superset? puppetfile.modules
|
898
|
+
missing_modules = puppetfile.modules - existing.modules
|
899
|
+
|
900
|
+
message = <<~MESSAGE.chomp
|
901
|
+
Puppetfile #{puppetfile_path} is missing specifications for the following
|
902
|
+
module declarations:
|
903
|
+
|
904
|
+
#{missing_modules.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
|
905
|
+
|
906
|
+
This may not be a Puppetfile managed by Bolt. To forcibly overwrite the
|
907
|
+
Puppetfile, run 'bolt module install --force'.
|
908
|
+
MESSAGE
|
909
|
+
|
910
|
+
raise Bolt::Error.new(message, 'bolt/missing-module-specs')
|
896
911
|
end
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
"Unknown module name#{plural} #{names.join(', ')}"
|
903
|
-
end
|
904
|
-
|
905
|
-
# Filter the dependency graph to only include module specifications
|
906
|
-
spec_graph = result.specifications.select do |_name, spec|
|
907
|
-
spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
|
912
|
+
else
|
913
|
+
outputter.print_message "Resolving module dependencies, this may take a moment"
|
914
|
+
puppetfile.resolve
|
915
|
+
outputter.print_message "Writing Puppetfile at #{puppetfile_path}"
|
916
|
+
puppetfile.write(puppetfile_path, force: true)
|
908
917
|
end
|
909
918
|
|
910
|
-
|
911
|
-
|
912
|
-
"mod '#{spec.owner}-#{spec.name}', '#{spec.version}'"
|
913
|
-
end
|
914
|
-
end
|
919
|
+
outputter.print_message "Syncing modules from #{puppetfile_path} to #{moduledir}"
|
920
|
+
ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile_path, moduledir)
|
915
921
|
|
916
|
-
|
917
|
-
|
918
|
-
require 'bolt/r10k_log_proxy'
|
922
|
+
# Automatically generate types after installing modules.
|
923
|
+
pal.generate_types
|
919
924
|
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
root: puppetfile.dirname.to_s,
|
924
|
-
puppetfile: puppetfile.to_s,
|
925
|
-
moduledir: moduledir
|
926
|
-
}
|
925
|
+
outputter.print_puppetfile_result(ok, puppetfile_path, moduledir)
|
926
|
+
ok ? 0 : 1
|
927
|
+
end
|
927
928
|
|
928
|
-
|
929
|
-
|
930
|
-
|
929
|
+
# Loads a Puppetfile and installs its modules.
|
930
|
+
#
|
931
|
+
def install_puppetfile(config, puppetfile, moduledir)
|
932
|
+
require 'bolt/puppetfile/installer'
|
931
933
|
|
932
|
-
|
933
|
-
R10K::Logging.instance_variable_set(:@outputter, Bolt::R10KLogProxy.new)
|
934
|
+
ok = Bolt::Puppetfile::Installer.new(config).install(puppetfile, moduledir)
|
934
935
|
|
935
|
-
|
936
|
-
|
937
|
-
# Automatically generate types after installing modules
|
938
|
-
pal.generate_types
|
936
|
+
# Automatically generate types after installing modules.
|
937
|
+
pal.generate_types
|
939
938
|
|
940
|
-
|
941
|
-
|
942
|
-
raise Bolt::FileError.new("Could not find a Puppetfile at #{puppetfile}", puppetfile)
|
943
|
-
end
|
944
|
-
rescue R10K::Error => e
|
945
|
-
raise PuppetfileError, e
|
939
|
+
outputter.print_puppetfile_result(ok, puppetfile, moduledir)
|
940
|
+
ok ? 0 : 1
|
946
941
|
end
|
947
942
|
|
948
943
|
def pal
|
@@ -958,17 +953,17 @@ module Bolt
|
|
958
953
|
# Collects the list of Bolt guides and maps them to their topics.
|
959
954
|
def guides
|
960
955
|
@guides ||= begin
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
971
|
-
|
956
|
+
root_path = File.expand_path(File.join(__dir__, '..', '..', 'guides'))
|
957
|
+
files = Dir.children(root_path).sort
|
958
|
+
|
959
|
+
files.each_with_object({}) do |file, guides|
|
960
|
+
next if file !~ /\.txt\z/
|
961
|
+
topic = File.basename(file, '.txt')
|
962
|
+
guides[topic] = File.join(root_path, file)
|
963
|
+
end
|
964
|
+
rescue SystemCallError => e
|
965
|
+
raise Bolt::FileError.new("#{e.message}: unable to load guides directory", root_path)
|
966
|
+
end
|
972
967
|
end
|
973
968
|
|
974
969
|
# Display the list of available Bolt guides.
|
@@ -1019,10 +1014,10 @@ module Bolt
|
|
1019
1014
|
|
1020
1015
|
def analytics
|
1021
1016
|
@analytics ||= begin
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1017
|
+
client = Bolt::Analytics.build_client
|
1018
|
+
client.bundled_content = bundled_content
|
1019
|
+
client
|
1020
|
+
end
|
1026
1021
|
end
|
1027
1022
|
|
1028
1023
|
def bundled_content
|
@@ -1057,17 +1052,10 @@ module Bolt
|
|
1057
1052
|
content
|
1058
1053
|
end
|
1059
1054
|
|
1060
|
-
def config_loaded
|
1061
|
-
msg = <<~MSG.chomp
|
1062
|
-
Loaded configuration from: '#{config.config_files.join("', '")}'
|
1063
|
-
MSG
|
1064
|
-
@logger.info(msg)
|
1065
|
-
end
|
1066
|
-
|
1067
1055
|
# Gem installs include the aggregate, canary, and puppetdb_fact modules, while
|
1068
1056
|
# package installs include modules listed in the Bolt repo Puppetfile
|
1069
1057
|
def incomplete_install?
|
1070
|
-
(Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact]).empty?
|
1058
|
+
(Dir.children(Bolt::PAL::MODULES_PATH) - %w[aggregate canary puppetdb_fact secure_env_vars]).empty?
|
1071
1059
|
end
|
1072
1060
|
|
1073
1061
|
# Mimicks the output from Outputter::Human#fatal_error. This should be used to print
|