bolt 2.29.0 → 2.30.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 +4 -3
- data/lib/bolt/bolt_option_parser.rb +48 -9
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +131 -110
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/options.rb +2 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/module_installer.rb +172 -0
- data/lib/bolt/outputter.rb +4 -0
- data/lib/bolt/outputter/human.rb +53 -11
- data/lib/bolt/outputter/json.rb +7 -1
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +8 -10
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +31 -15
- data/lib/bolt/project_migrator.rb +80 -0
- data/lib/bolt/project_migrator/base.rb +39 -0
- data/lib/bolt/project_migrator/config.rb +67 -0
- data/lib/bolt/project_migrator/inventory.rb +67 -0
- data/lib/bolt/project_migrator/modules.rb +198 -0
- data/lib/bolt/puppetfile.rb +26 -44
- data/lib/bolt/puppetfile/module.rb +2 -1
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/util.rb +22 -0
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/acl.rb +2 -2
- data/lib/bolt_server/base_config.rb +3 -3
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- metadata +8 -3
- data/lib/bolt/project_migrate.rb +0 -138
data/lib/bolt/puppetfile.rb
CHANGED
@@ -19,7 +19,7 @@ module Bolt
|
|
19
19
|
# Loads a Puppetfile and parses its module specifications, returning a
|
20
20
|
# Bolt::Puppetfile object with the modules set.
|
21
21
|
#
|
22
|
-
def self.parse(path)
|
22
|
+
def self.parse(path, skip_unsupported_modules: false)
|
23
23
|
require 'puppetfile-resolver'
|
24
24
|
require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
|
25
25
|
|
@@ -33,12 +33,24 @@ module Bolt
|
|
33
33
|
end
|
34
34
|
|
35
35
|
unless parsed.valid?
|
36
|
-
|
37
|
-
|
36
|
+
# valid? Just checks if validation_errors is empty, so if we get here we know it's not.
|
37
|
+
raise Bolt::ValidationError, <<~MSG
|
38
|
+
Unable to parse Puppetfile #{path}:
|
39
|
+
#{parsed.validation_errors.join("\n\n")}.
|
40
|
+
This may not be a Puppetfile managed by Bolt.
|
41
|
+
MSG
|
38
42
|
end
|
39
43
|
|
40
|
-
modules = parsed.modules.
|
41
|
-
|
44
|
+
modules = parsed.modules.each_with_object([]) do |mod, acc|
|
45
|
+
unless mod.instance_of? PuppetfileResolver::Puppetfile::ForgeModule
|
46
|
+
next if skip_unsupported_modules
|
47
|
+
|
48
|
+
raise Bolt::ValidationError,
|
49
|
+
"Module '#{mod.title}' is not a Puppet Forge module. Unable to "\
|
50
|
+
"parse Puppetfile #{path}."
|
51
|
+
end
|
52
|
+
|
53
|
+
acc << Bolt::Puppetfile::Module.new(mod.owner, mod.name, mod.version)
|
42
54
|
end
|
43
55
|
|
44
56
|
new(modules)
|
@@ -47,19 +59,11 @@ module Bolt
|
|
47
59
|
# Writes a Puppetfile that includes specifications for each of the
|
48
60
|
# modules.
|
49
61
|
#
|
50
|
-
def write(path,
|
51
|
-
if File.exist?(path) && !force
|
52
|
-
raise Bolt::FileError.new(
|
53
|
-
"Cannot overwrite existing Puppetfile at #{path}. To forcibly overwrite, "\
|
54
|
-
"run with the '--force' option.",
|
55
|
-
path
|
56
|
-
)
|
57
|
-
end
|
58
|
-
|
62
|
+
def write(path, moduledir = nil)
|
59
63
|
File.open(path, 'w') do |file|
|
60
64
|
file.puts '# This Puppetfile is managed by Bolt. Do not edit.'
|
65
|
+
file.puts "moduledir '#{moduledir.basename}'" if moduledir
|
61
66
|
modules.each { |mod| file.puts mod.to_spec }
|
62
|
-
file.puts
|
63
67
|
end
|
64
68
|
rescue SystemCallError => e
|
65
69
|
raise Bolt::FileError.new(
|
@@ -104,46 +108,24 @@ module Bolt
|
|
104
108
|
cache: nil,
|
105
109
|
ui: nil,
|
106
110
|
module_paths: [],
|
107
|
-
allow_missing_modules:
|
111
|
+
allow_missing_modules: false
|
108
112
|
)
|
109
113
|
rescue StandardError => e
|
110
114
|
raise Bolt::Error.new(e.message, 'bolt/puppetfile-resolver-error')
|
111
115
|
end
|
112
116
|
|
113
|
-
#
|
114
|
-
|
115
|
-
|
117
|
+
# Turn specifications into module objects. This will skip over anything that is not
|
118
|
+
# a module specification (i.e. a Puppet version specification).
|
119
|
+
@modules = result.specifications.each_with_object(Set.new) do |(_name, spec), acc|
|
120
|
+
next unless spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
|
121
|
+
acc << Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
|
116
122
|
end
|
117
|
-
|
118
|
-
if missing_graph.any?
|
119
|
-
titles = model.modules.each_with_object({}) do |mod, acc|
|
120
|
-
acc[mod.name] = mod.title
|
121
|
-
end
|
122
|
-
|
123
|
-
names = titles.values_at(*missing_graph.keys)
|
124
|
-
plural = names.count == 1 ? '' : 's'
|
125
|
-
|
126
|
-
raise Bolt::Error.new(
|
127
|
-
"Unknown module name#{plural} #{names.join(', ')}",
|
128
|
-
'bolt/unknown-modules'
|
129
|
-
)
|
130
|
-
end
|
131
|
-
|
132
|
-
# Filter the dependency graph to only include module specifications. This
|
133
|
-
# will only remove the Puppet version specification, which is not needed.
|
134
|
-
specs = result.specifications.select do |_name, spec|
|
135
|
-
spec.instance_of? PuppetfileResolver::Models::ModuleSpecification
|
136
|
-
end
|
137
|
-
|
138
|
-
@modules = specs.map do |_name, spec|
|
139
|
-
Bolt::Puppetfile::Module.new(spec.owner, spec.name, spec.version.to_s)
|
140
|
-
end.to_set
|
141
123
|
end
|
142
124
|
|
143
125
|
# Adds to the set of modules.
|
144
126
|
#
|
145
127
|
def add_modules(modules)
|
146
|
-
modules.each do |mod|
|
128
|
+
Array(modules).each do |mod|
|
147
129
|
case mod
|
148
130
|
when Bolt::Puppetfile::Module
|
149
131
|
@modules << mod
|
@@ -13,7 +13,7 @@ module Bolt
|
|
13
13
|
def initialize(owner, name, version = nil)
|
14
14
|
@owner = owner
|
15
15
|
@name = name
|
16
|
-
@version = version
|
16
|
+
@version = version unless version == :latest
|
17
17
|
end
|
18
18
|
|
19
19
|
# Creates a new module from a hash.
|
@@ -38,6 +38,7 @@ module Bolt
|
|
38
38
|
def title
|
39
39
|
"#{@owner}-#{@name}"
|
40
40
|
end
|
41
|
+
alias to_s title
|
41
42
|
|
42
43
|
# Checks two modules for equality.
|
43
44
|
#
|
data/lib/bolt/shell/bash.rb
CHANGED
@@ -143,7 +143,7 @@ module Bolt
|
|
143
143
|
|
144
144
|
execute_options[:stdin] = stdin
|
145
145
|
execute_options[:sudoable] = true if run_as
|
146
|
-
output = execute(remote_task_path, execute_options)
|
146
|
+
output = execute(remote_task_path, **execute_options)
|
147
147
|
end
|
148
148
|
Bolt::Result.for_task(target, output.stdout.string,
|
149
149
|
output.stderr.string,
|
data/lib/bolt/util.rb
CHANGED
@@ -299,6 +299,28 @@ module Bolt
|
|
299
299
|
raise Bolt::ValidationError, "path must be a String, received #{path.class} #{path}" unless path.is_a?(String)
|
300
300
|
path.split(%r{[/\\]}).last
|
301
301
|
end
|
302
|
+
|
303
|
+
# Prompts yes or no, returning true for yes and false for no.
|
304
|
+
#
|
305
|
+
def prompt_yes_no(prompt, outputter)
|
306
|
+
choices = {
|
307
|
+
'y' => true,
|
308
|
+
'yes' => true,
|
309
|
+
'n' => false,
|
310
|
+
'no' => false
|
311
|
+
}
|
312
|
+
|
313
|
+
loop do
|
314
|
+
outputter.print_prompt("#{prompt} ([y]es/[n]o) ")
|
315
|
+
response = $stdin.gets.to_s.downcase.chomp
|
316
|
+
|
317
|
+
if choices.key?(response)
|
318
|
+
return choices[response]
|
319
|
+
else
|
320
|
+
outputter.print_prompt_error("Invalid response, must pick [y]es or [n]o")
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
302
324
|
end
|
303
325
|
end
|
304
326
|
end
|
data/lib/bolt/version.rb
CHANGED
data/lib/bolt_server/acl.rb
CHANGED
@@ -7,7 +7,7 @@ module BoltServer
|
|
7
7
|
class BaseConfig
|
8
8
|
def config_keys
|
9
9
|
%w[host port ssl-cert ssl-key ssl-ca-cert
|
10
|
-
ssl-cipher-suites loglevel logfile
|
10
|
+
ssl-cipher-suites loglevel logfile allowlist projects-dir]
|
11
11
|
end
|
12
12
|
|
13
13
|
def env_keys
|
@@ -98,8 +98,8 @@ module BoltServer
|
|
98
98
|
raise Bolt::ValidationError, "Configured 'ssl-cipher-suites' must be an array of cipher suite names"
|
99
99
|
end
|
100
100
|
|
101
|
-
unless @data['
|
102
|
-
raise Bolt::ValidationError, "Configured '
|
101
|
+
unless @data['allowlist'].nil? || @data['allowlist'].is_a?(Array)
|
102
|
+
raise Bolt::ValidationError, "Configured 'allowlist' must be an array of names"
|
103
103
|
end
|
104
104
|
end
|
105
105
|
|
@@ -40,7 +40,7 @@ module BoltSpec
|
|
40
40
|
|
41
41
|
def module_file_id(file)
|
42
42
|
modpath = @modulepath.select { |path| file =~ /^#{path}/ }
|
43
|
-
raise "Could not identify
|
43
|
+
raise "Could not identify modulepath containing #{file}: #{modpath}" unless modpath.size == 1
|
44
44
|
|
45
45
|
path = Pathname.new(file)
|
46
46
|
relative = path.relative_path_from(Pathname.new(modpath.first))
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bolt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.30.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Puppet
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -461,6 +461,7 @@ files:
|
|
461
461
|
- lib/bolt/inventory/target.rb
|
462
462
|
- lib/bolt/logger.rb
|
463
463
|
- lib/bolt/module.rb
|
464
|
+
- lib/bolt/module_installer.rb
|
464
465
|
- lib/bolt/node/errors.rb
|
465
466
|
- lib/bolt/node/output.rb
|
466
467
|
- lib/bolt/outputter.rb
|
@@ -494,7 +495,11 @@ files:
|
|
494
495
|
- lib/bolt/plugin/puppetdb.rb
|
495
496
|
- lib/bolt/plugin/task.rb
|
496
497
|
- lib/bolt/project.rb
|
497
|
-
- lib/bolt/
|
498
|
+
- lib/bolt/project_migrator.rb
|
499
|
+
- lib/bolt/project_migrator/base.rb
|
500
|
+
- lib/bolt/project_migrator/config.rb
|
501
|
+
- lib/bolt/project_migrator/inventory.rb
|
502
|
+
- lib/bolt/project_migrator/modules.rb
|
498
503
|
- lib/bolt/puppetdb.rb
|
499
504
|
- lib/bolt/puppetdb/client.rb
|
500
505
|
- lib/bolt/puppetdb/config.rb
|
data/lib/bolt/project_migrate.rb
DELETED
@@ -1,138 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Bolt
|
4
|
-
class ProjectMigrate
|
5
|
-
attr_reader :path, :project_file, :backup_dir, :outputter, :inventory_file, :config_file
|
6
|
-
|
7
|
-
# This init mostly makes testing easier
|
8
|
-
def initialize(path, outputter, configured_inventory = nil)
|
9
|
-
@path = Pathname.new(path).expand_path
|
10
|
-
@project_file = @path + 'bolt-project.yaml'
|
11
|
-
@config_file = @path + 'bolt.yaml'
|
12
|
-
@backup_dir = @path + '.bolt-bak'
|
13
|
-
@inventory_file = configured_inventory || @path + 'inventory.yaml'
|
14
|
-
@outputter = outputter
|
15
|
-
end
|
16
|
-
|
17
|
-
def migrate_project
|
18
|
-
inv_ok = inventory_1_to_2(inventory_file, outputter) if inventory_file.file?
|
19
|
-
config_ok = bolt_yaml_to_bolt_project(inventory_file, outputter)
|
20
|
-
inv_ok && config_ok ? 0 : 1
|
21
|
-
end
|
22
|
-
|
23
|
-
# This could be made public and used elsewhere if the need arises
|
24
|
-
private def backup_file(origin_path)
|
25
|
-
unless File.exist?(origin_path)
|
26
|
-
outputter.print_message "Could not find file #{origin_path}, skipping backup."
|
27
|
-
return
|
28
|
-
end
|
29
|
-
|
30
|
-
date = Time.new.strftime("%Y%m%d_%H%M%S%L")
|
31
|
-
FileUtils.mkdir_p(backup_dir)
|
32
|
-
|
33
|
-
filename = File.basename(origin_path)
|
34
|
-
backup_path = File.join(backup_dir, "#{filename}.#{date}.bak")
|
35
|
-
|
36
|
-
outputter.print_message "Backing up #{filename} from #{origin_path} to #{backup_path}"
|
37
|
-
|
38
|
-
begin
|
39
|
-
FileUtils.cp(origin_path, backup_path)
|
40
|
-
rescue StandardError => e
|
41
|
-
raise Bolt::FileError.new("#{e.message}; unable to create backup of #{filename}.", origin_path)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private def bolt_yaml_to_bolt_project(inventory_file, outputter)
|
46
|
-
# If bolt-project.yaml already exists
|
47
|
-
if project_file.file?
|
48
|
-
outputter.print_message "bolt-project.yaml already exists in Bolt "\
|
49
|
-
"project at #{path}. Skipping project file update."
|
50
|
-
|
51
|
-
# If bolt.yaml doesn't exist
|
52
|
-
elsif !config_file.file?
|
53
|
-
outputter.print_message "Could not find bolt.yaml in project at "\
|
54
|
-
"#{path}. Skipping project file update."
|
55
|
-
|
56
|
-
else
|
57
|
-
config_data = Bolt::Util.read_optional_yaml_hash(config_file, 'config')
|
58
|
-
transport_data, project_data = config_data.partition do |k, _|
|
59
|
-
Bolt::Config::INVENTORY_OPTIONS.keys.include?(k)
|
60
|
-
end.map(&:to_h)
|
61
|
-
|
62
|
-
if transport_data.any?
|
63
|
-
if File.exist?(inventory_file)
|
64
|
-
inventory_data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
|
65
|
-
merged = Bolt::Util.deep_merge(transport_data, inventory_data['config'] || {})
|
66
|
-
inventory_data['config'] = merged
|
67
|
-
backup_file(inventory_file)
|
68
|
-
else
|
69
|
-
FileUtils.touch(inventory_file)
|
70
|
-
inventory_data = { 'config' => transport_data }
|
71
|
-
end
|
72
|
-
|
73
|
-
backup_file(config_file)
|
74
|
-
|
75
|
-
begin
|
76
|
-
outputter.print_message "Moving transportation configuration options "\
|
77
|
-
"'#{transport_data.keys.join(', ')}' from bolt.yaml to inventory.yaml"
|
78
|
-
File.write(inventory_file, inventory_data.to_yaml)
|
79
|
-
File.write(config_file, project_data.to_yaml)
|
80
|
-
rescue StandardError => e
|
81
|
-
raise Bolt::FileError.new("#{e.message}; unable to write inventory.", inventory_file)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
outputter.print_message "Renaming bolt.yaml to bolt-project.yaml"
|
86
|
-
FileUtils.mv(config_file, project_file)
|
87
|
-
outputter.print_message "Successfully updated project. Please add a "\
|
88
|
-
"'name' key to bolt-project.yaml to use project-level tasks and plans. "\
|
89
|
-
"Learn more about projects by running 'bolt guide project'."
|
90
|
-
# If nothing errored, this succeeded
|
91
|
-
true
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
private def inventory_1_to_2(inventory_file, outputter)
|
96
|
-
data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
|
97
|
-
data.delete('version') if data['version'] != 2
|
98
|
-
migrated = migrate_group(data)
|
99
|
-
|
100
|
-
ok = if migrated
|
101
|
-
backup_file(inventory_file)
|
102
|
-
File.write(inventory_file, data.to_yaml)
|
103
|
-
end
|
104
|
-
|
105
|
-
result = if migrated && ok
|
106
|
-
"Successfully migrated Bolt inventory to the latest version."
|
107
|
-
elsif !migrated
|
108
|
-
"Bolt inventory is already on the latest version. Skipping inventory update."
|
109
|
-
else
|
110
|
-
"Could not migrate Bolt inventory to the latest version. See "\
|
111
|
-
"https://puppet.com/docs/bolt/latest/inventory_file_v2.html to manually update."
|
112
|
-
end
|
113
|
-
outputter.print_message(result)
|
114
|
-
ok
|
115
|
-
end
|
116
|
-
|
117
|
-
# Walks an inventory hash and replaces all 'nodes' keys with 'targets' keys
|
118
|
-
# and all 'name' keys nested in a 'targets' hash with 'uri' keys. Data is
|
119
|
-
# modified in place.
|
120
|
-
private def migrate_group(group)
|
121
|
-
migrated = false
|
122
|
-
if group.key?('nodes')
|
123
|
-
migrated = true
|
124
|
-
targets = group['nodes'].map do |target|
|
125
|
-
target['uri'] = target.delete('name') if target.is_a?(Hash)
|
126
|
-
target
|
127
|
-
end
|
128
|
-
group.delete('nodes')
|
129
|
-
group['targets'] = targets
|
130
|
-
end
|
131
|
-
(group['groups'] || []).each do |subgroup|
|
132
|
-
migrated_group = migrate_group(subgroup)
|
133
|
-
migrated ||= migrated_group
|
134
|
-
end
|
135
|
-
migrated
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|