bolt 2.27.0 → 2.32.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 +13 -12
- 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/module.txt +19 -0
- data/guides/modulepath.txt +25 -0
- data/lib/bolt/applicator.rb +14 -14
- data/lib/bolt/bolt_option_parser.rb +74 -22
- data/lib/bolt/catalog.rb +1 -1
- data/lib/bolt/cli.rb +178 -127
- data/lib/bolt/config.rb +13 -1
- data/lib/bolt/config/modulepath.rb +30 -0
- data/lib/bolt/config/options.rb +38 -9
- data/lib/bolt/config/transport/options.rb +1 -1
- data/lib/bolt/executor.rb +1 -1
- data/lib/bolt/inventory.rb +11 -10
- data/lib/bolt/logger.rb +26 -19
- data/lib/bolt/module_installer.rb +197 -0
- 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 +84 -0
- data/lib/bolt/module_installer/specs/git_spec.rb +178 -0
- data/lib/bolt/outputter.rb +2 -45
- data/lib/bolt/outputter/human.rb +78 -18
- data/lib/bolt/outputter/json.rb +22 -7
- data/lib/bolt/outputter/logger.rb +2 -2
- data/lib/bolt/pal.rb +29 -25
- data/lib/bolt/plugin.rb +1 -1
- data/lib/bolt/plugin/module.rb +1 -1
- data/lib/bolt/project.rb +32 -22
- 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 +200 -0
- data/lib/bolt/shell/bash.rb +4 -3
- data/lib/bolt/transport/base.rb +4 -4
- data/lib/bolt/transport/ssh/connection.rb +1 -1
- data/lib/bolt/util.rb +51 -10
- 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_server/file_cache.rb +11 -11
- data/lib/bolt_server/schemas/partials/task.json +17 -2
- data/lib/bolt_server/transport_app.rb +93 -13
- data/lib/bolt_spec/bolt_context.rb +8 -6
- data/lib/bolt_spec/plans.rb +1 -1
- data/lib/bolt_spec/plans/mock_executor.rb +1 -1
- data/lib/bolt_spec/run.rb +1 -1
- metadata +30 -11
- data/lib/bolt/project_migrate.rb +0 -138
- data/lib/bolt/puppetfile.rb +0 -160
- data/lib/bolt/puppetfile/module.rb +0 -66
- data/lib/bolt_server/pe/pal.rb +0 -67
@@ -13,9 +13,9 @@ module Bolt
|
|
13
13
|
def handle_event(event)
|
14
14
|
case event[:type]
|
15
15
|
when :step_start
|
16
|
-
log_step_start(event)
|
16
|
+
log_step_start(**event)
|
17
17
|
when :step_finish
|
18
|
-
log_step_finish(event)
|
18
|
+
log_step_finish(**event)
|
19
19
|
when :plan_start
|
20
20
|
log_plan_start(event)
|
21
21
|
when :plan_finish
|
data/lib/bolt/pal.rb
CHANGED
@@ -5,18 +5,16 @@ require 'bolt/executor'
|
|
5
5
|
require 'bolt/error'
|
6
6
|
require 'bolt/plan_result'
|
7
7
|
require 'bolt/util'
|
8
|
+
require 'bolt/config/modulepath'
|
8
9
|
require 'etc'
|
9
10
|
|
10
11
|
module Bolt
|
11
12
|
class PAL
|
12
|
-
BOLTLIB_PATH = File.expand_path('../../bolt-modules', __dir__)
|
13
|
-
MODULES_PATH = File.expand_path('../../modules', __dir__)
|
14
|
-
|
15
13
|
# PALError is used to convert errors from executing puppet code into
|
16
14
|
# Bolt::Errors
|
17
15
|
class PALError < Bolt::Error
|
18
16
|
def self.from_preformatted_error(err)
|
19
|
-
if err.cause
|
17
|
+
if err.cause.is_a? Bolt::Error
|
20
18
|
err.cause
|
21
19
|
else
|
22
20
|
from_error(err)
|
@@ -29,15 +27,12 @@ module Bolt
|
|
29
27
|
message = err.cause ? err.cause.message : err.message
|
30
28
|
|
31
29
|
# Provide the location of an error if it came from a plan
|
32
|
-
details =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
else
|
37
|
-
{}
|
38
|
-
end
|
30
|
+
details = {}
|
31
|
+
details[:file] = err.file if defined?(err.file)
|
32
|
+
details[:line] = err.line if defined?(err.line)
|
33
|
+
details[:column] = err.pos if defined?(err.pos)
|
39
34
|
|
40
|
-
e = new(message, details)
|
35
|
+
e = new(message, details.compact)
|
41
36
|
|
42
37
|
e.set_backtrace(err.backtrace)
|
43
38
|
e
|
@@ -48,16 +43,16 @@ module Bolt
|
|
48
43
|
end
|
49
44
|
end
|
50
45
|
|
51
|
-
attr_reader :modulepath, :user_modulepath
|
52
|
-
|
53
46
|
def initialize(modulepath, hiera_config, resource_types, max_compiles = Etc.nprocessors,
|
54
47
|
trusted_external = nil, apply_settings = {}, project = nil)
|
48
|
+
unless modulepath.is_a?(Bolt::Config::Modulepath)
|
49
|
+
msg = "Type error in PAL: modulepath must be a Bolt::Config::Modulepath"
|
50
|
+
raise Bolt::Error.new(msg, "bolt/execution-error")
|
51
|
+
end
|
55
52
|
# Nothing works without initialized this global state. Reinitializing
|
56
53
|
# is safe and in practice only happens in tests
|
57
54
|
self.class.load_puppet
|
58
|
-
|
59
|
-
@user_modulepath = modulepath
|
60
|
-
@modulepath = [BOLTLIB_PATH, *modulepath, MODULES_PATH]
|
55
|
+
@modulepath = modulepath
|
61
56
|
@hiera_config = hiera_config
|
62
57
|
@trusted_external = trusted_external
|
63
58
|
@apply_settings = apply_settings
|
@@ -66,13 +61,21 @@ module Bolt
|
|
66
61
|
@project = project
|
67
62
|
|
68
63
|
@logger = Bolt::Logger.logger(self)
|
69
|
-
|
70
|
-
@logger.debug("Loading modules from #{
|
64
|
+
unless user_modulepath.empty?
|
65
|
+
@logger.debug("Loading modules from #{full_modulepath.join(File::PATH_SEPARATOR)}")
|
71
66
|
end
|
72
67
|
|
73
68
|
@loaded = false
|
74
69
|
end
|
75
70
|
|
71
|
+
def full_modulepath
|
72
|
+
@modulepath.full_modulepath
|
73
|
+
end
|
74
|
+
|
75
|
+
def user_modulepath
|
76
|
+
@modulepath.user_modulepath
|
77
|
+
end
|
78
|
+
|
76
79
|
# Puppet logging is global so this is class method to avoid confusion
|
77
80
|
def self.configure_logging
|
78
81
|
Puppet::Util::Log.destinations.clear
|
@@ -160,7 +163,7 @@ module Bolt
|
|
160
163
|
def in_bolt_compiler
|
161
164
|
# TODO: If we always call this inside a bolt_executor we can remove this here
|
162
165
|
setup
|
163
|
-
r = Puppet::Pal.in_tmp_environment('bolt', modulepath:
|
166
|
+
r = Puppet::Pal.in_tmp_environment('bolt', modulepath: full_modulepath, facts: {}) do |pal|
|
164
167
|
# Only load the project if it a) exists, b) has a name it can be loaded with
|
165
168
|
Puppet.override(bolt_project: @project,
|
166
169
|
yaml_plan_instantiator: Bolt::PAL::YamlPlan::Loader) do
|
@@ -215,11 +218,11 @@ module Bolt
|
|
215
218
|
apply_executor: applicator || Applicator.new(
|
216
219
|
inventory,
|
217
220
|
executor,
|
218
|
-
|
221
|
+
full_modulepath,
|
219
222
|
# Skip syncing built-in plugins, since we vendor some Puppet 6
|
220
223
|
# versions of "core" types, which are already present on the agent,
|
221
224
|
# but may cause issues on Puppet 5 agents.
|
222
|
-
|
225
|
+
user_modulepath,
|
223
226
|
@project,
|
224
227
|
pdb_client,
|
225
228
|
@hiera_config,
|
@@ -441,13 +444,14 @@ module Bolt
|
|
441
444
|
# Returns a mapping of all modules available to the Bolt compiler
|
442
445
|
#
|
443
446
|
# @return [Hash{String => Array<Hash{Symbol => String,nil}>}]
|
444
|
-
# A hash that associates each directory on the
|
447
|
+
# A hash that associates each directory on the modulepath with an array
|
445
448
|
# containing a hash of information for each module in that directory.
|
446
449
|
# The information hash provides the name, version, and a string
|
447
450
|
# indicating whether the module belongs to an internal module group.
|
448
451
|
def list_modules
|
449
|
-
internal_module_groups = { BOLTLIB_PATH => 'Plan Language Modules',
|
450
|
-
MODULES_PATH => 'Packaged Modules'
|
452
|
+
internal_module_groups = { Bolt::Config::Modulepath::BOLTLIB_PATH => 'Plan Language Modules',
|
453
|
+
Bolt::Config::Modulepath::MODULES_PATH => 'Packaged Modules',
|
454
|
+
@project.managed_moduledir.to_s => 'Project Dependencies' }
|
451
455
|
|
452
456
|
in_bolt_compiler do
|
453
457
|
# NOTE: Can replace map+to_h with transform_values when Ruby 2.4
|
data/lib/bolt/plugin.rb
CHANGED
data/lib/bolt/plugin/module.rb
CHANGED
data/lib/bolt/project.rb
CHANGED
@@ -16,9 +16,10 @@ module Bolt
|
|
16
16
|
"These tasks are included in `bolt task show` output"
|
17
17
|
}.freeze
|
18
18
|
|
19
|
-
attr_reader :path, :data, :config_file, :inventory_file, :
|
19
|
+
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
20
20
|
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
21
|
-
:deprecations, :downloads, :plans_path
|
21
|
+
:deprecations, :downloads, :plans_path, :modulepath, :managed_moduledir,
|
22
|
+
:backup_dir
|
22
23
|
|
23
24
|
def self.default_project(logs = [])
|
24
25
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
|
@@ -94,15 +95,16 @@ module Bolt
|
|
94
95
|
@deprecations << { type: 'Using bolt.yaml for project configuration', msg: msg }
|
95
96
|
end
|
96
97
|
|
97
|
-
@inventory_file
|
98
|
-
@
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
102
|
-
@
|
103
|
-
@
|
104
|
-
@
|
105
|
-
@
|
98
|
+
@inventory_file = @path + 'inventory.yaml'
|
99
|
+
@hiera_config = @path + 'hiera.yaml'
|
100
|
+
@puppetfile = @path + 'Puppetfile'
|
101
|
+
@rerunfile = @path + '.rerun.json'
|
102
|
+
@resource_types = @path + '.resource_types'
|
103
|
+
@type = type
|
104
|
+
@downloads = @path + 'downloads'
|
105
|
+
@plans_path = @path + 'plans'
|
106
|
+
@managed_moduledir = @path + '.modules'
|
107
|
+
@backup_dir = @path + '.bolt-bak'
|
106
108
|
|
107
109
|
tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
|
108
110
|
if tc.any?
|
@@ -112,6 +114,14 @@ module Bolt
|
|
112
114
|
|
113
115
|
@data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }
|
114
116
|
|
117
|
+
# If the 'modules' key is present in the project configuration file,
|
118
|
+
# use the new, shorter modulepath.
|
119
|
+
@modulepath = if @data.key?('modules')
|
120
|
+
[(@path + 'modules').to_s]
|
121
|
+
else
|
122
|
+
[(@path + 'modules').to_s, (@path + 'site-modules').to_s, (@path + 'site').to_s]
|
123
|
+
end
|
124
|
+
|
115
125
|
# Once bolt.yaml deprecation is removed, this attribute should be removed
|
116
126
|
# and replaced with .project_file in lib/bolt/config.rb
|
117
127
|
@config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
|
@@ -164,7 +174,13 @@ module Bolt
|
|
164
174
|
end
|
165
175
|
|
166
176
|
def modules
|
167
|
-
@data['modules']
|
177
|
+
@modules ||= @data['modules']&.map do |mod|
|
178
|
+
if mod.is_a?(String)
|
179
|
+
{ 'name' => mod }
|
180
|
+
else
|
181
|
+
mod
|
182
|
+
end
|
183
|
+
end
|
168
184
|
end
|
169
185
|
|
170
186
|
def validate
|
@@ -174,7 +190,7 @@ module Bolt
|
|
174
190
|
Invalid project name '#{name}' in bolt-project.yaml; project name must begin with a lowercase letter
|
175
191
|
and can include lowercase letters, numbers, and underscores.
|
176
192
|
ERROR_STRING
|
177
|
-
elsif Dir.children(Bolt::
|
193
|
+
elsif Dir.children(Bolt::Config::Modulepath::BOLTLIB_PATH).include?(name)
|
178
194
|
raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
|
179
195
|
"with a built-in Bolt module of the same name."
|
180
196
|
end
|
@@ -194,15 +210,9 @@ module Bolt
|
|
194
210
|
raise Bolt::ValidationError, "'modules' in bolt-project.yaml must be an array"
|
195
211
|
end
|
196
212
|
|
197
|
-
@data['modules'].each do |
|
198
|
-
next if
|
199
|
-
raise Bolt::ValidationError, "Module
|
200
|
-
end
|
201
|
-
|
202
|
-
unknown_keys = data['modules'].flat_map(&:keys).uniq - ['name']
|
203
|
-
if unknown_keys.any?
|
204
|
-
@logs << { warn: "Module declarations in bolt-project.yaml only support a name key. Ignoring "\
|
205
|
-
"unsupported keys: #{unknown_keys.join(', ')}." }
|
213
|
+
@data['modules'].each do |spec|
|
214
|
+
next if spec.is_a?(Hash) || spec.is_a?(String)
|
215
|
+
raise Bolt::ValidationError, "Module specification #{spec.inspect} must be a hash or string"
|
206
216
|
end
|
207
217
|
end
|
208
218
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/project_migrator/config'
|
4
|
+
require 'bolt/project_migrator/inventory'
|
5
|
+
require 'bolt/project_migrator/modules'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class ProjectMigrator
|
9
|
+
def initialize(config, outputter)
|
10
|
+
@config = config
|
11
|
+
@outputter = outputter
|
12
|
+
end
|
13
|
+
|
14
|
+
def migrate
|
15
|
+
unless $stdin.tty?
|
16
|
+
raise Bolt::Error.new(
|
17
|
+
"stdin is not a tty, unable to migrate project",
|
18
|
+
'bolt/stdin-not-a-tty-error'
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
@outputter.print_message("Migrating project #{@config.project.path}\n\n")
|
23
|
+
|
24
|
+
@outputter.print_action_step(
|
25
|
+
"Migrating a Bolt project may make irreversible changes to the project's "\
|
26
|
+
"configuration and inventory files. Before continuing, make sure the "\
|
27
|
+
"project has a backup or uses a version control system."
|
28
|
+
)
|
29
|
+
|
30
|
+
return 0 unless Bolt::Util.prompt_yes_no("Continue with project migration?", @outputter)
|
31
|
+
|
32
|
+
@outputter.print_message('')
|
33
|
+
|
34
|
+
ok = migrate_inventory && migrate_config && migrate_modules
|
35
|
+
|
36
|
+
if ok
|
37
|
+
@outputter.print_message("Project successfully migrated")
|
38
|
+
else
|
39
|
+
@outputter.print_error("Project could not be migrated completely")
|
40
|
+
end
|
41
|
+
|
42
|
+
ok ? 0 : 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Migrates the project-level configuration file to the latest version.
|
46
|
+
#
|
47
|
+
private def migrate_config
|
48
|
+
migrator = Bolt::ProjectMigrator::Config.new(@outputter)
|
49
|
+
|
50
|
+
migrator.migrate(
|
51
|
+
@config.project.config_file,
|
52
|
+
@config.project.project_file,
|
53
|
+
@config.inventoryfile || @config.project.inventory_file,
|
54
|
+
@config.project.backup_dir
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Migrates the inventory file to the latest version.
|
59
|
+
#
|
60
|
+
private def migrate_inventory
|
61
|
+
migrator = Bolt::ProjectMigrator::Inventory.new(@outputter)
|
62
|
+
|
63
|
+
migrator.migrate(
|
64
|
+
@config.inventoryfile || @config.project.inventory_file,
|
65
|
+
@config.project.backup_dir
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Migrates the project's modules to use current best practices.
|
70
|
+
#
|
71
|
+
private def migrate_modules
|
72
|
+
migrator = Bolt::ProjectMigrator::Modules.new(@outputter)
|
73
|
+
|
74
|
+
migrator.migrate(
|
75
|
+
@config.project,
|
76
|
+
@config.modulepath
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'bolt/error'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class ProjectMigrator
|
8
|
+
class Base
|
9
|
+
def initialize(outputter)
|
10
|
+
@outputter = outputter
|
11
|
+
end
|
12
|
+
|
13
|
+
protected def backup_file(origin_path, backup_dir)
|
14
|
+
unless File.exist?(origin_path)
|
15
|
+
@outputter.print_action_step(
|
16
|
+
"Could not find file #{origin_path}, skipping backup."
|
17
|
+
)
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
date = Time.new.strftime("%Y%m%d_%H%M%S%L")
|
22
|
+
FileUtils.mkdir_p(backup_dir)
|
23
|
+
|
24
|
+
filename = File.basename(origin_path)
|
25
|
+
backup_path = File.join(backup_dir, "#{filename}.#{date}.bak")
|
26
|
+
|
27
|
+
@outputter.print_action_step(
|
28
|
+
"Backing up #{filename} from #{origin_path} to #{backup_path}"
|
29
|
+
)
|
30
|
+
|
31
|
+
begin
|
32
|
+
FileUtils.cp(origin_path, backup_path)
|
33
|
+
rescue StandardError => e
|
34
|
+
raise Bolt::FileError.new("#{e.message}; unable to create backup of #{filename}.", origin_path)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/project_migrator/base'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class ProjectMigrator
|
7
|
+
class Config < Base
|
8
|
+
def migrate(config_file, project_file, inventory_file, backup_dir)
|
9
|
+
bolt_yaml_to_bolt_project(config_file, project_file, inventory_file, backup_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
private def bolt_yaml_to_bolt_project(config_file, project_file, inventory_file, backup_dir)
|
13
|
+
if File.exist?(project_file)
|
14
|
+
return true
|
15
|
+
end
|
16
|
+
|
17
|
+
unless File.exist?(config_file)
|
18
|
+
return true
|
19
|
+
end
|
20
|
+
|
21
|
+
@outputter.print_message "Migrating project configuration\n\n"
|
22
|
+
|
23
|
+
config_data = Bolt::Util.read_optional_yaml_hash(config_file, 'config')
|
24
|
+
transport_data, project_data = config_data.partition do |k, _|
|
25
|
+
Bolt::Config::INVENTORY_OPTIONS.keys.include?(k)
|
26
|
+
end.map(&:to_h)
|
27
|
+
|
28
|
+
if transport_data.any?
|
29
|
+
if File.exist?(inventory_file)
|
30
|
+
inventory_data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
|
31
|
+
merged = Bolt::Util.deep_merge(transport_data, inventory_data['config'] || {})
|
32
|
+
inventory_data['config'] = merged
|
33
|
+
backup_file(inventory_file, backup_dir)
|
34
|
+
else
|
35
|
+
FileUtils.touch(inventory_file)
|
36
|
+
inventory_data = { 'config' => transport_data }
|
37
|
+
end
|
38
|
+
|
39
|
+
backup_file(config_file, backup_dir)
|
40
|
+
|
41
|
+
begin
|
42
|
+
@outputter.print_action_step(
|
43
|
+
"Moving transportation configuration options '#{transport_data.keys.join(', ')}' "\
|
44
|
+
"from bolt.yaml to inventory.yaml"
|
45
|
+
)
|
46
|
+
|
47
|
+
File.write(inventory_file, inventory_data.to_yaml)
|
48
|
+
File.write(config_file, project_data.to_yaml)
|
49
|
+
rescue StandardError => e
|
50
|
+
raise Bolt::FileError.new("#{e.message}; unable to write inventory.", inventory_file)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
@outputter.print_action_step("Renaming bolt.yaml to bolt-project.yaml")
|
55
|
+
FileUtils.mv(config_file, project_file)
|
56
|
+
|
57
|
+
@outputter.print_action_step(
|
58
|
+
"Successfully migrated config. Please add a 'name' key to bolt-project.yaml "\
|
59
|
+
"to use project-level tasks and plans. Learn more about projects by running "\
|
60
|
+
"'bolt guide project'."
|
61
|
+
)
|
62
|
+
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/project_migrator/base'
|
4
|
+
|
5
|
+
module Bolt
|
6
|
+
class ProjectMigrator
|
7
|
+
class Inventory < Base
|
8
|
+
def migrate(inventory_file, backup_dir)
|
9
|
+
inventory_1_to_2(inventory_file, backup_dir)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Migrates an inventory v1 file to inventory v2.
|
13
|
+
#
|
14
|
+
private def inventory_1_to_2(inventory_file, backup_dir)
|
15
|
+
unless File.exist?(inventory_file)
|
16
|
+
return true
|
17
|
+
end
|
18
|
+
|
19
|
+
data = Bolt::Util.read_yaml_hash(inventory_file, 'inventory')
|
20
|
+
data.delete('version') if data['version'] != 2
|
21
|
+
migrated = migrate_group(data)
|
22
|
+
|
23
|
+
return true unless migrated
|
24
|
+
|
25
|
+
@outputter.print_message "Migrating inventory\n\n"
|
26
|
+
|
27
|
+
backup_file(inventory_file, backup_dir)
|
28
|
+
|
29
|
+
begin
|
30
|
+
File.write(inventory_file, data.to_yaml)
|
31
|
+
@outputter.print_action_step(
|
32
|
+
"Successfully migrated Bolt inventory to the latest version."
|
33
|
+
)
|
34
|
+
true
|
35
|
+
rescue StandardError => e
|
36
|
+
raise Bolt::FileError.new(
|
37
|
+
"Unable to write to #{inventory_file}: #{e.message}. See "\
|
38
|
+
"http://pup.pt/bolt-inventory to manually update.",
|
39
|
+
inventory_file
|
40
|
+
)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Walks an inventory hash and replaces all 'nodes' keys with 'targets'
|
45
|
+
# keys and all 'name' keys nested in a 'targets' hash with 'uri' keys.
|
46
|
+
# Data is modified in place.
|
47
|
+
#
|
48
|
+
private def migrate_group(group)
|
49
|
+
migrated = false
|
50
|
+
if group.key?('nodes')
|
51
|
+
migrated = true
|
52
|
+
targets = group['nodes'].map do |target|
|
53
|
+
target['uri'] = target.delete('name') if target.is_a?(Hash)
|
54
|
+
target
|
55
|
+
end
|
56
|
+
group.delete('nodes')
|
57
|
+
group['targets'] = targets
|
58
|
+
end
|
59
|
+
(group['groups'] || []).each do |subgroup|
|
60
|
+
migrated_group = migrate_group(subgroup)
|
61
|
+
migrated ||= migrated_group
|
62
|
+
end
|
63
|
+
migrated
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|