bolt 2.35.0 → 2.36.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bolt might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bolt-modules/boltlib/lib/puppet/datatypes/applyresult.rb +1 -0
- data/lib/bolt/analytics.rb +27 -8
- data/lib/bolt/apply_result.rb +3 -3
- data/lib/bolt/bolt_option_parser.rb +38 -15
- data/lib/bolt/cli.rb +13 -87
- data/lib/bolt/config.rb +131 -52
- data/lib/bolt/config/options.rb +42 -4
- data/lib/bolt/config/transport/base.rb +10 -19
- data/lib/bolt/config/transport/local.rb +0 -7
- data/lib/bolt/config/transport/ssh.rb +8 -14
- data/lib/bolt/config/validator.rb +231 -0
- data/lib/bolt/executor.rb +5 -17
- data/lib/bolt/outputter/rainbow.rb +1 -1
- data/lib/bolt/plugin.rb +0 -7
- data/lib/bolt/project.rb +30 -36
- data/lib/bolt/project_manager.rb +199 -0
- data/lib/bolt/{project_migrator/config.rb → project_manager/config_migrator.rb} +41 -4
- data/lib/bolt/{project_migrator/inventory.rb → project_manager/inventory_migrator.rb} +3 -3
- data/lib/bolt/{project_migrator/base.rb → project_manager/migrator.rb} +2 -2
- data/lib/bolt/{project_migrator/modules.rb → project_manager/module_migrator.rb} +3 -3
- data/lib/bolt/puppetdb/config.rb +1 -2
- data/lib/bolt/shell/bash.rb +1 -1
- data/lib/bolt/task/run.rb +1 -1
- data/lib/bolt/transport/ssh/exec_connection.rb +6 -2
- data/lib/bolt/util.rb +14 -7
- data/lib/bolt/version.rb +1 -1
- data/lib/bolt_server/base_config.rb +3 -1
- data/lib/bolt_server/config.rb +3 -1
- data/lib/bolt_server/schemas/partials/task.json +2 -2
- data/lib/bolt_server/transport_app.rb +5 -5
- data/libexec/apply_catalog.rb +1 -1
- data/libexec/custom_facts.rb +1 -1
- data/libexec/query_resources.rb +1 -1
- metadata +8 -13
- data/lib/bolt/project_migrator.rb +0 -80
data/lib/bolt/executor.rb
CHANGED
@@ -256,10 +256,10 @@ module Bolt
|
|
256
256
|
@logger.trace { "Failed to submit analytics event: #{e.message}" }
|
257
257
|
end
|
258
258
|
|
259
|
-
def with_node_logging(description, batch)
|
260
|
-
@logger.
|
259
|
+
def with_node_logging(description, batch, log_level = :info)
|
260
|
+
@logger.send(log_level, "#{description} on #{batch.map(&:safe_name)}")
|
261
261
|
result = yield
|
262
|
-
@logger.
|
262
|
+
@logger.send(log_level, result.to_json)
|
263
263
|
result
|
264
264
|
end
|
265
265
|
|
@@ -289,32 +289,20 @@ module Bolt
|
|
289
289
|
end
|
290
290
|
end
|
291
291
|
|
292
|
-
def run_task(targets, task, arguments, options = {}, position = [])
|
292
|
+
def run_task(targets, task, arguments, options = {}, position = [], log_level = :info)
|
293
293
|
description = options.fetch(:description, "task #{task.name}")
|
294
294
|
log_action(description, targets) do
|
295
295
|
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
296
296
|
arguments['_task'] = task.name
|
297
297
|
|
298
298
|
batch_execute(targets) do |transport, batch|
|
299
|
-
with_node_logging("Running task #{task.name} with '#{arguments.to_json}'", batch) do
|
299
|
+
with_node_logging("Running task #{task.name} with '#{arguments.to_json}'", batch, log_level) do
|
300
300
|
transport.batch_task(batch, task, arguments, options, position, &method(:publish_event))
|
301
301
|
end
|
302
302
|
end
|
303
303
|
end
|
304
304
|
end
|
305
305
|
|
306
|
-
def run_task_with_minimal_logging(targets, task, arguments, options = {})
|
307
|
-
description = options.fetch(:description, "task #{task.name}")
|
308
|
-
log_action(description, targets) do
|
309
|
-
options[:run_as] = run_as if run_as && !options.key?(:run_as)
|
310
|
-
arguments['_task'] = task.name
|
311
|
-
|
312
|
-
batch_execute(targets) do |transport, batch|
|
313
|
-
transport.batch_task(batch, task, arguments, options, [], &method(:publish_event))
|
314
|
-
end
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
306
|
def run_task_with(target_mapping, task, options = {}, position = [])
|
319
307
|
targets = target_mapping.keys
|
320
308
|
description = options.fetch(:description, "task #{task.name}")
|
data/lib/bolt/plugin.rb
CHANGED
@@ -122,13 +122,6 @@ module Bolt
|
|
122
122
|
def self.setup(config, pal, analytics = Bolt::Analytics::NoopClient.new)
|
123
123
|
plugins = new(config, pal, analytics)
|
124
124
|
|
125
|
-
# Initialize any plugins referenced in plugin config. This will also indirectly
|
126
|
-
# initialize any plugins they depend on.
|
127
|
-
if plugins.reference?(config.plugins)
|
128
|
-
msg = "The 'plugins' setting cannot be set by a plugin reference"
|
129
|
-
raise PluginError.new(msg, 'bolt/plugin-error')
|
130
|
-
end
|
131
|
-
|
132
125
|
config.plugins.each_key do |plugin|
|
133
126
|
plugins.by_name(plugin)
|
134
127
|
end
|
data/lib/bolt/project.rb
CHANGED
@@ -2,19 +2,14 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'bolt/config'
|
5
|
+
require 'bolt/config/validator'
|
5
6
|
require 'bolt/pal'
|
6
7
|
require 'bolt/module'
|
7
8
|
|
8
9
|
module Bolt
|
9
10
|
class Project
|
10
11
|
BOLTDIR_NAME = 'Boltdir'
|
11
|
-
|
12
|
-
"name" => "The name of the project",
|
13
|
-
"plans" => "An array of plan names to show, if they exist in the project."\
|
14
|
-
"These plans are included in `bolt plan show` and `Get-BoltPlan` output",
|
15
|
-
"tasks" => "An array of task names to show, if they exist in the project."\
|
16
|
-
"These tasks are included in `bolt task show` and `Get-BoltTask` output"
|
17
|
-
}.freeze
|
12
|
+
CONFIG_NAME = 'bolt-project.yaml'
|
18
13
|
|
19
14
|
attr_reader :path, :data, :config_file, :inventory_file, :hiera_config,
|
20
15
|
:puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
|
@@ -37,7 +32,7 @@ module Bolt
|
|
37
32
|
|
38
33
|
if (dir + BOLTDIR_NAME).directory?
|
39
34
|
create_project(dir + BOLTDIR_NAME, 'embedded', logs)
|
40
|
-
elsif (dir + 'bolt.yaml').file? || (dir +
|
35
|
+
elsif (dir + 'bolt.yaml').file? || (dir + CONFIG_NAME).file?
|
41
36
|
create_project(dir, 'local', logs)
|
42
37
|
elsif dir.root?
|
43
38
|
default_project(logs)
|
@@ -73,21 +68,37 @@ module Bolt
|
|
73
68
|
)
|
74
69
|
end
|
75
70
|
|
76
|
-
project_file = File.join(fullpath,
|
77
|
-
data
|
78
|
-
default
|
79
|
-
exist
|
71
|
+
project_file = File.join(fullpath, CONFIG_NAME)
|
72
|
+
data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
|
73
|
+
default = type =~ /user|system/ ? 'default ' : ''
|
74
|
+
exist = File.exist?(File.expand_path(project_file))
|
75
|
+
deprecations = []
|
76
|
+
|
80
77
|
logs << { info: "Loaded #{default}project from '#{fullpath}'" } if exist
|
81
|
-
new(data, path, type, logs)
|
82
|
-
end
|
83
78
|
|
84
|
-
|
85
|
-
|
79
|
+
# Validate the config against the schema. This will raise a single error
|
80
|
+
# with all validation errors.
|
81
|
+
schema = Bolt::Config::OPTIONS.slice(*Bolt::Config::BOLT_PROJECT_OPTIONS)
|
82
|
+
|
83
|
+
Bolt::Config::Validator.new.tap do |validator|
|
84
|
+
validator.validate(data, schema, project_file)
|
85
|
+
|
86
|
+
validator.warnings.each { |warning| logs << { warn: warning } }
|
87
|
+
|
88
|
+
validator.deprecations.each do |dep|
|
89
|
+
deprecations << { type: "#{CONFIG_NAME} #{dep[:option]}", msg: dep[:message] }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
new(data, path, type, logs, deprecations)
|
94
|
+
end
|
86
95
|
|
87
|
-
|
96
|
+
def initialize(raw_data, path, type = 'option', logs = [], deprecations = [])
|
97
|
+
@path = Pathname.new(path).expand_path
|
98
|
+
@project_file = @path + CONFIG_NAME
|
99
|
+
@logs = logs
|
100
|
+
@deprecations = deprecations
|
88
101
|
|
89
|
-
@logs = logs
|
90
|
-
@deprecations = []
|
91
102
|
if (@path + 'bolt.yaml').file? && project_file?
|
92
103
|
msg = "Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
|
93
104
|
"Transport config should be set in inventory.yaml, all other config should be set in "\
|
@@ -198,23 +209,6 @@ module Bolt
|
|
198
209
|
message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
|
199
210
|
@logs << { warn: message }
|
200
211
|
end
|
201
|
-
|
202
|
-
%w[tasks plans].each do |conf|
|
203
|
-
unless @data.fetch(conf, []).is_a?(Array)
|
204
|
-
raise Bolt::ValidationError, "'#{conf}' in bolt-project.yaml must be an array"
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
if @data['modules']
|
209
|
-
unless @data['modules'].is_a?(Array)
|
210
|
-
raise Bolt::ValidationError, "'modules' in bolt-project.yaml must be an array"
|
211
|
-
end
|
212
|
-
|
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"
|
216
|
-
end
|
217
|
-
end
|
218
212
|
end
|
219
213
|
|
220
214
|
def check_deprecated_file
|
@@ -0,0 +1,199 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/project_manager/config_migrator'
|
4
|
+
require 'bolt/project_manager/inventory_migrator'
|
5
|
+
require 'bolt/project_manager/module_migrator'
|
6
|
+
|
7
|
+
module Bolt
|
8
|
+
class ProjectManager
|
9
|
+
INVENTORY_TEMPLATE = <<~INVENTORY
|
10
|
+
# This is an example inventory.yaml
|
11
|
+
# To read more about inventory files, see https://pup.pt/bolt-inventory
|
12
|
+
#
|
13
|
+
# groups:
|
14
|
+
# - name: linux
|
15
|
+
# targets:
|
16
|
+
# - target1.example.com
|
17
|
+
# - target2.example.com
|
18
|
+
# config:
|
19
|
+
# transport: ssh
|
20
|
+
# ssh:
|
21
|
+
# private-key: /path/to/private_key.pem
|
22
|
+
# - name: windows
|
23
|
+
# targets:
|
24
|
+
# - name: win1
|
25
|
+
# uri: target3.example.com
|
26
|
+
# - name: win2
|
27
|
+
# uri: target4.example.com
|
28
|
+
# config:
|
29
|
+
# transport: winrm
|
30
|
+
# config:
|
31
|
+
# ssh:
|
32
|
+
# host-key-check: false
|
33
|
+
# winrm:
|
34
|
+
# user: Administrator
|
35
|
+
# password: Bolt!
|
36
|
+
# ssl: false
|
37
|
+
INVENTORY
|
38
|
+
|
39
|
+
def initialize(config, outputter, pal)
|
40
|
+
@config = config
|
41
|
+
@outputter = outputter
|
42
|
+
@pal = pal
|
43
|
+
end
|
44
|
+
|
45
|
+
# Creates a new project at the specified directory.
|
46
|
+
#
|
47
|
+
def create(path, name, modules)
|
48
|
+
require 'bolt/module_installer'
|
49
|
+
|
50
|
+
project = Pathname.new(File.expand_path(path))
|
51
|
+
old_config = project + 'bolt.yaml'
|
52
|
+
config = project + 'bolt-project.yaml'
|
53
|
+
puppetfile = project + 'Puppetfile'
|
54
|
+
moduledir = project + '.modules'
|
55
|
+
inventoryfile = project + 'inventory.yaml'
|
56
|
+
project_name = name || File.basename(project)
|
57
|
+
|
58
|
+
if config.exist?
|
59
|
+
if modules
|
60
|
+
command = Bolt::Util.powershell? ? 'Add-BoltModule -Module' : 'bolt module add'
|
61
|
+
raise Bolt::Error.new(
|
62
|
+
"Found existing project directory with #{config.basename} at #{project}, "\
|
63
|
+
"unable to initialize project with modules. To add modules to the project, "\
|
64
|
+
"run '#{command} <module>' instead.",
|
65
|
+
'bolt/existing-project-error'
|
66
|
+
)
|
67
|
+
else
|
68
|
+
raise Bolt::Error.new(
|
69
|
+
"Found existing project directory with #{config.basename} at #{project}, "\
|
70
|
+
"unable to initialize project.",
|
71
|
+
'bolt/existing-project-error'
|
72
|
+
)
|
73
|
+
end
|
74
|
+
elsif old_config.exist?
|
75
|
+
command = Bolt::Util.powershell? ? 'Update-BoltProject' : 'bolt project migrate'
|
76
|
+
raise Bolt::Error.new(
|
77
|
+
"Found existing project directory with #{old_config.basename} at #{project}, "\
|
78
|
+
"unable to initialize project. #{old_config.basename} is deprecated. To "\
|
79
|
+
"update the project to current best practices, run '#{command}'.",
|
80
|
+
'bolt/existing-project-error'
|
81
|
+
)
|
82
|
+
elsif modules && puppetfile.exist?
|
83
|
+
raise Bolt::Error.new(
|
84
|
+
"Found existing Puppetfile at #{puppetfile}, unable to initialize project "\
|
85
|
+
"with modules.",
|
86
|
+
'bolt/existing-puppetfile-error'
|
87
|
+
)
|
88
|
+
elsif project_name !~ Bolt::Module::MODULE_NAME_REGEX
|
89
|
+
if name
|
90
|
+
raise Bolt::ValidationError,
|
91
|
+
"The provided project name '#{project_name}' is invalid; project name must "\
|
92
|
+
"begin with a lowercase letter and can include lowercase letters, "\
|
93
|
+
"numbers, and underscores."
|
94
|
+
else
|
95
|
+
command = Bolt::Util.powershell? ? 'New-BoltProject -Name' : 'bolt project init'
|
96
|
+
raise Bolt::ValidationError,
|
97
|
+
"The current directory name '#{project_name}' is an invalid project name. "\
|
98
|
+
"Please specify a name using '#{command} <name>'."
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# If modules were specified, resolve and install first. We want to error
|
103
|
+
# early here and not initialize the project if the modules cannot be
|
104
|
+
# resolved and installed.
|
105
|
+
if modules
|
106
|
+
Bolt::ModuleInstaller.new(@outputter, @pal).install(modules, puppetfile, moduledir)
|
107
|
+
end
|
108
|
+
|
109
|
+
data = { 'name' => project_name }
|
110
|
+
data['modules'] = modules || []
|
111
|
+
|
112
|
+
begin
|
113
|
+
File.write(config.to_path, data.to_yaml)
|
114
|
+
rescue StandardError => e
|
115
|
+
raise Bolt::FileError.new("Could not create bolt-project.yaml at #{project}: #{e.message}", nil)
|
116
|
+
end
|
117
|
+
|
118
|
+
unless inventoryfile.exist?
|
119
|
+
begin
|
120
|
+
File.write(inventoryfile.to_path, INVENTORY_TEMPLATE)
|
121
|
+
rescue StandardError => e
|
122
|
+
raise Bolt::FileError.new("Could not create inventory.yaml at #{project}: #{e.message}", nil)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
@outputter.print_message("Successfully created Bolt project at #{project}")
|
127
|
+
|
128
|
+
0
|
129
|
+
end
|
130
|
+
|
131
|
+
# Migrates a project to use the latest file versions and best practices.
|
132
|
+
#
|
133
|
+
def migrate
|
134
|
+
unless $stdin.tty?
|
135
|
+
raise Bolt::Error.new(
|
136
|
+
"stdin is not a tty, unable to migrate project",
|
137
|
+
'bolt/stdin-not-a-tty-error'
|
138
|
+
)
|
139
|
+
end
|
140
|
+
|
141
|
+
@outputter.print_message("Migrating project #{@config.project.path}\n\n")
|
142
|
+
|
143
|
+
@outputter.print_action_step(
|
144
|
+
"Migrating a Bolt project may make irreversible changes to the project's "\
|
145
|
+
"configuration and inventory files. Before continuing, make sure the "\
|
146
|
+
"project has a backup or uses a version control system."
|
147
|
+
)
|
148
|
+
|
149
|
+
return 0 unless Bolt::Util.prompt_yes_no("Continue with project migration?", @outputter)
|
150
|
+
|
151
|
+
@outputter.print_message('')
|
152
|
+
|
153
|
+
ok = migrate_inventory && migrate_config && migrate_modules
|
154
|
+
|
155
|
+
if ok
|
156
|
+
@outputter.print_message("Project successfully migrated")
|
157
|
+
else
|
158
|
+
@outputter.print_error("Project could not be migrated completely")
|
159
|
+
end
|
160
|
+
|
161
|
+
ok ? 0 : 1
|
162
|
+
end
|
163
|
+
|
164
|
+
# Migrates the project-level configuration file to the latest version.
|
165
|
+
#
|
166
|
+
private def migrate_config
|
167
|
+
migrator = ConfigMigrator.new(@outputter)
|
168
|
+
|
169
|
+
migrator.migrate(
|
170
|
+
@config.project.config_file,
|
171
|
+
@config.project.project_file,
|
172
|
+
@config.inventoryfile || @config.project.inventory_file,
|
173
|
+
@config.project.backup_dir
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Migrates the inventory file to the latest version.
|
178
|
+
#
|
179
|
+
private def migrate_inventory
|
180
|
+
migrator = InventoryMigrator.new(@outputter)
|
181
|
+
|
182
|
+
migrator.migrate(
|
183
|
+
@config.inventoryfile || @config.project.inventory_file,
|
184
|
+
@config.project.backup_dir
|
185
|
+
)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Migrates the project's modules to use current best practices.
|
189
|
+
#
|
190
|
+
private def migrate_modules
|
191
|
+
migrator = ModuleMigrator.new(@outputter)
|
192
|
+
|
193
|
+
migrator.migrate(
|
194
|
+
@config.project,
|
195
|
+
@config.modulepath
|
196
|
+
)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'bolt/
|
3
|
+
require 'bolt/project_manager/migrator'
|
4
4
|
|
5
5
|
module Bolt
|
6
|
-
class
|
7
|
-
class
|
6
|
+
class ProjectManager
|
7
|
+
class ConfigMigrator < Migrator
|
8
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)
|
9
|
+
bolt_yaml_to_bolt_project(config_file, project_file, inventory_file, backup_dir) &&
|
10
|
+
update_options(project_file)
|
10
11
|
end
|
11
12
|
|
12
13
|
private def bolt_yaml_to_bolt_project(config_file, project_file, inventory_file, backup_dir)
|
@@ -63,6 +64,42 @@ module Bolt
|
|
63
64
|
|
64
65
|
true
|
65
66
|
end
|
67
|
+
|
68
|
+
private def update_options(project_file)
|
69
|
+
return true unless File.exist?(project_file)
|
70
|
+
|
71
|
+
@outputter.print_message("Updating project configuration options\n\n")
|
72
|
+
data = Bolt::Util.read_yaml_hash(project_file, 'config')
|
73
|
+
modified = false
|
74
|
+
|
75
|
+
[%w[apply_settings apply-settings], %w[plugin_hooks plugin-hooks]].each do |old, new|
|
76
|
+
next unless data.key?(old)
|
77
|
+
|
78
|
+
if data.key?(new)
|
79
|
+
@outputter.print_action_step("Removing deprecated option '#{old}'")
|
80
|
+
data.delete(old)
|
81
|
+
else
|
82
|
+
@outputter.print_action_step("Updating deprecated option '#{old}' to '#{new}'")
|
83
|
+
data[new] = data.delete(old)
|
84
|
+
end
|
85
|
+
|
86
|
+
modified = true
|
87
|
+
end
|
88
|
+
|
89
|
+
if modified
|
90
|
+
begin
|
91
|
+
File.write(project_file, data.to_yaml)
|
92
|
+
rescue StandardError => e
|
93
|
+
raise Bolt::FileError.new("#{e.message}; unable to write config.", project_file)
|
94
|
+
end
|
95
|
+
|
96
|
+
@outputter.print_action_step("Successfully updated project configuration #{project_file}")
|
97
|
+
else
|
98
|
+
@outputter.print_action_step("Project configuration is up to date, nothing to do.")
|
99
|
+
end
|
100
|
+
|
101
|
+
true
|
102
|
+
end
|
66
103
|
end
|
67
104
|
end
|
68
105
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'bolt/
|
3
|
+
require 'bolt/project_manager/migrator'
|
4
4
|
|
5
5
|
module Bolt
|
6
|
-
class
|
7
|
-
class
|
6
|
+
class ProjectManager
|
7
|
+
class InventoryMigrator < Migrator
|
8
8
|
def migrate(inventory_file, backup_dir)
|
9
9
|
inventory1to2(inventory_file, backup_dir)
|
10
10
|
end
|