bolt 2.30.0 → 2.34.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 +12 -12
- data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/facts.rb +6 -0
- data/bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb +2 -2
- data/bolt-modules/boltlib/lib/puppet/functions/run_command.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_script.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/run_task_with.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/upload_file.rb +1 -1
- data/bolt-modules/boltlib/lib/puppet/functions/write_file.rb +2 -2
- data/bolt-modules/out/lib/puppet/functions/out/message.rb +44 -1
- data/bolt-modules/prompt/lib/puppet/functions/prompt.rb +3 -0
- data/guides/logging.txt +18 -0
- data/guides/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/bolt_option_parser.rb +6 -1
- data/lib/bolt/cli.rb +82 -142
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +31 -13
- data/lib/bolt/config/transport/options.rb +2 -2
- data/lib/bolt/error.rb +13 -3
- data/lib/bolt/executor.rb +24 -12
- data/lib/bolt/inventory.rb +10 -9
- data/lib/bolt/inventory/group.rb +2 -1
- data/lib/bolt/module_installer.rb +117 -91
- data/lib/bolt/{puppetfile → module_installer}/installer.rb +3 -2
- data/lib/bolt/module_installer/puppetfile.rb +117 -0
- data/lib/bolt/module_installer/puppetfile/forge_module.rb +54 -0
- data/lib/bolt/module_installer/puppetfile/git_module.rb +37 -0
- data/lib/bolt/module_installer/puppetfile/module.rb +26 -0
- data/lib/bolt/module_installer/resolver.rb +76 -0
- data/lib/bolt/module_installer/specs.rb +93 -0
- data/lib/bolt/module_installer/specs/forge_spec.rb +85 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +179 -0
- data/lib/bolt/outputter.rb +0 -47
- data/lib/bolt/outputter/human.rb +46 -16
- data/lib/bolt/outputter/json.rb +17 -8
- data/lib/bolt/pal.rb +52 -40
- data/lib/bolt/pal/yaml_plan.rb +4 -2
- data/lib/bolt/pal/yaml_plan/evaluator.rb +23 -1
- data/lib/bolt/pal/yaml_plan/loader.rb +14 -9
- data/lib/bolt/plan_creator.rb +160 -0
- data/lib/bolt/plugin.rb +2 -2
- data/lib/bolt/project.rb +6 -11
- data/lib/bolt/project_migrator.rb +1 -1
- data/lib/bolt/project_migrator/base.rb +2 -2
- data/lib/bolt/project_migrator/config.rb +5 -4
- data/lib/bolt/project_migrator/inventory.rb +3 -3
- data/lib/bolt/project_migrator/modules.rb +23 -21
- data/lib/bolt/puppetdb/config.rb +5 -5
- data/lib/bolt/result.rb +23 -11
- data/lib/bolt/shell/bash.rb +14 -8
- data/lib/bolt/shell/powershell.rb +12 -7
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/base.rb +18 -18
- data/lib/bolt/transport/docker.rb +23 -6
- data/lib/bolt/transport/orch.rb +26 -17
- data/lib/bolt/transport/remote.rb +3 -3
- data/lib/bolt/transport/simple.rb +6 -6
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +5 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/file_cache.rb +2 -0
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +92 -12
- data/lib/bolt_spec/bolt_context.rb +4 -2
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/command_stub.rb +1 -1
- data/lib/bolt_spec/plans/action_stubs/script_stub.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +5 -5
- data/lib/bolt_spec/run.rb +1 -1
- metadata +24 -9
- data/lib/bolt/puppetfile.rb +0 -142
- data/lib/bolt/puppetfile/module.rb +0 -90
- data/lib/bolt_server/pe/pal.rb +0 -67
- data/modules/secure_env_vars/plans/init.pp +0 -20
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/config'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class Config
|
7
|
+
class Modulepath
|
8
|
+
BOLTLIB_PATH = File.expand_path('../../../bolt-modules', __dir__)
|
9
|
+
MODULES_PATH = File.expand_path('../../../modules', __dir__)
|
10
|
+
|
11
|
+
# The user_modulepath only includes the original modulepath and is used during pluginsync.
|
12
|
+
# We don't want to pluginsync any of the content from BOLT_MODULES since that content
|
13
|
+
# includes core modules that can conflict with modules installed with an agent.
|
14
|
+
attr_reader :user_modulepath
|
15
|
+
|
16
|
+
def initialize(user_modulepath, boltlib_path: BOLTLIB_PATH, builtin_content_path: MODULES_PATH)
|
17
|
+
@user_modulepath = Array(user_modulepath).flatten
|
18
|
+
@boltlib_path = Array(boltlib_path).flatten
|
19
|
+
@builtin_content_path = Array(builtin_content_path).flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
# The full_modulepath includes both the BOLTLIB
|
23
|
+
# path and the MODULES_PATH to ensure bolt functions and
|
24
|
+
# built-in content are available in the compliler
|
25
|
+
def full_modulepath
|
26
|
+
@boltlib_path + @user_modulepath + @builtin_content_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/bolt/config/options.rb
CHANGED
@@ -234,26 +234,44 @@ module Bolt
|
|
234
234
|
type: Array,
|
235
235
|
items: {
|
236
236
|
type: [Hash, String],
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
237
|
+
oneOf: [
|
238
|
+
{
|
239
|
+
required: ["name"],
|
240
|
+
properties: {
|
241
|
+
"name" => {
|
242
|
+
description: "The name of the module.",
|
243
|
+
type: String
|
244
|
+
},
|
245
|
+
"version_requirement" => {
|
246
|
+
description: "The version requirement for the module. Accepts a specific version (1.2.3), version "\
|
247
|
+
"shorthand (1.2.x), or a version range (>= 1.2.0).",
|
248
|
+
type: String
|
249
|
+
}
|
250
|
+
}
|
242
251
|
},
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
252
|
+
{
|
253
|
+
required: %w[git ref],
|
254
|
+
properties: {
|
255
|
+
"git" => {
|
256
|
+
description: "The URL to the public git repository.",
|
257
|
+
type: String
|
258
|
+
},
|
259
|
+
"ref" => {
|
260
|
+
description: "The git reference to check out. Can be either a branch, tag, or commit SHA.",
|
261
|
+
type: String
|
262
|
+
}
|
263
|
+
}
|
247
264
|
}
|
248
|
-
|
265
|
+
]
|
249
266
|
},
|
250
267
|
_plugin: false,
|
251
268
|
_example: [
|
252
|
-
{ "name" => "puppetlabs-mysql" },
|
253
269
|
"puppetlabs-facts",
|
270
|
+
{ "name" => "puppetlabs-mysql" },
|
254
271
|
{ "name" => "puppetlabs-apache", "version_requirement" => "5.5.0" },
|
255
272
|
{ "name" => "puppetlabs-puppetdb", "version_requirement" => "7.x" },
|
256
|
-
{ "name" => "puppetlabs-firewall", "version_requirement" => ">= 1.0.0 < 3.0.0" }
|
273
|
+
{ "name" => "puppetlabs-firewall", "version_requirement" => ">= 1.0.0 < 3.0.0" },
|
274
|
+
{ "git" => "https://github.com/puppetlabs/puppetlabs-apt", "ref" => "7.6.0" }
|
257
275
|
]
|
258
276
|
},
|
259
277
|
"name" => {
|
@@ -329,7 +347,7 @@ module Bolt
|
|
329
347
|
"server_urls" => {
|
330
348
|
description: "An array containing the PuppetDB host to connect to. Include the protocol `https` "\
|
331
349
|
"and the port, which is usually `8081`. For example, "\
|
332
|
-
"`https://my-
|
350
|
+
"`https://my-puppetdb-server.com:8081`.",
|
333
351
|
type: Array,
|
334
352
|
_example: ["https://puppet.example.com:8081"]
|
335
353
|
},
|
@@ -357,7 +357,7 @@ module Bolt
|
|
357
357
|
description: "The URL of the host used for API requests.",
|
358
358
|
format: "uri",
|
359
359
|
_plugin: true,
|
360
|
-
_example: "https://api.example.com"
|
360
|
+
_example: "https://api.example.com:<port>"
|
361
361
|
},
|
362
362
|
"shell-command" => {
|
363
363
|
type: String,
|
@@ -374,7 +374,7 @@ module Bolt
|
|
374
374
|
},
|
375
375
|
"ssh-command" => {
|
376
376
|
type: [Array, String],
|
377
|
-
description: "The command and
|
377
|
+
description: "The command and options to use when SSHing. This option is used when you need support for "\
|
378
378
|
"features or algorithms that are not supported by the net-ssh Ruby library. **This option "\
|
379
379
|
"is experimental.** You can read more about this option in [Native SSH "\
|
380
380
|
"transport](experimental_features.md#native-ssh-transport).",
|
data/lib/bolt/error.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'bolt/util'
|
4
|
+
|
3
5
|
module Bolt
|
4
6
|
class Error < RuntimeError
|
5
7
|
attr_reader :kind, :details, :issue_code, :error_code
|
@@ -24,6 +26,10 @@ module Bolt
|
|
24
26
|
h
|
25
27
|
end
|
26
28
|
|
29
|
+
def add_filelineno(details)
|
30
|
+
@details.merge!(details) unless @details['file']
|
31
|
+
end
|
32
|
+
|
27
33
|
def to_json(opts = nil)
|
28
34
|
to_h.to_json(opts)
|
29
35
|
end
|
@@ -33,13 +39,17 @@ module Bolt
|
|
33
39
|
end
|
34
40
|
|
35
41
|
def self.unknown_task(task)
|
36
|
-
|
37
|
-
|
42
|
+
command = Bolt::Util.powershell? ? "Get-BoltTask" : "bolt task show"
|
43
|
+
new(
|
44
|
+
"Could not find a task named '#{task}'. For a list of available tasks, run '#{command}'.",
|
45
|
+
'bolt/unknown-task'
|
46
|
+
)
|
38
47
|
end
|
39
48
|
|
40
49
|
def self.unknown_plan(plan)
|
50
|
+
command = Bolt::Util.powershell? ? "Get-BoltPlan" : "bolt plan show"
|
41
51
|
new(
|
42
|
-
"Could not find a plan named
|
52
|
+
"Could not find a plan named '#{plan}'. For a list of available plans, run '#{command}'.",
|
43
53
|
'bolt/unknown-plan'
|
44
54
|
)
|
45
55
|
end
|
data/lib/bolt/executor.rb
CHANGED
@@ -253,33 +253,33 @@ module Bolt
|
|
253
253
|
result
|
254
254
|
end
|
255
255
|
|
256
|
-
def run_command(targets, command, options = {})
|
256
|
+
def run_command(targets, command, options = {}, position = [])
|
257
257
|
description = options.fetch(:description, "command '#{command}'")
|
258
258
|
log_action(description, targets) do
|
259
259
|
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
260
260
|
|
261
261
|
batch_execute(targets) do |transport, batch|
|
262
262
|
with_node_logging("Running command '#{command}'", batch) do
|
263
|
-
transport.batch_command(batch, command, options, &method(:publish_event))
|
263
|
+
transport.batch_command(batch, command, options, position, &method(:publish_event))
|
264
264
|
end
|
265
265
|
end
|
266
266
|
end
|
267
267
|
end
|
268
268
|
|
269
|
-
def run_script(targets, script, arguments, options = {})
|
269
|
+
def run_script(targets, script, arguments, options = {}, position = [])
|
270
270
|
description = options.fetch(:description, "script #{script}")
|
271
271
|
log_action(description, targets) do
|
272
272
|
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
273
273
|
|
274
274
|
batch_execute(targets) do |transport, batch|
|
275
275
|
with_node_logging("Running script #{script} with '#{arguments.to_json}'", batch) do
|
276
|
-
transport.batch_script(batch, script, arguments, options, &method(:publish_event))
|
276
|
+
transport.batch_script(batch, script, arguments, options, position, &method(:publish_event))
|
277
277
|
end
|
278
278
|
end
|
279
279
|
end
|
280
280
|
end
|
281
281
|
|
282
|
-
def run_task(targets, task, arguments, options = {})
|
282
|
+
def run_task(targets, task, arguments, options = {}, position = [])
|
283
283
|
description = options.fetch(:description, "task #{task.name}")
|
284
284
|
log_action(description, targets) do
|
285
285
|
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
@@ -287,13 +287,25 @@ module Bolt
|
|
287
287
|
|
288
288
|
batch_execute(targets) do |transport, batch|
|
289
289
|
with_node_logging("Running task #{task.name} with '#{arguments.to_json}'", batch) do
|
290
|
-
transport.batch_task(batch, task, arguments, options, &method(:publish_event))
|
290
|
+
transport.batch_task(batch, task, arguments, options, position, &method(:publish_event))
|
291
291
|
end
|
292
292
|
end
|
293
293
|
end
|
294
294
|
end
|
295
295
|
|
296
|
-
def
|
296
|
+
def run_task_with_minimal_logging(targets, task, arguments, options = {})
|
297
|
+
description = options.fetch(:description, "task #{task.name}")
|
298
|
+
log_action(description, targets) do
|
299
|
+
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
300
|
+
arguments['_task'] = task.name
|
301
|
+
|
302
|
+
batch_execute(targets) do |transport, batch|
|
303
|
+
transport.batch_task(batch, task, arguments, options, [], &method(:publish_event))
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
def run_task_with(target_mapping, task, options = {}, position = [])
|
297
309
|
targets = target_mapping.keys
|
298
310
|
description = options.fetch(:description, "task #{task.name}")
|
299
311
|
|
@@ -303,26 +315,26 @@ module Bolt
|
|
303
315
|
|
304
316
|
batch_execute(targets) do |transport, batch|
|
305
317
|
with_node_logging("Running task #{task.name}'", batch) do
|
306
|
-
transport.batch_task_with(batch, task, target_mapping, options, &method(:publish_event))
|
318
|
+
transport.batch_task_with(batch, task, target_mapping, options, position, &method(:publish_event))
|
307
319
|
end
|
308
320
|
end
|
309
321
|
end
|
310
322
|
end
|
311
323
|
|
312
|
-
def upload_file(targets, source, destination, options = {})
|
324
|
+
def upload_file(targets, source, destination, options = {}, position = [])
|
313
325
|
description = options.fetch(:description, "file upload from #{source} to #{destination}")
|
314
326
|
log_action(description, targets) do
|
315
327
|
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
316
328
|
|
317
329
|
batch_execute(targets) do |transport, batch|
|
318
330
|
with_node_logging("Uploading file #{source} to #{destination}", batch) do
|
319
|
-
transport.batch_upload(batch, source, destination, options, &method(:publish_event))
|
331
|
+
transport.batch_upload(batch, source, destination, options, position, &method(:publish_event))
|
320
332
|
end
|
321
333
|
end
|
322
334
|
end
|
323
335
|
end
|
324
336
|
|
325
|
-
def download_file(targets, source, destination, options = {})
|
337
|
+
def download_file(targets, source, destination, options = {}, position = [])
|
326
338
|
description = options.fetch(:description, "file download from #{source} to #{destination}")
|
327
339
|
|
328
340
|
begin
|
@@ -337,7 +349,7 @@ module Bolt
|
|
337
349
|
|
338
350
|
batch_execute(targets) do |transport, batch|
|
339
351
|
with_node_logging("Downloading file #{source} to #{destination}", batch) do
|
340
|
-
transport.batch_download(batch, source, destination, options, &method(:publish_event))
|
352
|
+
transport.batch_download(batch, source, destination, options, position, &method(:publish_event))
|
341
353
|
end
|
342
354
|
end
|
343
355
|
end
|
data/lib/bolt/inventory.rb
CHANGED
@@ -56,16 +56,17 @@ module Bolt
|
|
56
56
|
rescue Psych::Exception
|
57
57
|
raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
|
58
58
|
end
|
59
|
+
elsif config.inventoryfile
|
60
|
+
data = Bolt::Util.read_yaml_hash(config.inventoryfile, 'inventory')
|
61
|
+
logger.debug("Loaded inventory from #{config.inventoryfile}")
|
59
62
|
else
|
60
|
-
data =
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
# This avoids rubocop complaining about identical conditionals
|
68
|
-
logger.debug("Loaded inventory from #{config.inventoryfile}") if config.inventoryfile
|
63
|
+
data = Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
|
64
|
+
|
65
|
+
if config.default_inventoryfile.exist?
|
66
|
+
logger.debug("Loaded inventory from #{config.default_inventoryfile}")
|
67
|
+
else
|
68
|
+
logger.debug("Tried to load inventory from #{config.default_inventoryfile}, but the file does not exist")
|
69
|
+
end
|
69
70
|
end
|
70
71
|
|
71
72
|
# Resolve plugin references from transport config
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -241,10 +241,11 @@ module Bolt
|
|
241
241
|
end
|
242
242
|
|
243
243
|
if input.key?('nodes')
|
244
|
+
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
244
245
|
msg = <<~MSG.chomp
|
245
246
|
Found 'nodes' key in group #{@name}. This looks like a v1 inventory file, which is
|
246
247
|
no longer supported by Bolt. Migrate to a v2 inventory file automatically using
|
247
|
-
'
|
248
|
+
'#{command}'.
|
248
249
|
MSG
|
249
250
|
raise ValidationError.new(msg, nil)
|
250
251
|
end
|
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'bolt/error'
|
4
4
|
require 'bolt/logger'
|
5
|
+
require 'bolt/module_installer/installer'
|
6
|
+
require 'bolt/module_installer/puppetfile'
|
7
|
+
require 'bolt/module_installer/resolver'
|
8
|
+
require 'bolt/module_installer/specs'
|
5
9
|
|
6
10
|
module Bolt
|
7
11
|
class ModuleInstaller
|
@@ -13,36 +17,53 @@ module Bolt
|
|
13
17
|
|
14
18
|
# Adds a single module to the project.
|
15
19
|
#
|
16
|
-
def add(name,
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
@outputter.print_message "Project configuration file #{config_path} already "\
|
26
|
-
"includes module #{new_module}. Nothing to do."
|
20
|
+
def add(name, specs, puppetfile_path, moduledir, config_path)
|
21
|
+
project_specs = Specs.new(specs)
|
22
|
+
|
23
|
+
# Exit early if project config already includes a spec with this name.
|
24
|
+
if project_specs.include?(name)
|
25
|
+
@outputter.print_message(
|
26
|
+
"Project configuration file #{config_path} already includes specification with name "\
|
27
|
+
"#{name}. Nothing to do."
|
28
|
+
)
|
27
29
|
return true
|
28
30
|
end
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
@outputter.print_message("Adding module #{name} to project\n\n")
|
33
|
+
|
34
|
+
# Generate the specs to resolve from. If a Puppetfile exists, parse it and
|
35
|
+
# convert the modules to specs. Otherwise, use the project specs.
|
36
|
+
resolve_specs = if puppetfile_path.exist?
|
37
|
+
existing_puppetfile = Puppetfile.parse(puppetfile_path)
|
38
|
+
existing_puppetfile.assert_satisfies(project_specs)
|
39
|
+
Specs.from_puppetfile(existing_puppetfile)
|
40
|
+
else
|
41
|
+
project_specs
|
42
|
+
end
|
43
|
+
|
44
|
+
# Resolve module dependencies. Attempt to first resolve with resolve
|
45
|
+
# specss. If that fails, fall back to resolving from project specs.
|
46
|
+
# This prevents Bolt from modifying installed modules unless there is
|
47
|
+
# a version conflict.
|
48
|
+
@outputter.print_action_step("Resolving module dependencies, this may take a moment")
|
49
|
+
|
50
|
+
begin
|
51
|
+
resolve_specs.add_specs('name' => name)
|
52
|
+
puppetfile = Resolver.new.resolve(resolve_specs)
|
53
|
+
rescue Bolt::Error
|
54
|
+
project_specs.add_specs('name' => name)
|
55
|
+
puppetfile = Resolver.new.resolve(project_specs)
|
33
56
|
end
|
34
57
|
|
35
|
-
#
|
36
|
-
|
37
|
-
# project config or modify the Puppetfile.
|
38
|
-
puppetfile = add_new_module_to_puppetfile(new_module, modules, puppetfile_path)
|
58
|
+
# Display the diff between the existing Puppetfile and the new Puppetfile.
|
59
|
+
print_puppetfile_diff(existing_puppetfile, puppetfile)
|
39
60
|
|
40
61
|
# Add the module to the project configuration.
|
41
|
-
@outputter.
|
62
|
+
@outputter.print_action_step("Updating project configuration file at #{config_path}")
|
42
63
|
|
43
64
|
data = Bolt::Util.read_yaml_hash(config_path, 'project')
|
44
65
|
data['modules'] ||= []
|
45
|
-
data['modules'] <<
|
66
|
+
data['modules'] << name.tr('-', '/')
|
46
67
|
|
47
68
|
begin
|
48
69
|
File.write(config_path, data.to_yaml)
|
@@ -54,76 +75,104 @@ module Bolt
|
|
54
75
|
end
|
55
76
|
|
56
77
|
# Write the Puppetfile.
|
57
|
-
@outputter.
|
78
|
+
@outputter.print_action_step("Writing Puppetfile at #{puppetfile_path}")
|
58
79
|
puppetfile.write(puppetfile_path, moduledir)
|
59
80
|
|
60
81
|
# Install the modules.
|
61
82
|
install_puppetfile(puppetfile_path, moduledir)
|
62
83
|
end
|
63
84
|
|
64
|
-
#
|
85
|
+
# Outputs a diff of an old Puppetfile and a new Puppetfile.
|
65
86
|
#
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
87
|
+
def print_puppetfile_diff(old, new)
|
88
|
+
# Build hashes mapping the module name to the module object. This makes it
|
89
|
+
# a little easier to determine which modules have been added, removed, or
|
90
|
+
# modified.
|
91
|
+
old = (old&.modules || []).each_with_object({}) do |mod, acc|
|
92
|
+
next unless mod.type == :forge
|
93
|
+
acc[mod.full_name] = mod
|
94
|
+
end
|
95
|
+
|
96
|
+
new = new.modules.each_with_object({}) do |mod, acc|
|
97
|
+
next unless mod.type == :forge
|
98
|
+
acc[mod.full_name] = mod
|
99
|
+
end
|
100
|
+
|
101
|
+
# New modules are those present in new but not in old.
|
102
|
+
added = new.reject { |full_name, _mod| old.include?(full_name) }.values
|
103
|
+
|
104
|
+
if added.any?
|
105
|
+
diff = "Adding the following modules:\n"
|
106
|
+
added.each { |mod| diff += "#{mod.full_name} #{mod.version}\n" }
|
107
|
+
@outputter.print_action_step(diff)
|
108
|
+
end
|
109
|
+
|
110
|
+
# Upgraded modules are those that have a newer version in new than old.
|
111
|
+
upgraded = new.select do |full_name, mod|
|
112
|
+
if old.include?(full_name)
|
113
|
+
mod.version > old[full_name].version
|
114
|
+
end
|
115
|
+
end.keys
|
116
|
+
|
117
|
+
if upgraded.any?
|
118
|
+
diff = "Upgrading the following modules:\n"
|
119
|
+
upgraded.each { |full_name| diff += "#{full_name} #{old[full_name].version} to #{new[full_name].version}\n" }
|
120
|
+
@outputter.print_action_step(diff)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Downgraded modules are those that have an older version in new than old.
|
124
|
+
downgraded = new.select do |full_name, mod|
|
125
|
+
if old.include?(full_name)
|
126
|
+
mod.version < old[full_name].version
|
83
127
|
end
|
128
|
+
end.keys
|
129
|
+
|
130
|
+
if downgraded.any?
|
131
|
+
diff = "Downgrading the following modules: \n"
|
132
|
+
downgraded.each { |full_name| diff += "#{full_name} #{old[full_name].version} to #{new[full_name].version}\n" }
|
133
|
+
@outputter.print_action_step(diff)
|
84
134
|
end
|
85
135
|
|
86
|
-
#
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
136
|
+
# Removed modules are those present in old but not in new.
|
137
|
+
removed = old.reject { |full_name, _mod| new.include?(full_name) }.values
|
138
|
+
|
139
|
+
if removed.any?
|
140
|
+
diff = "Removing the following modules:\n"
|
141
|
+
removed.each { |mod| diff += "#{mod.full_name} #{mod.version}\n" }
|
142
|
+
@outputter.print_action_step(diff)
|
143
|
+
end
|
93
144
|
end
|
94
145
|
|
95
146
|
# Installs a project's module dependencies.
|
96
147
|
#
|
97
|
-
def install(
|
98
|
-
|
99
|
-
|
100
|
-
puppetfile = Bolt::Puppetfile.new(modules)
|
101
|
-
|
102
|
-
# If the Puppetfile exists, check if it includes specs for each declared
|
103
|
-
# module, erroring if there are any missing. Otherwise, resolve the
|
104
|
-
# module dependencies and write a new Puppetfile. Users can forcibly
|
105
|
-
# overwrite an existing Puppetfile with the '--force' option, or opt to
|
106
|
-
# install the Puppetfile as-is with --no-resolve.
|
107
|
-
#
|
108
|
-
# This is just if resolve is not false (nil should default to true)
|
148
|
+
def install(specs, path, moduledir, force: false, resolve: true)
|
149
|
+
@outputter.print_message("Installing project modules\n\n")
|
150
|
+
|
109
151
|
if resolve != false
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
152
|
+
specs = Specs.new(specs)
|
153
|
+
|
154
|
+
# If forcibly installing or if there is no Puppetfile, resolve
|
155
|
+
# and write a Puppetfile.
|
156
|
+
if force || !path.exist?
|
157
|
+
@outputter.print_action_step("Resolving module dependencies, this may take a moment")
|
158
|
+
puppetfile = Resolver.new.resolve(specs)
|
115
159
|
|
116
|
-
@outputter.print_message "Writing Puppetfile at #{path}"
|
117
160
|
# We get here either through 'bolt module install' which uses the
|
118
161
|
# managed modulepath (which isn't configurable) or through bolt
|
119
162
|
# project init --modules, which uses the default modulepath. This
|
120
163
|
# should be safe to assume that if `.modules/` is the moduledir the
|
121
164
|
# user is using the new workflow
|
122
|
-
|
165
|
+
@outputter.print_action_step("Writing Puppetfile at #{path}")
|
166
|
+
if moduledir.basename.to_s == '.modules'
|
123
167
|
puppetfile.write(path, moduledir)
|
124
168
|
else
|
125
169
|
puppetfile.write(path)
|
126
170
|
end
|
171
|
+
# If not forcibly installing and there is a Puppetfile, assert
|
172
|
+
# that it satisfies the specs.
|
173
|
+
else
|
174
|
+
puppetfile = Puppetfile.parse(path)
|
175
|
+
puppetfile.assert_satisfies(specs)
|
127
176
|
end
|
128
177
|
end
|
129
178
|
|
@@ -134,39 +183,16 @@ module Bolt
|
|
134
183
|
# Installs the Puppetfile and generates types.
|
135
184
|
#
|
136
185
|
def install_puppetfile(path, moduledir, config = {})
|
137
|
-
|
138
|
-
|
139
|
-
@outputter.print_message "Syncing modules from #{path} to #{moduledir}"
|
140
|
-
ok = Bolt::Puppetfile::Installer.new(config).install(path, moduledir)
|
186
|
+
@outputter.print_action_step("Syncing modules from #{path} to #{moduledir}")
|
187
|
+
ok = Installer.new(config).install(path, moduledir)
|
141
188
|
|
142
189
|
# Automatically generate types after installing modules
|
190
|
+
@outputter.print_action_step("Generating type references")
|
143
191
|
@pal.generate_types
|
144
192
|
|
145
193
|
@outputter.print_puppetfile_result(ok, path, moduledir)
|
146
194
|
|
147
195
|
ok
|
148
196
|
end
|
149
|
-
|
150
|
-
# Asserts that an existing Puppetfile is managed by Bolt.
|
151
|
-
#
|
152
|
-
private def assert_managed_puppetfile(puppetfile, path)
|
153
|
-
existing_puppetfile = Bolt::Puppetfile.parse(path)
|
154
|
-
|
155
|
-
unless existing_puppetfile.modules.superset? puppetfile.modules
|
156
|
-
missing_modules = puppetfile.modules - existing_puppetfile.modules
|
157
|
-
|
158
|
-
message = <<~MESSAGE.chomp
|
159
|
-
Puppetfile #{path} is missing specifications for the following
|
160
|
-
module declarations:
|
161
|
-
|
162
|
-
#{missing_modules.map(&:to_hash).to_yaml.lines.drop(1).join.chomp}
|
163
|
-
|
164
|
-
This may not be a Puppetfile managed by Bolt. To forcibly overwrite the
|
165
|
-
Puppetfile, run 'bolt module install --force'.
|
166
|
-
MESSAGE
|
167
|
-
|
168
|
-
raise Bolt::Error.new(message, 'bolt/missing-module-specs')
|
169
|
-
end
|
170
|
-
end
|
171
197
|
end
|
172
198
|
end
|