bolt 2.14.0 → 2.15.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/file/lib/puppet/functions/file/exists.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/read.rb +1 -1
- data/bolt-modules/file/lib/puppet/functions/file/readable.rb +1 -1
- data/lib/bolt/applicator.rb +13 -5
- data/lib/bolt/config.rb +187 -57
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/outputter.rb +3 -0
- data/lib/bolt/outputter/rainbow.rb +80 -0
- data/lib/bolt/project.rb +4 -11
- data/lib/bolt/version.rb +1 -1
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 696f0fbeea26bd6bccad0ab2935e8174b871148bcae962438996e0822a216a7f
|
4
|
+
data.tar.gz: fc865179b5b437a6474e45d1c94139b892cec331fb85511c81ef19b41af4a806
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e0bf903cf991732261e779a665de009b9e45a08c911cab88df738201ebdc8a75b5d4e82cc7585956215b0e93f30b21142a37401f94d5ce8e0d7e41c8014bf499
|
7
|
+
data.tar.gz: ea88a182be0b972869342ef16423089338b13f9e0f540b13da4b133c231f80597646c4f920d254184e5c5b276c08ec6ec6eff0c79aeb456dfd777e68ba4433fc
|
data/Puppetfile
CHANGED
@@ -33,7 +33,7 @@ mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
|
|
33
33
|
mod 'puppetlabs-aws_inventory', '0.5.0'
|
34
34
|
mod 'puppetlabs-azure_inventory', '0.3.0'
|
35
35
|
mod 'puppetlabs-gcloud_inventory', '0.1.1'
|
36
|
-
mod 'puppetlabs-pkcs7', '0.1.
|
36
|
+
mod 'puppetlabs-pkcs7', '0.1.1'
|
37
37
|
mod 'puppetlabs-terraform', '0.5.0'
|
38
38
|
mod 'puppetlabs-vault', '0.3.0'
|
39
39
|
mod 'puppetlabs-yaml', '0.2.0'
|
@@ -9,7 +9,7 @@ Puppet::Functions.create_function(:'file::exists', Puppet::Functions::InternalFu
|
|
9
9
|
# @example Check a file on disk
|
10
10
|
# file::exists('/tmp/i_dumped_this_here')
|
11
11
|
# @example check a file from the modulepath
|
12
|
-
# file::exists('example/
|
12
|
+
# file::exists('example/VERSION')
|
13
13
|
dispatch :exists do
|
14
14
|
scope_param
|
15
15
|
required_param 'String', :filename
|
@@ -8,7 +8,7 @@ Puppet::Functions.create_function(:'file::read', Puppet::Functions::InternalFunc
|
|
8
8
|
# @example Read a file from disk
|
9
9
|
# file::read('/tmp/i_dumped_this_here')
|
10
10
|
# @example Read a file from the modulepath
|
11
|
-
# file::read('example/
|
11
|
+
# file::read('example/VERSION')
|
12
12
|
dispatch :read do
|
13
13
|
scope_param
|
14
14
|
required_param 'String', :filename
|
@@ -9,7 +9,7 @@ Puppet::Functions.create_function(:'file::readable', Puppet::Functions::Internal
|
|
9
9
|
# @example Check a file on disk
|
10
10
|
# file::readable('/tmp/i_dumped_this_here')
|
11
11
|
# @example check a file from the modulepath
|
12
|
-
# file::readable('example/
|
12
|
+
# file::readable('example/VERSION')
|
13
13
|
dispatch :readable do
|
14
14
|
scope_param
|
15
15
|
required_param 'String', :filename
|
data/lib/bolt/applicator.rb
CHANGED
@@ -106,6 +106,16 @@ module Bolt
|
|
106
106
|
out, err, stat = Open3.capture3('ruby', bolt_catalog_exe, 'compile', stdin_data: catalog_input.to_json)
|
107
107
|
ENV['PATH'] = old_path
|
108
108
|
|
109
|
+
# If bolt_catalog does not return valid JSON, we should print stderr to
|
110
|
+
# see what happened
|
111
|
+
print_logs = stat.success?
|
112
|
+
result = begin
|
113
|
+
JSON.parse(out)
|
114
|
+
rescue JSON::ParserError
|
115
|
+
print_logs = true
|
116
|
+
{ 'message' => "Something's gone terribly wrong! STDERR is logged." }
|
117
|
+
end
|
118
|
+
|
109
119
|
# Any messages logged by Puppet will be on stderr as JSON hashes, so we
|
110
120
|
# parse those and store them here. Any message on stderr that is not
|
111
121
|
# properly JSON formatted is assumed to be an error message. If
|
@@ -119,17 +129,15 @@ module Bolt
|
|
119
129
|
{ 'level' => 'err', 'message' => line }
|
120
130
|
end
|
121
131
|
|
122
|
-
|
123
|
-
if stat.success?
|
132
|
+
if print_logs
|
124
133
|
logs.each do |log|
|
125
134
|
bolt_level = Bolt::Util::PuppetLogLevel::MAPPING[log['level'].to_sym]
|
126
135
|
message = log['message'].chomp
|
127
136
|
@logger.send(bolt_level, "#{target.name}: #{message}")
|
128
137
|
end
|
129
|
-
result
|
130
|
-
else
|
131
|
-
raise ApplyError.new(target.name, result['message'])
|
132
138
|
end
|
139
|
+
raise ApplyError.new(target.name, result['message']) unless stat.success?
|
140
|
+
result
|
133
141
|
end
|
134
142
|
|
135
143
|
def validate_hiera_config(hiera_config)
|
data/lib/bolt/config.rb
CHANGED
@@ -25,6 +25,11 @@ module Bolt
|
|
25
25
|
class Config
|
26
26
|
attr_reader :config_files, :warnings, :data, :transports, :project, :modified_concurrency
|
27
27
|
|
28
|
+
BOLT_CONFIG_NAME = 'bolt.yaml'
|
29
|
+
BOLT_DEFAULTS_NAME = 'bolt-defaults.yaml'
|
30
|
+
|
31
|
+
# Transport config classes. Used to load default transport config which
|
32
|
+
# gets passed along to the inventory.
|
28
33
|
TRANSPORT_CONFIG = {
|
29
34
|
'ssh' => Bolt::Config::Transport::SSH,
|
30
35
|
'winrm' => Bolt::Config::Transport::WinRM,
|
@@ -34,50 +39,84 @@ module Bolt
|
|
34
39
|
'remote' => Bolt::Config::Transport::Remote
|
35
40
|
}.freeze
|
36
41
|
|
37
|
-
|
38
|
-
|
42
|
+
# Options that configure Bolt. These options are used in bolt.yaml and
|
43
|
+
# bolt-defaults.yaml.
|
44
|
+
BOLT_CONFIG = {
|
45
|
+
"color" => "Whether to use colored output when printing messages to the console.",
|
46
|
+
"compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
|
47
|
+
"concurrency" => "The number of threads to use when executing on remote targets.",
|
48
|
+
"format" => "The format to use when printing results. Options are `human` and `json`.",
|
49
|
+
"plugin_hooks" => "Which plugins a specific hook should use.",
|
50
|
+
"plugins" => "A map of plugins and their configuration data.",
|
51
|
+
"puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
|
52
|
+
"puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
|
53
|
+
"save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
|
54
|
+
"your target names include passwords, set this value to `false` to avoid "\
|
55
|
+
"writing passwords to disk."
|
56
|
+
}.freeze
|
39
57
|
|
40
|
-
|
58
|
+
# These options are only available to bolt-defaults.yaml.
|
59
|
+
DEFAULTS_CONFIG = {
|
60
|
+
"inventory-config" => "A map of default configuration options for the inventory. This includes options "\
|
61
|
+
"for setting the default transport to use when connecting to targets, as well as "\
|
62
|
+
"options for configuring the default behavior of each transport."
|
63
|
+
}.freeze
|
41
64
|
|
42
|
-
|
65
|
+
# Options that configure the inventory, specifically the default transport
|
66
|
+
# used by targets and the transports themselves. These options are used in
|
67
|
+
# bolt.yaml, inventory.yaml, and under the inventory-config key in
|
68
|
+
# bolt-defaults.yaml.
|
69
|
+
INVENTORY_CONFIG = {
|
70
|
+
"transport" => "The default transport to use when the transport for a target is not specified in the URI.",
|
71
|
+
"docker" => "A map of configuration options for the docker transport.",
|
72
|
+
"local" => "A map of configuration options for the local transport.",
|
73
|
+
"pcp" => "A map of configuration options for the pcp transport.",
|
74
|
+
"remote" => "A map of configuration options for the remote transport.",
|
75
|
+
"ssh" => "A map of configuration options for the ssh transport.",
|
76
|
+
"winrm" => "A map of configuration options for the winrm transport."
|
77
|
+
}.freeze
|
43
78
|
|
44
|
-
#
|
45
|
-
#
|
46
|
-
|
79
|
+
# Options that configure the project, such as paths to files used for a
|
80
|
+
# specific project. These settings are used in bolt.yaml and bolt-project.yaml.
|
81
|
+
PROJECT_CONFIG = {
|
47
82
|
"apply_settings" => "A map of Puppet settings to use when applying Puppet code",
|
48
|
-
"color" => "Whether to use colored output when printing messages to the console.",
|
49
|
-
"compile-concurrency" => "The maximum number of simultaneous manifest block compiles.",
|
50
|
-
"concurrency" => "The number of threads to use when executing on remote targets.",
|
51
|
-
"format" => "The format to use when printing results. Options are `human` and `json`.",
|
52
83
|
"hiera-config" => "The path to your Hiera config.",
|
53
84
|
"inventoryfile" => "The path to a structured data inventory file used to refer to groups of "\
|
54
85
|
"targets on the command line and from plans.",
|
55
86
|
"log" => "The configuration of the logfile output. Configuration can be set for "\
|
56
87
|
"`console` and the path to a log file, such as `~/.puppetlabs/bolt/debug.log`.",
|
57
88
|
"modulepath" => "An array of directories that Bolt loads content (e.g. plans and tasks) from.",
|
58
|
-
"plugin_hooks" => "Which plugins a specific hook should use.",
|
59
|
-
"plugins" => "A map of plugins and their configuration data.",
|
60
|
-
"puppetdb" => "A map containing options for configuring the Bolt PuppetDB client.",
|
61
|
-
"puppetfile" => "A map containing options for the `bolt puppetfile install` command.",
|
62
|
-
"save-rerun" => "Whether to update `.rerun.json` in the Bolt project directory. If "\
|
63
|
-
"your target names include passwords, set this value to `false` to avoid "\
|
64
|
-
"writing passwords to disk.",
|
65
|
-
"transport" => "The default transport to use when the transport for a target is not specified "\
|
66
|
-
"in the URL or inventory.",
|
67
89
|
"trusted-external-command" => "The path to an executable on the Bolt controller that can produce "\
|
68
90
|
"external trusted facts. **External trusted facts are experimental in both "\
|
69
91
|
"Puppet and Bolt and this API may change or be removed.**"
|
70
92
|
}.freeze
|
71
93
|
|
94
|
+
# A combined map of all configuration options that can be set in this class.
|
95
|
+
# Includes all options except 'inventory-config', which is munged when loading
|
96
|
+
# a bolt-defaults.yaml file.
|
97
|
+
OPTIONS = BOLT_CONFIG.merge(INVENTORY_CONFIG).merge(PROJECT_CONFIG).freeze
|
98
|
+
|
99
|
+
# Default values for select options. These do not set the default values in Bolt
|
100
|
+
# and are only used for documentation.
|
72
101
|
DEFAULT_OPTIONS = {
|
73
|
-
"color"
|
102
|
+
"color" => true,
|
74
103
|
"compile-concurrency" => "Number of cores",
|
75
|
-
"concurrency"
|
76
|
-
"format"
|
77
|
-
"hiera-config"
|
78
|
-
"inventoryfile"
|
79
|
-
"modulepath"
|
80
|
-
"save-rerun"
|
104
|
+
"concurrency" => "100 or one-seventh of the ulimit, whichever is lower",
|
105
|
+
"format" => "human",
|
106
|
+
"hiera-config" => "Boltdir/hiera.yaml",
|
107
|
+
"inventoryfile" => "Boltdir/inventory.yaml",
|
108
|
+
"modulepath" => ["Boltdir/modules", "Boltdir/site-modules", "Boltdir/site"],
|
109
|
+
"save-rerun" => true,
|
110
|
+
"transport" => "ssh"
|
111
|
+
}.freeze
|
112
|
+
|
113
|
+
PUPPETDB_OPTIONS = {
|
114
|
+
"cacert" => "The path to the ca certificate for PuppetDB.",
|
115
|
+
"cert" => "The path to the client certificate file to use for authentication.",
|
116
|
+
"key" => "The private key for the certificate.",
|
117
|
+
"server_urls" => "An array containing the PuppetDB host to connect to. Include the protocol `https` and "\
|
118
|
+
"the port, which is usually `8081`. For example, `https://my-master.example.com:8081`.",
|
119
|
+
"token" => "The path to the PE RBAC Token."
|
81
120
|
}.freeze
|
82
121
|
|
83
122
|
PUPPETFILE_OPTIONS = {
|
@@ -121,57 +160,144 @@ module Bolt
|
|
121
160
|
Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
|
122
161
|
end
|
123
162
|
|
124
|
-
data =
|
125
|
-
|
126
|
-
|
163
|
+
data = load_defaults(project).push(
|
164
|
+
filepath: project.config_file,
|
165
|
+
data: conf,
|
166
|
+
warnings: []
|
167
|
+
)
|
127
168
|
|
128
169
|
new(project, data, overrides)
|
129
170
|
end
|
130
171
|
|
131
172
|
def self.from_file(configfile, overrides = {})
|
132
173
|
project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
|
174
|
+
|
133
175
|
conf = if project.project_file == project.config_file
|
134
176
|
project.data
|
135
177
|
else
|
136
178
|
Bolt::Util.read_yaml_hash(configfile, 'config')
|
137
179
|
end
|
138
|
-
|
139
|
-
data = load_defaults(project).push(
|
180
|
+
|
181
|
+
data = load_defaults(project).push(
|
182
|
+
filepath: project.config_file,
|
183
|
+
data: conf,
|
184
|
+
warnings: []
|
185
|
+
)
|
140
186
|
|
141
187
|
new(project, data, overrides)
|
142
188
|
end
|
143
189
|
|
144
|
-
def self.
|
190
|
+
def self.system_path
|
145
191
|
# Lazy-load expensive gem code
|
146
192
|
require 'win32/dir' if Bolt::Util.windows?
|
147
193
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
194
|
+
if Bolt::Util.windows?
|
195
|
+
Pathname.new(File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc'))
|
196
|
+
else
|
197
|
+
Pathname.new(File.join('/etc', 'puppetlabs', 'bolt'))
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.user_path
|
202
|
+
Pathname.new(File.expand_path(File.join('~', '.puppetlabs', 'etc', 'bolt')))
|
203
|
+
rescue StandardError
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
|
207
|
+
# Loads a 'bolt-defaults.yaml' file, which contains default configuration that applies to all
|
208
|
+
# projects. This file does not allow project-specific configuration such as 'hiera-config' and
|
209
|
+
# 'inventoryfile', and nests all default inventory configuration under an 'inventory-config' key.
|
210
|
+
def self.load_bolt_defaults_yaml(dir)
|
211
|
+
filepath = dir + BOLT_DEFAULTS_NAME
|
212
|
+
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
213
|
+
warnings = []
|
214
|
+
|
215
|
+
# Warn if 'bolt.yaml' detected in same directory.
|
216
|
+
if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
|
217
|
+
warnings.push(
|
218
|
+
msg: "Detected multiple configuration files: ['#{bolt_yaml}', '#{filepath}']. '#{bolt_yaml}' "\
|
219
|
+
"will be ignored."
|
220
|
+
)
|
221
|
+
end
|
222
|
+
|
223
|
+
# Remove project-specific config such as hiera-config, etc.
|
224
|
+
project_config = data.slice(*PROJECT_CONFIG.keys)
|
225
|
+
|
226
|
+
if project_config.any?
|
227
|
+
data.reject! { |key, _| project_config.include?(key) }
|
228
|
+
warnings.push(
|
229
|
+
msg: "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
|
230
|
+
"Project configuration should be set in 'bolt-project.yaml'."
|
231
|
+
)
|
232
|
+
end
|
233
|
+
|
234
|
+
# Remove top-level transport config such as transport, ssh, etc.
|
235
|
+
transport_config = data.slice(*INVENTORY_CONFIG.keys)
|
236
|
+
|
237
|
+
if transport_config.any?
|
238
|
+
data.reject! { |key, _| transport_config.include?(key) }
|
239
|
+
warnings.push(
|
240
|
+
msg: "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
|
241
|
+
"Transport configuration should be set under the 'inventory-config' option or "\
|
242
|
+
"in 'inventory.yaml'."
|
243
|
+
)
|
244
|
+
end
|
245
|
+
|
246
|
+
# Move data under transport-config to top-level so it can be easily merged with
|
247
|
+
# config from other sources.
|
248
|
+
if data.key?('inventory-config')
|
249
|
+
data = data.merge(data.delete('inventory-config'))
|
250
|
+
end
|
251
|
+
|
252
|
+
{ filepath: filepath, data: data, warnings: warnings }
|
253
|
+
end
|
254
|
+
|
255
|
+
# Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
|
256
|
+
# here since Bolt::Config will just ignore any invalid keys.
|
257
|
+
def self.load_bolt_yaml(dir)
|
258
|
+
filepath = dir + BOLT_CONFIG_NAME
|
259
|
+
data = Bolt::Util.read_yaml_hash(filepath, 'config')
|
260
|
+
warnings = [msg: "Configuration file #{filepath} is deprecated and will be removed in a future version "\
|
261
|
+
"of Bolt. Use '#{dir + BOLT_DEFAULTS_NAME}' instead."]
|
262
|
+
|
263
|
+
{ filepath: filepath, data: data, warnings: warnings }
|
264
|
+
end
|
265
|
+
|
266
|
+
def self.load_defaults(project)
|
267
|
+
confs = []
|
268
|
+
|
269
|
+
# Load system-level config. Prefer a 'bolt-defaults.yaml' file, but fall back to the
|
270
|
+
# legacy 'bolt.yaml' file. If the project-level config file is also the system-level
|
271
|
+
# config file, don't load it a second time.
|
272
|
+
if File.exist?(system_path + BOLT_DEFAULTS_NAME)
|
273
|
+
confs << load_bolt_defaults_yaml(system_path)
|
274
|
+
elsif File.exist?(system_path + BOLT_CONFIG_NAME) &&
|
275
|
+
(system_path + BOLT_CONFIG_NAME) != project.config_file
|
276
|
+
confs << load_bolt_yaml(system_path)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Load user-level config if there is a homedir. Prefer a 'bolt-defaults.yaml' file, but
|
280
|
+
# fall back to the legacy 'bolt.yaml' file.
|
281
|
+
if user_path
|
282
|
+
if File.exist?(user_path + BOLT_DEFAULTS_NAME)
|
283
|
+
confs << load_bolt_defaults_yaml(user_path)
|
284
|
+
elsif File.exist?(user_path + BOLT_CONFIG_NAME)
|
285
|
+
confs << load_bolt_yaml(user_path)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
163
289
|
confs
|
164
290
|
end
|
165
291
|
|
166
292
|
def initialize(project, config_data, overrides = {})
|
167
293
|
unless config_data.is_a?(Array)
|
168
|
-
config_data = [{ filepath: project.config_file, data: config_data }]
|
294
|
+
config_data = [{ filepath: project.config_file, data: config_data, warnings: [] }]
|
169
295
|
end
|
170
296
|
|
171
|
-
@logger
|
172
|
-
@project
|
173
|
-
@warnings
|
174
|
-
@transports
|
297
|
+
@logger = Logging.logger[self]
|
298
|
+
@project = project
|
299
|
+
@warnings = @project.warnings.dup
|
300
|
+
@transports = {}
|
175
301
|
@config_files = []
|
176
302
|
|
177
303
|
default_data = {
|
@@ -189,9 +315,13 @@ module Bolt
|
|
189
315
|
'transport' => 'ssh'
|
190
316
|
}
|
191
317
|
|
192
|
-
loaded_data = config_data.
|
193
|
-
@
|
194
|
-
|
318
|
+
loaded_data = config_data.each_with_object([]) do |data, acc|
|
319
|
+
@warnings.concat(data[:warnings]) if data[:warnings].any?
|
320
|
+
|
321
|
+
if data[:data].any?
|
322
|
+
@config_files.push(data[:filepath])
|
323
|
+
acc.push(data[:data])
|
324
|
+
end
|
195
325
|
end
|
196
326
|
|
197
327
|
override_data = normalize_overrides(overrides)
|
@@ -356,7 +486,7 @@ module Bolt
|
|
356
486
|
raise Bolt::ValidationError, "Compilation is CPU-intensive, set concurrency less than #{compile_limit}"
|
357
487
|
end
|
358
488
|
|
359
|
-
|
489
|
+
if (format == 'rainbow' && Bolt::Util.windows?) || !(%w[human json rainbow].include? format)
|
360
490
|
raise Bolt::ValidationError, "Unsupported format: '#{format}'"
|
361
491
|
end
|
362
492
|
|
data/lib/bolt/inventory/group.rb
CHANGED
@@ -16,7 +16,7 @@ module Bolt
|
|
16
16
|
DATA_KEYS = %w[config facts vars features plugin_hooks].freeze
|
17
17
|
TARGET_KEYS = DATA_KEYS + %w[name alias uri]
|
18
18
|
GROUP_KEYS = DATA_KEYS + %w[name groups targets]
|
19
|
-
CONFIG_KEYS = Bolt::Config::
|
19
|
+
CONFIG_KEYS = Bolt::Config::INVENTORY_CONFIG.keys
|
20
20
|
|
21
21
|
def initialize(input, plugins)
|
22
22
|
@logger = Logging.logger[self]
|
data/lib/bolt/outputter.rb
CHANGED
@@ -8,6 +8,8 @@ module Bolt
|
|
8
8
|
Bolt::Outputter::Human.new(color, verbose, trace)
|
9
9
|
when 'json'
|
10
10
|
Bolt::Outputter::JSON.new(color, verbose, trace)
|
11
|
+
when 'rainbow'
|
12
|
+
Bolt::Outputter::Rainbow.new(color, verbose, trace)
|
11
13
|
when nil
|
12
14
|
raise "Cannot use outputter before parsing."
|
13
15
|
end
|
@@ -24,4 +26,5 @@ end
|
|
24
26
|
|
25
27
|
require 'bolt/outputter/human'
|
26
28
|
require 'bolt/outputter/json'
|
29
|
+
require 'bolt/outputter/rainbow'
|
27
30
|
require 'bolt/outputter/logger'
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bolt/pal'
|
4
|
+
require 'paint'
|
5
|
+
|
6
|
+
module Bolt
|
7
|
+
class Outputter
|
8
|
+
class Rainbow < Bolt::Outputter::Human
|
9
|
+
def initialize(color, verbose, trace, stream = $stdout)
|
10
|
+
super
|
11
|
+
@line_color = 0
|
12
|
+
@color = 0
|
13
|
+
@state = :normal
|
14
|
+
end
|
15
|
+
|
16
|
+
# The algorithm is from lolcat (https://github.com/busyloop/lolcat)
|
17
|
+
# lolcat is released with WTFPL
|
18
|
+
def rainbow
|
19
|
+
red = Math.sin(0.3 * @color + 0) * 127 + 128
|
20
|
+
green = Math.sin(0.3 * @color + 2 * Math::PI / 3) * 127 + 128
|
21
|
+
blue = Math.sin(0.3 * @color + 4 * Math::PI / 3) * 127 + 128
|
22
|
+
@color += 1 / 8.0
|
23
|
+
format("%<red>02X%<green>02X%<blue>02X", red: red, green: green, blue: blue)
|
24
|
+
end
|
25
|
+
|
26
|
+
def colorize(color, string)
|
27
|
+
if @color && @stream.isatty
|
28
|
+
if %i[green rainbow].include?(color)
|
29
|
+
a = string.chars.map do |c|
|
30
|
+
case @state
|
31
|
+
when :normal
|
32
|
+
if c == "\e"
|
33
|
+
@state = :ansi
|
34
|
+
elsif c == "\n"
|
35
|
+
@line_color += 1
|
36
|
+
@color = @line_color
|
37
|
+
c
|
38
|
+
else
|
39
|
+
Paint[c, rainbow]
|
40
|
+
end
|
41
|
+
when :ansi
|
42
|
+
@state = :normal if c == 'm'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
a.join('')
|
46
|
+
else
|
47
|
+
"\033[#{COLORS[color]}m#{string}\033[0m"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
string
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def print_summary(results, elapsed_time = nil)
|
55
|
+
ok_set = results.ok_set
|
56
|
+
unless ok_set.empty?
|
57
|
+
@stream.puts colorize(:rainbow, format('Successful on %<size>d target%<plural>s: %<names>s',
|
58
|
+
size: ok_set.size,
|
59
|
+
plural: ok_set.size == 1 ? '' : 's',
|
60
|
+
names: ok_set.targets.map(&:safe_name).join(',')))
|
61
|
+
end
|
62
|
+
|
63
|
+
error_set = results.error_set
|
64
|
+
unless error_set.empty?
|
65
|
+
@stream.puts colorize(:red,
|
66
|
+
format('Failed on %<size>d target%<plural>s: %<names>s',
|
67
|
+
size: error_set.size,
|
68
|
+
plural: error_set.size == 1 ? '' : 's',
|
69
|
+
names: error_set.targets.map(&:safe_name).join(',')))
|
70
|
+
end
|
71
|
+
|
72
|
+
total_msg = format('Ran on %<size>d target%<plural>s',
|
73
|
+
size: results.size,
|
74
|
+
plural: results.size == 1 ? '' : 's')
|
75
|
+
total_msg << " in #{duration_to_string(elapsed_time)}" unless elapsed_time.nil?
|
76
|
+
@stream.puts colorize(:rainbow, total_msg)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/bolt/project.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
require 'bolt/pal'
|
5
|
+
require 'bolt/config'
|
5
6
|
|
6
7
|
module Bolt
|
7
8
|
class Project
|
@@ -19,15 +20,7 @@ module Bolt
|
|
19
20
|
create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
|
20
21
|
# If homedir isn't defined use the system config path
|
21
22
|
rescue ArgumentError
|
22
|
-
create_project(system_path, 'system')
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.system_path
|
26
|
-
if Bolt::Util.windows?
|
27
|
-
File.join(Dir::COMMON_APPDATA, 'PuppetLabs', 'bolt', 'etc')
|
28
|
-
else
|
29
|
-
File.join('/etc', 'puppetlabs', 'bolt')
|
30
|
-
end
|
23
|
+
create_project(Bolt::Config.system_path, 'system')
|
31
24
|
end
|
32
25
|
|
33
26
|
# Search recursively up the directory hierarchy for the Project. Look for a
|
@@ -74,13 +67,13 @@ module Bolt
|
|
74
67
|
@resource_types = @path + '.resource_types'
|
75
68
|
@type = type
|
76
69
|
|
77
|
-
tc = Bolt::Config::
|
70
|
+
tc = Bolt::Config::INVENTORY_CONFIG.keys & raw_data.keys
|
78
71
|
if tc.any?
|
79
72
|
msg = "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}"
|
80
73
|
@warnings << { msg: msg }
|
81
74
|
end
|
82
75
|
|
83
|
-
@data = raw_data.reject { |k, _| Bolt::Config::
|
76
|
+
@data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_CONFIG.keys.include?(k) }
|
84
77
|
|
85
78
|
# Once bolt.yaml deprecation is removed, this attribute should be removed
|
86
79
|
# and replaced with .project_file in lib/bolt/config.rb
|
data/lib/bolt/version.rb
CHANGED
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.15.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-06-
|
11
|
+
date: 2020-06-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: addressable
|
@@ -178,6 +178,20 @@ dependencies:
|
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0.4'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: paint
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '2.2'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '2.2'
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: puppet
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -467,6 +481,7 @@ files:
|
|
467
481
|
- lib/bolt/outputter/human.rb
|
468
482
|
- lib/bolt/outputter/json.rb
|
469
483
|
- lib/bolt/outputter/logger.rb
|
484
|
+
- lib/bolt/outputter/rainbow.rb
|
470
485
|
- lib/bolt/pal.rb
|
471
486
|
- lib/bolt/pal/issues.rb
|
472
487
|
- lib/bolt/pal/logging.rb
|