bolt 2.37.0 → 2.44.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.

Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/Puppetfile +17 -17
  3. data/bolt-modules/boltlib/lib/puppet/functions/parallelize.rb +6 -8
  4. data/lib/bolt/analytics.rb +3 -2
  5. data/lib/bolt/applicator.rb +11 -1
  6. data/lib/bolt/bolt_option_parser.rb +20 -13
  7. data/lib/bolt/catalog.rb +10 -29
  8. data/lib/bolt/cli.rb +58 -40
  9. data/lib/bolt/config.rb +134 -119
  10. data/lib/bolt/config/options.rb +142 -77
  11. data/lib/bolt/config/transport/base.rb +2 -2
  12. data/lib/bolt/config/transport/local.rb +1 -0
  13. data/lib/bolt/config/transport/options.rb +18 -68
  14. data/lib/bolt/config/transport/orch.rb +1 -0
  15. data/lib/bolt/config/transport/ssh.rb +0 -5
  16. data/lib/bolt/executor.rb +15 -5
  17. data/lib/bolt/inventory.rb +26 -0
  18. data/lib/bolt/inventory/group.rb +35 -12
  19. data/lib/bolt/inventory/inventory.rb +1 -1
  20. data/lib/bolt/inventory/options.rb +130 -0
  21. data/lib/bolt/inventory/target.rb +10 -11
  22. data/lib/bolt/logger.rb +114 -10
  23. data/lib/bolt/module.rb +10 -2
  24. data/lib/bolt/module_installer.rb +25 -15
  25. data/lib/bolt/module_installer/resolver.rb +65 -12
  26. data/lib/bolt/module_installer/specs/forge_spec.rb +8 -2
  27. data/lib/bolt/module_installer/specs/git_spec.rb +17 -2
  28. data/lib/bolt/outputter.rb +19 -5
  29. data/lib/bolt/outputter/human.rb +24 -1
  30. data/lib/bolt/outputter/json.rb +1 -1
  31. data/lib/bolt/outputter/logger.rb +1 -1
  32. data/lib/bolt/outputter/rainbow.rb +12 -1
  33. data/lib/bolt/pal.rb +93 -14
  34. data/lib/bolt/pal/yaml_plan.rb +8 -2
  35. data/lib/bolt/pal/yaml_plan/evaluator.rb +2 -2
  36. data/lib/bolt/pal/yaml_plan/transpiler.rb +6 -1
  37. data/lib/bolt/plugin.rb +3 -3
  38. data/lib/bolt/plugin/cache.rb +8 -8
  39. data/lib/bolt/plugin/module.rb +1 -1
  40. data/lib/bolt/plugin/puppet_connect_data.rb +35 -0
  41. data/lib/bolt/plugin/puppetdb.rb +2 -2
  42. data/lib/bolt/project.rb +76 -50
  43. data/lib/bolt/project_manager.rb +2 -0
  44. data/lib/bolt/project_manager/config_migrator.rb +9 -1
  45. data/lib/bolt/project_manager/module_migrator.rb +2 -0
  46. data/lib/bolt/puppetdb/client.rb +8 -0
  47. data/lib/bolt/rerun.rb +1 -1
  48. data/lib/bolt/shell/bash.rb +1 -1
  49. data/lib/bolt/shell/bash/tmpdir.rb +4 -1
  50. data/lib/bolt/shell/powershell.rb +7 -5
  51. data/lib/bolt/target.rb +4 -0
  52. data/lib/bolt/task.rb +1 -1
  53. data/lib/bolt/transport/docker/connection.rb +2 -2
  54. data/lib/bolt/transport/local.rb +13 -0
  55. data/lib/bolt/transport/orch/connection.rb +1 -1
  56. data/lib/bolt/transport/ssh.rb +1 -2
  57. data/lib/bolt/transport/ssh/connection.rb +1 -1
  58. data/lib/bolt/validator.rb +227 -0
  59. data/lib/bolt/version.rb +1 -1
  60. data/lib/bolt_server/config.rb +1 -1
  61. data/lib/bolt_server/schemas/partials/task.json +1 -1
  62. data/lib/bolt_server/transport_app.rb +28 -27
  63. data/libexec/bolt_catalog +1 -1
  64. metadata +27 -11
  65. data/lib/bolt/config/validator.rb +0 -231
@@ -12,6 +12,7 @@ module Bolt
12
12
  host
13
13
  job-poll-interval
14
14
  job-poll-timeout
15
+ read-timeout
15
16
  service-url
16
17
  task-environment
17
18
  token-file
@@ -111,11 +111,6 @@ module Bolt
111
111
  @config['interpreters'] = normalize_interpreters(@config['interpreters'])
112
112
  end
113
113
 
114
- if @config['login-shell'] && !LOGIN_SHELLS.include?(@config['login-shell'])
115
- raise Bolt::ValidationError,
116
- "Unsupported login-shell #{@config['login-shell']}. Supported shells are #{LOGIN_SHELLS.join(', ')}"
117
- end
118
-
119
114
  if @config['login-shell'] == 'powershell'
120
115
  %w[tty run-as].each do |key|
121
116
  if @config[key]
@@ -100,6 +100,8 @@ module Bolt
100
100
  # that type of event, publish the event
101
101
  next unless types.nil? || types.include?(event[:type])
102
102
  @publisher.post(subscriber) do |sub|
103
+ # Wait for user to input to prompt before printing anything
104
+ sleep(0.1) while @prompting
103
105
  sub.handle_event(event)
104
106
  end
105
107
  end
@@ -119,11 +121,12 @@ module Bolt
119
121
  def queue_execute(targets)
120
122
  if @warn_concurrency && targets.length > @concurrency
121
123
  @warn_concurrency = false
122
- @logger.warn("The ulimit is low, which may cause file limit issues. Default concurrency has been set to "\
123
- "'#{@concurrency}' to mitigate those issues, which may cause Bolt to run slow. "\
124
- "Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
125
- "configuration, or by configuring Bolt's concurrency. "\
126
- "See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details.")
124
+ msg = "The ulimit is low, which may cause file limit issues. Default concurrency has been set to "\
125
+ "'#{@concurrency}' to mitigate those issues, which may cause Bolt to run slow. "\
126
+ "Disable this warning by configuring ulimit using 'ulimit -n <limit>' in your shell "\
127
+ "configuration, or by configuring Bolt's concurrency. "\
128
+ "See https://puppet.com/docs/bolt/latest/bolt_known_issues.html for details."
129
+ Bolt::Logger.warn("low_ulimit", msg)
127
130
  end
128
131
 
129
132
  targets.group_by(&:transport).flat_map do |protocol, protocol_targets|
@@ -258,7 +261,9 @@ module Bolt
258
261
 
259
262
  def with_node_logging(description, batch, log_level = :info)
260
263
  @logger.send(log_level, "#{description} on #{batch.map(&:safe_name)}")
264
+ publish_event(type: :start_spin)
261
265
  result = yield
266
+ publish_event(type: :stop_spin)
262
267
  @logger.send(log_level, result.to_json)
263
268
  result
264
269
  end
@@ -410,6 +415,7 @@ module Bolt
410
415
  subscribe(self, [:node_result])
411
416
  results = Array.new(skein.length)
412
417
  @in_parallel = true
418
+ publish_event(type: :stop_spin)
413
419
 
414
420
  until skein.empty?
415
421
  @thread_completed = false
@@ -417,6 +423,7 @@ module Bolt
417
423
 
418
424
  skein.each do |yarn|
419
425
  if yarn.alive?
426
+ publish_event(type: :stop_spin)
420
427
  r = yarn.resume
421
428
  else
422
429
  results[yarn.index] = yarn.value
@@ -428,6 +435,7 @@ module Bolt
428
435
  sleep(0.1) until @thread_completed || skein.empty?
429
436
  end
430
437
 
438
+ publish_event(type: :stop_spin)
431
439
  @in_parallel = false
432
440
  unsubscribe(self, [:node_result])
433
441
  results
@@ -469,6 +477,7 @@ module Bolt
469
477
  end
470
478
 
471
479
  def prompt(prompt, options)
480
+ @prompting = true
472
481
  unless $stdin.tty?
473
482
  raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
474
483
  end
@@ -480,6 +489,7 @@ module Bolt
480
489
  else
481
490
  $stdin.gets.to_s.chomp
482
491
  end
492
+ @prompting = false
483
493
 
484
494
  $stderr.puts if options[:sensitive]
485
495
 
@@ -4,13 +4,17 @@ require 'set'
4
4
  require 'bolt/config'
5
5
  require 'bolt/inventory/group'
6
6
  require 'bolt/inventory/inventory'
7
+ require 'bolt/inventory/options'
7
8
  require 'bolt/target'
8
9
  require 'bolt/util'
9
10
  require 'bolt/plugin'
11
+ require 'bolt/validator'
10
12
  require 'yaml'
11
13
 
12
14
  module Bolt
13
15
  class Inventory
16
+ include Bolt::Inventory::Options
17
+
14
18
  ENVIRONMENT_VAR = 'BOLT_INVENTORY'
15
19
 
16
20
  class ValidationError < Bolt::Error
@@ -45,11 +49,26 @@ module Bolt
45
49
  end
46
50
  end
47
51
 
52
+ # Builds the schema used by the validator.
53
+ #
54
+ def self.schema
55
+ schema = {
56
+ type: Hash,
57
+ properties: OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
58
+ definitions: DEFINITIONS,
59
+ _plugin: true
60
+ }
61
+
62
+ schema[:definitions]['config'][:properties] = Bolt::Config.transport_definitions
63
+ schema
64
+ end
65
+
48
66
  def self.from_config(config, plugins)
49
67
  logger = Bolt::Logger.logger(self)
50
68
 
51
69
  if ENV.include?(ENVIRONMENT_VAR)
52
70
  begin
71
+ source = ENVIRONMENT_VAR
53
72
  data = YAML.safe_load(ENV[ENVIRONMENT_VAR])
54
73
  raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}" unless data.is_a?(Hash)
55
74
  logger.debug("Loaded inventory from environment variable #{ENVIRONMENT_VAR}")
@@ -57,9 +76,11 @@ module Bolt
57
76
  raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
58
77
  end
59
78
  elsif config.inventoryfile
79
+ source = config.inventoryfile
60
80
  data = Bolt::Util.read_yaml_hash(config.inventoryfile, 'inventory')
61
81
  logger.debug("Loaded inventory from #{config.inventoryfile}")
62
82
  else
83
+ source = config.default_inventoryfile
63
84
  data = Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
64
85
 
65
86
  if config.default_inventoryfile.exist?
@@ -74,6 +95,11 @@ module Bolt
74
95
  t.resolve(plugins) unless t.resolved?
75
96
  end
76
97
 
98
+ Bolt::Validator.new.tap do |validator|
99
+ validator.validate(data, schema, source)
100
+ validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
101
+ end
102
+
77
103
  inventory = create_version(data, config.transport, config.transports, plugins)
78
104
  inventory.validate
79
105
  inventory
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bolt/config/options'
3
4
  require 'bolt/inventory/group'
4
5
  require 'bolt/inventory/inventory'
5
6
  require 'bolt/inventory/target'
@@ -18,12 +19,23 @@ module Bolt
18
19
  GROUP_KEYS = DATA_KEYS + %w[name groups targets]
19
20
  CONFIG_KEYS = Bolt::Config::INVENTORY_OPTIONS.keys
20
21
 
21
- def initialize(input, plugins)
22
+ def initialize(input, plugins, all_group: false)
22
23
  @logger = Bolt::Logger.logger(self)
23
24
  @plugins = plugins
24
25
 
25
26
  input = @plugins.resolve_top_level_references(input) if @plugins.reference?(input)
26
27
 
28
+ if all_group
29
+ if input.key?('name') && input['name'] != 'all'
30
+ Bolt::Logger.warn(
31
+ "top_level_group_name",
32
+ "Top-level group '#{input['name']}' cannot specify a name, using 'all' instead."
33
+ )
34
+ end
35
+
36
+ input = input.merge('name' => 'all')
37
+ end
38
+
27
39
  raise ValidationError.new("Group does not have a name", nil) unless input.key?('name')
28
40
 
29
41
  @name = @plugins.resolve_references(input['name'])
@@ -125,7 +137,7 @@ module Bolt
125
137
 
126
138
  unless (unexpected_keys = target.keys - TARGET_KEYS).empty?
127
139
  msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in target #{t_name}"
128
- @logger.warn(msg)
140
+ Bolt::Logger.warn("unknown_target_keys", msg)
129
141
  end
130
142
 
131
143
  validate_data_keys(target, t_name)
@@ -252,15 +264,7 @@ module Bolt
252
264
 
253
265
  unless (unexpected_keys = input.keys - GROUP_KEYS).empty?
254
266
  msg = "Found unexpected key(s) #{unexpected_keys.join(', ')} in group #{@name}"
255
- @logger.warn(msg)
256
- end
257
-
258
- Bolt::Util.walk_keys(input) do |key|
259
- if @plugins.reference?(key)
260
- raise ValidationError.new("Group keys cannot be specified as _plugin references", @name)
261
- else
262
- key
263
- end
267
+ Bolt::Logger.warn("unknown_group_keys", msg)
264
268
  end
265
269
  end
266
270
 
@@ -323,7 +327,26 @@ module Bolt
323
327
  'features' => @plugins.resolve_references(data.fetch('features', [])),
324
328
  'plugin_hooks' => @plugins.resolve_references(data.fetch('plugin_hooks', {}))
325
329
  }
330
+
326
331
  validate_data_keys(result, target)
332
+
333
+ Bolt::Config::Options::TRANSPORT_CONFIG.each_key do |transport|
334
+ next unless result['config'].key?(transport)
335
+ transport_config = result['config'][transport]
336
+ next unless transport_config.is_a?(Hash)
337
+ transport_config = Bolt::Util.postwalk_vals(transport_config) do |val|
338
+ if val.is_a?(Hash)
339
+ val = val.compact
340
+ val = nil if val.empty?
341
+ end
342
+ val
343
+ end
344
+ # the transport config is user-specified data so we
345
+ # still want to preserve it even if it exclusively
346
+ # contains nil-resolved keys
347
+ result['config'][transport] = transport_config || {}
348
+ end
349
+
327
350
  result['features'] = Set.new(result['features'].flatten)
328
351
  result
329
352
  end
@@ -348,7 +371,7 @@ module Bolt
348
371
  msg = +"Found unexpected key(s) #{unexpected_keys.join(', ')} in config for"
349
372
  msg << " target #{target} in" if target
350
373
  msg << " group #{@name}"
351
- @logger.warn(msg)
374
+ Bolt::Logger.warn("unknown_config_keys", msg)
352
375
  end
353
376
  end
354
377
  end
@@ -21,7 +21,7 @@ module Bolt
21
21
  @transport = transport
22
22
  @config = transports
23
23
  @plugins = plugins
24
- @groups = Group.new(@data.merge('name' => 'all'), plugins)
24
+ @groups = Group.new(@data, plugins, all_group: true)
25
25
  @group_lookup = {}
26
26
  @targets = {}
27
27
 
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/config/options'
4
+
5
+ module Bolt
6
+ class Inventory
7
+ module Options
8
+ # Top-level options available in the inventory.
9
+ OPTIONS = %w[
10
+ config
11
+ facts
12
+ features
13
+ groups
14
+ targets
15
+ vars
16
+ ].freeze
17
+
18
+ # Definitions used to validate the data.
19
+ # https://github.com/puppetlabs/bolt/blob/main/schemas/README.md
20
+ DEFINITIONS = {
21
+ "alias" => {
22
+ description: "A unique alias to refer to the target. Aliases cannot conflict "\
23
+ "with the name of a group, the name of a target, or another alias.",
24
+ type: [String, Array],
25
+ uniqueItems: true,
26
+ items: {
27
+ type: String,
28
+ _plugin: true
29
+ },
30
+ _plugin: true
31
+ },
32
+ "config" => {
33
+ description: "A map of configuration options.",
34
+ type: Hash,
35
+ # These properties are populated as part of Bolt::Inventory.schema
36
+ properties: {},
37
+ _plugin: true
38
+ },
39
+ "facts" => {
40
+ description: "A map of system information, also known as facts, for the target.",
41
+ type: Hash,
42
+ _plugin: true
43
+ },
44
+ "features" => {
45
+ description: "A list of available features for the target.",
46
+ type: Array,
47
+ uniqueItems: true,
48
+ items: {
49
+ type: String,
50
+ _plugin: true
51
+ },
52
+ _plugin: true
53
+ },
54
+ "groups" => {
55
+ description: "A list of groups and their associated configuration.",
56
+ type: Array,
57
+ items: {
58
+ type: Hash,
59
+ required: ["name"],
60
+ properties: {
61
+ "config" => { _ref: "config" },
62
+ "facts" => { _ref: "facts" },
63
+ "features" => { _ref: "features" },
64
+ "groups" => { _ref: "groups" },
65
+ "name" => { _ref: "name" },
66
+ "plugin_hooks" => { _ref: "plugin_hooks" },
67
+ "targets" => { _ref: "targets" },
68
+ "vars" => { _ref: "vars" }
69
+ },
70
+ _plugin: true
71
+ },
72
+ _plugin: true
73
+ },
74
+ "name" => {
75
+ description: "A human-readable name to refer to the group or target. Names "\
76
+ "cannot conflict with the name of a group, the name of a target, "\
77
+ "or the alias of a target. A name is required for a group and is "\
78
+ "required for a target unless the uri option is set.",
79
+ type: String,
80
+ _plugin: true
81
+ },
82
+ "plugin_hooks" => {
83
+ description: "Configuration for the Puppet library plugin used to install the "\
84
+ "Puppet agent on the target. For more information, see "\
85
+ "https://pup.pt/bolt-plugin-hooks",
86
+ type: Hash,
87
+ properties: {
88
+ "puppet_library" => {
89
+ description: "Configuration for the Puppet library plugin.",
90
+ type: Hash,
91
+ _plugin: true
92
+ }
93
+ },
94
+ _plugin: true
95
+ },
96
+ "targets" => {
97
+ description: "A list of targets and their associated configuration.",
98
+ type: Array,
99
+ items: {
100
+ type: [String, Hash],
101
+ properties: {
102
+ "alias" => { _ref: "alias" },
103
+ "config" => { _ref: "config" },
104
+ "facts" => { _ref: "facts" },
105
+ "features" => { _ref: "features" },
106
+ "name" => { _ref: "name" },
107
+ "plugin_hooks" => { _ref: "plugin_hooks" },
108
+ "uri" => { _ref: "uri" },
109
+ "vars" => { _ref: "vars" }
110
+ },
111
+ _plugin: true
112
+ },
113
+ _plugin: true
114
+ },
115
+ "uri" => {
116
+ description: "The URI of the target. This option is required unless the name "\
117
+ "option is set.",
118
+ type: String,
119
+ format: "uri",
120
+ _plugin: true
121
+ },
122
+ "vars" => {
123
+ description: "A map of variables for the group or target.",
124
+ type: Hash,
125
+ _plugin: true
126
+ }
127
+ }.freeze
128
+ end
129
+ end
130
+ end
@@ -31,7 +31,8 @@ module Bolt
31
31
  end
32
32
 
33
33
  if @name == 'localhost'
34
- target_data = localhost_defaults(target_data)
34
+ default = { 'config' => { 'transport' => 'local' } }
35
+ target_data = Bolt::Util.deep_merge(default, target_data)
35
36
  end
36
37
 
37
38
  @config = target_data['config'] || {}
@@ -49,18 +50,16 @@ module Bolt
49
50
  validate
50
51
  end
51
52
 
52
- def localhost_defaults(data)
53
+ def set_local_defaults
54
+ return if @set_local_default
53
55
  defaults = {
54
- 'config' => {
55
- 'transport' => 'local',
56
- 'local' => { 'interpreters' => { '.rb' => RbConfig.ruby } }
57
- },
58
- 'features' => ['puppet-agent']
56
+ 'local' => { 'interpreters' => { '.rb' => RbConfig.ruby } }
59
57
  }
60
- data = Bolt::Util.deep_merge(defaults, data)
61
- # If features is an empty array deep_merge won't add the puppet-agent
62
- data['features'] += ['puppet-agent'] if data['features'].empty?
63
- data
58
+ old_config = @config
59
+ @config = Bolt::Util.deep_merge(defaults, @config)
60
+ invalidate_config_cache! if old_config != @config
61
+ set_feature('puppet-agent')
62
+ @set_local_default = true
64
63
  end
65
64
 
66
65
  # rubocop:disable Naming/AccessorMethodName
@@ -5,8 +5,14 @@ require 'logging'
5
5
  module Bolt
6
6
  module Logger
7
7
  LEVELS = %w[trace debug info notice warn error fatal].freeze
8
- @mutex = Mutex.new
9
- @warnings = Set.new
8
+
9
+ # This module is treated as a global singleton so that multiple classes
10
+ # in Bolt can log warnings with IDs. Access to the following variables
11
+ # are controlled by a mutex.
12
+ @mutex = Mutex.new
13
+ @warnings = Set.new
14
+ @disable_warnings = Set.new
15
+ @message_queue = []
10
16
 
11
17
  # This method provides a single point-of-entry to setup logging for both
12
18
  # the CLI and for tests. This is necessary because we define custom log
@@ -36,7 +42,7 @@ module Bolt
36
42
  end
37
43
  end
38
44
 
39
- def self.configure(destinations, color)
45
+ def self.configure(destinations, color, disable_warnings = nil)
40
46
  root_logger = Bolt::Logger.logger(:root)
41
47
 
42
48
  root_logger.add_appenders Logging.appenders.stderr(
@@ -73,6 +79,16 @@ module Bolt
73
79
 
74
80
  appender.level = params[:level] if params[:level]
75
81
  end
82
+
83
+ # Set the list of disabled warnings and mark the logger as configured.
84
+ # Log all messages in the message queue and flush the queue.
85
+ if disable_warnings
86
+ @mutex.synchronize { @disable_warnings = disable_warnings }
87
+ end
88
+ end
89
+
90
+ def self.configured?
91
+ Logging.logger[:root].appenders.any?
76
92
  end
77
93
 
78
94
  # A helper to ensure the Logging library is always initialized with our
@@ -123,18 +139,106 @@ module Bolt
123
139
  Logging.reset
124
140
  end
125
141
 
126
- def self.warn_once(type, msg)
142
+ # The following methods are used in place of the Logging.logger
143
+ # methods of the same name when logging warning messages or logging
144
+ # any messages prior to the logger being configured. If the logger
145
+ # is not configured when any of these methods are called, the message
146
+ # will be added to a queue, otherwise they are logged immediately.
147
+ # The message queue is flushed by calling #flush_queue, which is
148
+ # called from Bolt::CLI after configuring the logger.
149
+ #
150
+ def self.warn(id, msg)
151
+ log(type: :warn, msg: "#{msg} [ID: #{id}]", id: id)
152
+ end
153
+
154
+ def self.warn_once(id, msg)
155
+ log(type: :warn_once, msg: "#{msg} [ID: #{id}]", id: id)
156
+ end
157
+
158
+ def self.deprecate(id, msg)
159
+ log(type: :deprecate, msg: "#{msg} [ID: #{id}]", id: id)
160
+ end
161
+
162
+ def self.deprecate_once(id, msg)
163
+ log(type: :deprecate_once, msg: "#{msg} [ID: #{id}]", id: id)
164
+ end
165
+
166
+ def self.debug(msg)
167
+ log(type: :debug, msg: msg)
168
+ end
169
+
170
+ def self.info(msg)
171
+ log(type: :info, msg: msg)
172
+ end
173
+
174
+ # Logs a message. If the logger has not been configured, this will queue
175
+ # the message to be logged later. Once the logger is configured, the
176
+ # queue will be flushed of all messages and new messages will be logged
177
+ # immediately.
178
+ #
179
+ # Logging with this method is controlled by a mutex, as the Bolt::Logger
180
+ # module is treated as a global singleton to allow multiple classes
181
+ # access to its methods.
182
+ #
183
+ private_class_method def self.log(type:, msg:, id: nil)
184
+ @mutex.synchronize do
185
+ if configured?
186
+ log_message(type: type, msg: msg, id: id)
187
+ else
188
+ @message_queue << { type: type, msg: msg, id: id }
189
+ end
190
+ end
191
+ end
192
+
193
+ # Logs all messages in the message queue and then flushes the queue.
194
+ #
195
+ def self.flush_queue
127
196
  @mutex.synchronize do
128
- @logger ||= Bolt::Logger.logger(self)
129
- if @warnings.add?(type)
130
- @logger.warn(msg)
197
+ @message_queue.each do |message|
198
+ log_message(message)
131
199
  end
200
+
201
+ @message_queue.clear
202
+ end
203
+ end
204
+
205
+ # Handles the actual logging of a message.
206
+ #
207
+ private_class_method def self.log_message(type:, msg:, id: nil)
208
+ case type
209
+ when :warn
210
+ do_warn(msg, id)
211
+ when :warn_once
212
+ do_warn_once(msg, id)
213
+ when :deprecate
214
+ do_deprecate(msg, id)
215
+ when :deprecate_once
216
+ do_deprecate_once(msg, id)
217
+ else
218
+ logger(self).send(type, msg)
132
219
  end
133
220
  end
134
221
 
135
- def self.deprecation_warning(type, msg)
136
- @analytics&.event('Warn', 'deprecation', label: type)
137
- warn_once(type, msg)
222
+ # The following methods do the actual warning.
223
+ #
224
+ private_class_method def self.do_warn(msg, id)
225
+ return if @disable_warnings.include?(id)
226
+ logger(self).warn(msg)
227
+ end
228
+
229
+ private_class_method def self.do_warn_once(msg, id)
230
+ return unless @warnings.add?(id)
231
+ do_warn(msg, id)
232
+ end
233
+
234
+ private_class_method def self.do_deprecate(msg, id)
235
+ @analytics&.event('Warn', 'deprecation', label: id)
236
+ do_warn(msg, id)
237
+ end
238
+
239
+ private_class_method def self.do_deprecate_once(msg, id)
240
+ @analytics&.event('Warn', 'deprecation', label: id)
241
+ do_warn_once(msg, id)
138
242
  end
139
243
  end
140
244
  end