bolt 2.25.0 → 2.26.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/bolt-modules/boltlib/lib/puppet/datatypes/result.rb +2 -1
  3. data/bolt-modules/boltlib/lib/puppet/functions/download_file.rb +1 -1
  4. data/bolt-modules/dir/lib/puppet/functions/dir/children.rb +1 -1
  5. data/lib/bolt/analytics.rb +3 -3
  6. data/lib/bolt/applicator.rb +2 -3
  7. data/lib/bolt/bolt_option_parser.rb +4 -4
  8. data/lib/bolt/catalog.rb +4 -2
  9. data/lib/bolt/cli.rb +6 -16
  10. data/lib/bolt/config.rb +39 -24
  11. data/lib/bolt/config/options.rb +5 -2
  12. data/lib/bolt/executor.rb +1 -1
  13. data/lib/bolt/inventory.rb +8 -1
  14. data/lib/bolt/inventory/group.rb +1 -1
  15. data/lib/bolt/inventory/inventory.rb +1 -1
  16. data/lib/bolt/inventory/target.rb +1 -1
  17. data/lib/bolt/logger.rb +9 -2
  18. data/lib/bolt/outputter/logger.rb +1 -1
  19. data/lib/bolt/pal.rb +4 -10
  20. data/lib/bolt/pal/yaml_plan/evaluator.rb +1 -1
  21. data/lib/bolt/plugin/puppetdb.rb +1 -1
  22. data/lib/bolt/project.rb +28 -17
  23. data/lib/bolt/puppetdb/client.rb +1 -1
  24. data/lib/bolt/puppetdb/config.rb +1 -1
  25. data/lib/bolt/r10k_log_proxy.rb +1 -1
  26. data/lib/bolt/rerun.rb +1 -1
  27. data/lib/bolt/result.rb +8 -0
  28. data/lib/bolt/shell.rb +1 -1
  29. data/lib/bolt/task.rb +1 -1
  30. data/lib/bolt/transport/base.rb +1 -1
  31. data/lib/bolt/transport/docker/connection.rb +1 -1
  32. data/lib/bolt/transport/local/connection.rb +1 -1
  33. data/lib/bolt/transport/ssh.rb +1 -1
  34. data/lib/bolt/transport/ssh/connection.rb +1 -1
  35. data/lib/bolt/transport/ssh/exec_connection.rb +1 -1
  36. data/lib/bolt/transport/winrm.rb +1 -1
  37. data/lib/bolt/transport/winrm/connection.rb +1 -1
  38. data/lib/bolt/util.rb +1 -1
  39. data/lib/bolt/version.rb +1 -1
  40. data/lib/bolt_server/file_cache.rb +1 -1
  41. metadata +4 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bfb8de382e2a2ab6931f43ffce1eb97187fda6e2e56036b20637afb8ae8da2d
4
- data.tar.gz: 56cf9c3fda7f29434413feb8b7f2eb3b358c1ed4316f2d5f4e1e47875a6919c5
3
+ metadata.gz: 195c9ad94788e4811c9a90accb11515fc707346106827e15ef71995b016efbcc
4
+ data.tar.gz: 7259f2a7b9af58dab4b4f8224360fe8f1570e042c43b6becb183a0dd1257d91e
5
5
  SHA512:
6
- metadata.gz: ec5bf195ad19bac051942f2689e58a325982a8c47b12d8970e1ed6a6758e714d8f22ce5275dc3fb7160ea6d06e7ee63bf528b2f18d67ee754b9ae41c3e92a7c5
7
- data.tar.gz: cc6a87ed720484d04c7bb2275cd6812ac211733e2efce265b69cfb5ee5614fa743122ce50369079697ad08374eccea6e9797e8398176d7669846887b0f0832aa
6
+ metadata.gz: 2092172d760e7a806e865a3a1315ec2687cff1835b8b44addec9de208711a16be2193ff570a9cae65a620b6d9203575ccb64c2798c71509dd2fc1a0d41ca3399
7
+ data.tar.gz: 77828dd923626abe08c0bbf8ef0f30175462aab55a8584127359dde412f37b17d7cd952a4bd1be2ce115f47db4f8d6f4269eb455f063ff01ee1297a4637c03c8
@@ -9,11 +9,12 @@ Puppet::DataTypes.create_type('Result') do
9
9
  functions => {
10
10
  error => Callable[[], Optional[Error]],
11
11
  message => Callable[[], Optional[String]],
12
+ sensitive => Callable[[], Optional[Sensitive[Data]]],
12
13
  action => Callable[[], String],
13
14
  status => Callable[[], String],
14
15
  to_data => Callable[[], Hash],
15
16
  ok => Callable[[], Boolean],
16
- '[]' => Callable[[String[1]], Data]
17
+ '[]' => Callable[[String[1]], Variant[Data, Sensitive[Data]]]
17
18
  }
18
19
  PUPPET
19
20
 
@@ -96,7 +96,7 @@ Puppet::Functions.create_function(:download_file, Puppet::Functions::InternalFun
96
96
 
97
97
  # Paths expand relative to the default downloads directory for the project
98
98
  # e.g. ~/.puppetlabs/bolt/downloads/
99
- destination = Puppet.lookup(:bolt_project_data).downloads + destination
99
+ destination = Puppet.lookup(:bolt_project).downloads + destination
100
100
 
101
101
  # If the destination directory already exists, delete any existing contents
102
102
  if Dir.exist?(destination)
@@ -25,7 +25,7 @@ Puppet::Functions.create_function(:'dir::children', Puppet::Functions::InternalF
25
25
  full_mod_path = File.join(mod_path, subpath || '') if mod_path
26
26
 
27
27
  # Expand relative to the project directory if path is relative
28
- project = Puppet.lookup(:bolt_project_data)
28
+ project = Puppet.lookup(:bolt_project)
29
29
  pathname = Pathname.new(dirname)
30
30
  full_dir = pathname.absolute? ? dirname : File.expand_path(File.join(project.path, dirname))
31
31
 
@@ -27,7 +27,7 @@ module Bolt
27
27
  }.freeze
28
28
 
29
29
  def self.build_client
30
- logger = Logging.logger[self]
30
+ logger = Bolt::Logger.logger(self)
31
31
  begin
32
32
  config_file = config_path(logger)
33
33
  config = load_config(config_file, logger)
@@ -106,7 +106,7 @@ module Bolt
106
106
  require 'httpclient'
107
107
  require 'locale'
108
108
 
109
- @logger = Logging.logger[self]
109
+ @logger = Bolt::Logger.logger(self)
110
110
  @http = HTTPClient.new
111
111
  @user_id = user_id
112
112
  @executor = Concurrent.global_io_executor
@@ -210,7 +210,7 @@ module Bolt
210
210
  attr_accessor :bundled_content
211
211
 
212
212
  def initialize
213
- @logger = Logging.logger[self]
213
+ @logger = Bolt::Logger.logger(self)
214
214
  @bundled_content = []
215
215
  end
216
216
 
@@ -28,7 +28,7 @@ module Bolt
28
28
  @apply_settings = apply_settings || {}
29
29
 
30
30
  @pool = Concurrent::ThreadPoolExecutor.new(name: 'apply', max_threads: max_compiles)
31
- @logger = Logging.logger[self]
31
+ @logger = Bolt::Logger.logger(self)
32
32
  end
33
33
 
34
34
  private def libexec
@@ -183,11 +183,10 @@ module Bolt
183
183
  type_by_reference: true,
184
184
  local_reference: true)
185
185
 
186
- bolt_project = @project if @project&.name
187
186
  scope = {
188
187
  code_ast: ast,
189
188
  modulepath: @modulepath,
190
- project: bolt_project.to_h,
189
+ project: @project.to_h,
191
190
  pdb_config: @pdb_client.config.to_hash,
192
191
  hiera_config: @hiera_config,
193
192
  plan_vars: plan_vars,
@@ -366,11 +366,11 @@ module Bolt
366
366
  bolt plan convert <path> [options]
367
367
 
368
368
  DESCRIPTION
369
- Convert a YAML plan to a Bolt plan.
369
+ Convert a YAML plan to a Puppet language plan and print the converted plan to stdout.
370
370
 
371
371
  Converting a YAML plan may result in a plan that is syntactically
372
372
  correct but has different behavior. Always verify a converted plan's
373
- functionality.
373
+ functionality. Note that the converted plan is not written to a file.
374
374
 
375
375
  EXAMPLES
376
376
  bolt plan convert path/to/plan/myplan.yaml
@@ -421,9 +421,9 @@ module Bolt
421
421
  the plan, including a list of available parameters.
422
422
 
423
423
  EXAMPLES
424
- Display a list of available tasks
424
+ Display a list of available plans
425
425
  bolt plan show
426
- Display documentation for the canary task
426
+ Display documentation for the aggregate::count plan
427
427
  bolt plan show aggregate::count
428
428
  HELP
429
429
 
@@ -57,8 +57,10 @@ module Bolt
57
57
 
58
58
  def compile_catalog(request)
59
59
  pdb_client = Bolt::PuppetDB::Client.new(Bolt::PuppetDB::Config.new(request['pdb_config']))
60
- project = request['project'] || {}
61
- bolt_project = Struct.new(:name, :path).new(project['name'], project['path']) unless project.empty?
60
+ project = request['project']
61
+ bolt_project = Struct.new(:name, :path, :load_as_module?).new(project['name'],
62
+ project['path'],
63
+ project['load_as_module?'])
62
64
  inv = Bolt::ApplyInventory.new(request['config'])
63
65
  puppet_overrides = {
64
66
  bolt_pdb_client: pdb_client,
@@ -46,7 +46,7 @@ module Bolt
46
46
 
47
47
  def initialize(argv)
48
48
  Bolt::Logger.initialize_logging
49
- @logger = Logging.logger[self]
49
+ @logger = Bolt::Logger.logger(self)
50
50
  @argv = argv
51
51
  @options = {}
52
52
  end
@@ -79,7 +79,7 @@ module Bolt
79
79
 
80
80
  # Wrapper method that is called by the Bolt executable. Parses the command and
81
81
  # then loads the project and config. Once config is loaded, it completes the
82
- # setup process by configuring Bolt and issuing warnings.
82
+ # setup process by configuring Bolt and logging messages.
83
83
  #
84
84
  # This separation is needed since the Bolt::Outputter class that normally handles
85
85
  # printing errors relies on config being loaded. All setup that happens before
@@ -167,20 +167,17 @@ module Bolt
167
167
  raise e
168
168
  end
169
169
 
170
- # Completes the setup process by configuring Bolt and issuing warnings
170
+ # Completes the setup process by configuring Bolt and log messages
171
171
  def finalize_setup
172
172
  Bolt::Logger.configure(config.log, config.color)
173
173
  Bolt::Logger.analytics = analytics
174
174
 
175
- # Logger must be configured before checking path case and project file, otherwise warnings will not display
175
+ # Logger must be configured before checking path case and project file, otherwise logs will not display
176
176
  config.check_path_case('modulepath', config.modulepath)
177
177
  config.project.check_deprecated_file
178
178
 
179
- # Log the file paths for loaded config files
180
- config_loaded
181
-
182
- # Display warnings created during parser and config initialization
183
- config.warnings.each { |warning| @logger.warn(warning[:msg]) }
179
+ # Log messages created during parser and config initialization
180
+ config.logs.each { |log| @logger.send(log.keys[0], log.values[0]) }
184
181
  @parser_deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
185
182
  config.deprecations.each { |dep| Bolt::Logger.deprecation_warning(dep[:type], dep[:msg]) }
186
183
 
@@ -1057,13 +1054,6 @@ module Bolt
1057
1054
  content
1058
1055
  end
1059
1056
 
1060
- def config_loaded
1061
- msg = <<~MSG.chomp
1062
- Loaded configuration from: '#{config.config_files.join("', '")}'
1063
- MSG
1064
- @logger.info(msg)
1065
- end
1066
-
1067
1057
  # Gem installs include the aggregate, canary, and puppetdb_fact modules, while
1068
1058
  # package installs include modules listed in the Bolt repo Puppetfile
1069
1059
  def incomplete_install?
@@ -19,7 +19,7 @@ module Bolt
19
19
  class Config
20
20
  include Bolt::Config::Options
21
21
 
22
- attr_reader :config_files, :warnings, :data, :transports, :project, :modified_concurrency, :deprecations
22
+ attr_reader :config_files, :logs, :data, :transports, :project, :modified_concurrency, :deprecations
23
23
 
24
24
  BOLT_CONFIG_NAME = 'bolt.yaml'
25
25
  BOLT_DEFAULTS_NAME = 'bolt-defaults.yaml'
@@ -32,16 +32,19 @@ module Bolt
32
32
  end
33
33
 
34
34
  def self.from_project(project, overrides = {})
35
+ logs = []
35
36
  conf = if project.project_file == project.config_file
36
37
  project.data
37
38
  else
38
- Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
39
+ c = Bolt::Util.read_optional_yaml_hash(project.config_file, 'config')
40
+ logs << { debug: "Loaded configuration from #{project.config_file}" } if File.exist?(project.config_file)
41
+ c
39
42
  end
40
43
 
41
44
  data = load_defaults(project).push(
42
45
  filepath: project.config_file,
43
46
  data: conf,
44
- warnings: [],
47
+ logs: logs,
45
48
  deprecations: []
46
49
  )
47
50
 
@@ -50,17 +53,20 @@ module Bolt
50
53
 
51
54
  def self.from_file(configfile, overrides = {})
52
55
  project = Bolt::Project.create_project(Pathname.new(configfile).expand_path.dirname)
56
+ logs = []
53
57
 
54
58
  conf = if project.project_file == project.config_file
55
59
  project.data
56
60
  else
57
- Bolt::Util.read_yaml_hash(configfile, 'config')
61
+ c = Bolt::Util.read_yaml_hash(configfile, 'config')
62
+ logs << { debug: "Loaded configuration from #{configfile}" }
63
+ c
58
64
  end
59
65
 
60
66
  data = load_defaults(project).push(
61
67
  filepath: project.config_file,
62
68
  data: conf,
63
- warnings: [],
69
+ logs: logs,
64
70
  deprecations: []
65
71
  )
66
72
 
@@ -90,13 +96,13 @@ module Bolt
90
96
  def self.load_bolt_defaults_yaml(dir)
91
97
  filepath = dir + BOLT_DEFAULTS_NAME
92
98
  data = Bolt::Util.read_yaml_hash(filepath, 'config')
93
- warnings = []
99
+ logs = [{ debug: "Loaded configuration from #{filepath}" }]
94
100
 
95
101
  # Warn if 'bolt.yaml' detected in same directory.
96
102
  if File.exist?(bolt_yaml = dir + BOLT_CONFIG_NAME)
97
- warnings.push(
98
- msg: "Detected multiple configuration files: ['#{bolt_yaml}', '#{filepath}']. '#{bolt_yaml}' "\
99
- "will be ignored."
103
+ logs.push(
104
+ warn: "Detected multiple configuration files: ['#{bolt_yaml}', '#{filepath}']. '#{bolt_yaml}' "\
105
+ "will be ignored."
100
106
  )
101
107
  end
102
108
 
@@ -105,9 +111,9 @@ module Bolt
105
111
 
106
112
  if project_config.any?
107
113
  data.reject! { |key, _| project_config.include?(key) }
108
- warnings.push(
109
- msg: "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
110
- "Project configuration should be set in 'bolt-project.yaml'."
114
+ logs.push(
115
+ warn: "Unsupported project configuration detected in '#{filepath}': #{project_config.keys}. "\
116
+ "Project configuration should be set in 'bolt-project.yaml'."
111
117
  )
112
118
  end
113
119
 
@@ -116,10 +122,10 @@ module Bolt
116
122
 
117
123
  if transport_config.any?
118
124
  data.reject! { |key, _| transport_config.include?(key) }
119
- warnings.push(
120
- msg: "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
121
- "Transport configuration should be set under the 'inventory-config' option or "\
122
- "in 'inventory.yaml'."
125
+ logs.push(
126
+ warn: "Unsupported inventory configuration detected in '#{filepath}': #{transport_config.keys}. "\
127
+ "Transport configuration should be set under the 'inventory-config' option or "\
128
+ "in 'inventory.yaml'."
123
129
  )
124
130
  end
125
131
 
@@ -142,7 +148,7 @@ module Bolt
142
148
  data = data.merge(data.delete('inventory-config'))
143
149
  end
144
150
 
145
- { filepath: filepath, data: data, warnings: warnings, deprecations: [] }
151
+ { filepath: filepath, data: data, logs: logs, deprecations: [] }
146
152
  end
147
153
 
148
154
  # Loads a 'bolt.yaml' file, the legacy configuration file. There's no special munging needed
@@ -150,11 +156,12 @@ module Bolt
150
156
  def self.load_bolt_yaml(dir)
151
157
  filepath = dir + BOLT_CONFIG_NAME
152
158
  data = Bolt::Util.read_yaml_hash(filepath, 'config')
159
+ logs = [{ debug: "Loaded configuration from #{filepath}" }]
153
160
  deprecations = [{ type: 'Using bolt.yaml for system configuration',
154
161
  msg: "Configuration file #{filepath} is deprecated and will be removed in a future version "\
155
162
  "of Bolt. Use '#{dir + BOLT_DEFAULTS_NAME}' instead." }]
156
163
 
157
- { filepath: filepath, data: data, warnings: [], deprecations: deprecations }
164
+ { filepath: filepath, data: data, logs: logs, deprecations: deprecations }
158
165
  end
159
166
 
160
167
  def self.load_defaults(project)
@@ -187,13 +194,13 @@ module Bolt
187
194
  unless config_data.is_a?(Array)
188
195
  config_data = [{ filepath: project.config_file,
189
196
  data: config_data,
190
- warnings: [],
197
+ logs: [],
191
198
  deprecations: [] }]
192
199
  end
193
200
 
194
- @logger = Logging.logger[self]
201
+ @logger = Bolt::Logger.logger(self)
195
202
  @project = project
196
- @warnings = @project.warnings.dup
203
+ @logs = @project.logs.dup
197
204
  @deprecations = @project.deprecations.dup
198
205
  @transports = {}
199
206
  @config_files = []
@@ -221,7 +228,7 @@ module Bolt
221
228
  end
222
229
 
223
230
  loaded_data = config_data.each_with_object([]) do |data, acc|
224
- @warnings.concat(data[:warnings]) if data[:warnings].any?
231
+ @logs.concat(data[:logs]) if data[:logs].any?
225
232
  @deprecations.concat(data[:deprecations]) if data[:deprecations].any?
226
233
 
227
234
  if data[:data].any?
@@ -338,9 +345,17 @@ module Bolt
338
345
 
339
346
  private def update_logs(logs)
340
347
  logs.each_with_object({}) do |(key, val), acc|
341
- next unless val.is_a?(Hash)
348
+ # Remove any disabled logs
349
+ next if val == 'disable'
342
350
 
343
351
  name = normalize_log(key)
352
+
353
+ # But otherwise it has to be a Hash
354
+ unless val.is_a?(Hash)
355
+ raise Bolt::ValidationError,
356
+ "config of log #{name} must be a Hash, received #{val.class} #{val.inspect}"
357
+ end
358
+
344
359
  acc[name] = val.slice('append', 'level')
345
360
  .transform_keys(&:to_sym)
346
361
 
@@ -365,7 +380,7 @@ module Bolt
365
380
  def validate
366
381
  if @data['future']
367
382
  msg = "Configuration option 'future' no longer exposes future behavior."
368
- @warnings << { option: 'future', msg: msg }
383
+ @logs << { warn: msg }
369
384
  end
370
385
 
371
386
  keys = OPTIONS.keys - %w[plugins plugin_hooks puppetdb]
@@ -176,7 +176,9 @@ module Bolt
176
176
  description: "A map of configuration for the logfile output. Under `log`, you can configure log options "\
177
177
  "for `console` and add configuration for individual log files, such as "\
178
178
  "`~/.puppetlabs/bolt/debug.log`. Individual log files must be valid filepaths. If the log "\
179
- "file does not exist, then Bolt will create it before logging information.",
179
+ "file does not exist, then Bolt will create it before logging information. Set the value to "\
180
+ "`disable` to remove a log file defined at an earlier level of the config hierarchy. By "\
181
+ "default, Bolt logs to a bolt-debug.log file in the Bolt project directory.",
180
182
  type: Hash,
181
183
  properties: {
182
184
  "console" => {
@@ -194,7 +196,8 @@ module Bolt
194
196
  },
195
197
  additionalProperties: {
196
198
  description: "Configuration for the logfile output.",
197
- type: Hash,
199
+ type: [String, Hash],
200
+ enum: ['disable'],
198
201
  properties: {
199
202
  "append" => {
200
203
  description: "Whether to append output to an existing log file.",
@@ -40,7 +40,7 @@ module Bolt
40
40
  require 'concurrent'
41
41
 
42
42
  @analytics = analytics
43
- @logger = Logging.logger[self]
43
+ @logger = Bolt::Logger.logger(self)
44
44
 
45
45
  @transports = Bolt::TRANSPORTS.each_with_object({}) do |(key, val), coll|
46
46
  coll[key.to_s] = if key == :remote
@@ -46,10 +46,13 @@ module Bolt
46
46
  end
47
47
 
48
48
  def self.from_config(config, plugins)
49
+ logger = Logging.logger[self]
50
+
49
51
  if ENV.include?(ENVIRONMENT_VAR)
50
52
  begin
51
53
  data = YAML.safe_load(ENV[ENVIRONMENT_VAR])
52
54
  raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}" unless data.is_a?(Hash)
55
+ logger.debug("Loaded inventory from environment variable #{ENVIRONMENT_VAR}")
53
56
  rescue Psych::Exception
54
57
  raise Bolt::ParseError, "Could not parse inventory from $#{ENVIRONMENT_VAR}"
55
58
  end
@@ -57,8 +60,12 @@ module Bolt
57
60
  data = if config.inventoryfile
58
61
  Bolt::Util.read_yaml_hash(config.inventoryfile, 'inventory')
59
62
  else
60
- Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
63
+ i = Bolt::Util.read_optional_yaml_hash(config.default_inventoryfile, 'inventory')
64
+ logger.debug("Loaded inventory from #{config.default_inventoryfile}") if i
65
+ i
61
66
  end
67
+ # This avoids rubocop complaining about identical conditionals
68
+ logger.debug("Loaded inventory from #{config.inventoryfile}") if config.inventoryfile
62
69
  end
63
70
 
64
71
  # Resolve plugin references from transport config
@@ -19,7 +19,7 @@ module Bolt
19
19
  CONFIG_KEYS = Bolt::Config::INVENTORY_OPTIONS.keys
20
20
 
21
21
  def initialize(input, plugins)
22
- @logger = Logging.logger[self]
22
+ @logger = Bolt::Logger.logger(self)
23
23
  @plugins = plugins
24
24
 
25
25
  input = @plugins.resolve_top_level_references(input) if @plugins.reference?(input)
@@ -16,7 +16,7 @@ module Bolt
16
16
 
17
17
  # TODO: Pass transport config instead of config object
18
18
  def initialize(data, transport, transports, plugins)
19
- @logger = Logging.logger[self]
19
+ @logger = Bolt::Logger.logger(self)
20
20
  @data = data || {}
21
21
  @transport = transport
22
22
  @config = transports
@@ -13,7 +13,7 @@ module Bolt
13
13
  raise Bolt::Inventory::ValidationError.new("Target must have either a name or uri", nil)
14
14
  end
15
15
 
16
- @logger = Logging.logger[inventory]
16
+ @logger = Bolt::Logger.logger(inventory)
17
17
 
18
18
  # If the target isn't mentioned by any groups, it won't have a uri or
19
19
  # name and we will use the target_name as both
@@ -28,7 +28,7 @@ module Bolt
28
28
  end
29
29
 
30
30
  def self.configure(destinations, color)
31
- root_logger = Logging.logger[:root]
31
+ root_logger = Bolt::Logger.logger(:root)
32
32
 
33
33
  root_logger.add_appenders Logging.appenders.stderr(
34
34
  'console',
@@ -66,6 +66,13 @@ module Bolt
66
66
  end
67
67
  end
68
68
 
69
+ # A helper to ensure the Logging library is always initialized with our
70
+ # custom log levels before retrieving a Logger instance.
71
+ def self.logger(name)
72
+ initialize_logging
73
+ Logging.logger[name]
74
+ end
75
+
69
76
  def self.analytics=(analytics)
70
77
  @analytics = analytics
71
78
  end
@@ -110,7 +117,7 @@ module Bolt
110
117
  def self.warn_once(type, msg)
111
118
  @mutex.synchronize {
112
119
  @warnings ||= []
113
- @logger ||= Logging.logger[self]
120
+ @logger ||= Bolt::Logger.logger(self)
114
121
  unless @warnings.include?(type)
115
122
  @logger.warn(msg)
116
123
  @warnings << type
@@ -7,7 +7,7 @@ module Bolt
7
7
  class Logger < Bolt::Outputter
8
8
  def initialize(verbose, trace)
9
9
  super(false, verbose, trace)
10
- @logger = Logging.logger[self]
10
+ @logger = Bolt::Logger.logger(self)
11
11
  end
12
12
 
13
13
  def handle_event(event)
@@ -65,7 +65,7 @@ module Bolt
65
65
  @resource_types = resource_types
66
66
  @project = project
67
67
 
68
- @logger = Logging.logger[self]
68
+ @logger = Bolt::Logger.logger(self)
69
69
  if modulepath && !modulepath.empty?
70
70
  @logger.debug("Loading modules from #{@modulepath.join(File::PATH_SEPARATOR)}")
71
71
  end
@@ -76,7 +76,7 @@ module Bolt
76
76
  # Puppet logging is global so this is class method to avoid confusion
77
77
  def self.configure_logging
78
78
  Puppet::Util::Log.destinations.clear
79
- Puppet::Util::Log.newdestination(Logging.logger['Puppet'])
79
+ Puppet::Util::Log.newdestination(Bolt::Logger.logger('Puppet'))
80
80
  # Defer all log level decisions to the Logging library by telling Puppet
81
81
  # to log everything
82
82
  Puppet.settings[:log_level] = 'debug'
@@ -149,15 +149,9 @@ module Bolt
149
149
  setup
150
150
  r = Puppet::Pal.in_tmp_environment('bolt', modulepath: @modulepath, facts: {}) do |pal|
151
151
  # Only load the project if it a) exists, b) has a name it can be loaded with
152
- bolt_project = @project if @project&.name
153
- # Puppet currently won't receive the project unless it is a named project. Since
154
- # the download_file plan function needs access to the project path, add it to the
155
- # context.
156
- bolt_project_data = @project
157
- Puppet.override(bolt_project: bolt_project,
158
- bolt_project_data: bolt_project_data,
152
+ Puppet.override(bolt_project: @project,
159
153
  yaml_plan_instantiator: Bolt::PAL::YamlPlan::Loader) do
160
- pal.with_script_compiler do |compiler|
154
+ pal.with_script_compiler(set_local_facts: false) do |compiler|
161
155
  alias_types(compiler)
162
156
  register_resource_types(Puppet.lookup(:loaders)) if @resource_types
163
157
  begin
@@ -7,7 +7,7 @@ module Bolt
7
7
  class YamlPlan
8
8
  class Evaluator
9
9
  def initialize(analytics = Bolt::Analytics::NoopClient.new)
10
- @logger = Logging.logger[self]
10
+ @logger = Bolt::Logger.logger(self)
11
11
  @analytics = analytics
12
12
  @evaluator = Puppet::Pops::Parser::EvaluatingParser.new
13
13
  end
@@ -19,7 +19,7 @@ module Bolt
19
19
  def initialize(config:, context:)
20
20
  pdb_config = Bolt::PuppetDB::Config.load_config(config, context.boltdir)
21
21
  @puppetdb_client = Bolt::PuppetDB::Client.new(pdb_config)
22
- @logger = Logging.logger[self]
22
+ @logger = Bolt::Logger.logger(self)
23
23
  end
24
24
 
25
25
  def name
@@ -17,35 +17,37 @@ module Bolt
17
17
  }.freeze
18
18
 
19
19
  attr_reader :path, :data, :config_file, :inventory_file, :modulepath, :hiera_config,
20
- :puppetfile, :rerunfile, :type, :resource_types, :warnings, :project_file,
20
+ :puppetfile, :rerunfile, :type, :resource_types, :logs, :project_file,
21
21
  :deprecations, :downloads, :plans_path
22
22
 
23
- def self.default_project
24
- create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
23
+ def self.default_project(logs = [])
24
+ create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user', logs)
25
25
  # If homedir isn't defined use the system config path
26
26
  rescue ArgumentError
27
- create_project(Bolt::Config.system_path, 'system')
27
+ create_project(Bolt::Config.system_path, 'system', logs)
28
28
  end
29
29
 
30
30
  # Search recursively up the directory hierarchy for the Project. Look for a
31
31
  # directory called Boltdir or a file called bolt.yaml (for a control repo
32
32
  # type Project). Otherwise, repeat the check on each directory up the
33
33
  # hierarchy, falling back to the default if we reach the root.
34
- def self.find_boltdir(dir)
34
+ def self.find_boltdir(dir, logs = [])
35
35
  dir = Pathname.new(dir)
36
36
 
37
37
  if (dir + BOLTDIR_NAME).directory?
38
- create_project(dir + BOLTDIR_NAME, 'embedded')
38
+ create_project(dir + BOLTDIR_NAME, 'embedded', logs)
39
39
  elsif (dir + 'bolt.yaml').file? || (dir + 'bolt-project.yaml').file?
40
- create_project(dir, 'local')
40
+ create_project(dir, 'local', logs)
41
41
  elsif dir.root?
42
- default_project
42
+ default_project(logs)
43
43
  else
44
- find_boltdir(dir.parent)
44
+ logs << { debug: "Did not detect Boltdir, bolt.yaml, or bolt-project.yaml at '#{dir}'. "\
45
+ "This directory won't be loaded as a project." }
46
+ find_boltdir(dir.parent, logs)
45
47
  end
46
48
  end
47
49
 
48
- def self.create_project(path, type = 'option')
50
+ def self.create_project(path, type = 'option', logs = [])
49
51
  fullpath = Pathname.new(path).expand_path
50
52
 
51
53
  if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
@@ -58,15 +60,18 @@ module Bolt
58
60
 
59
61
  project_file = File.join(fullpath, 'bolt-project.yaml')
60
62
  data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
61
- new(data, path, type)
63
+ default = type =~ /user|system/ ? 'default ' : ''
64
+ exist = File.exist?(File.expand_path(project_file))
65
+ logs << { info: "Loaded #{default}project from '#{fullpath}'" } if exist
66
+ new(data, path, type, logs)
62
67
  end
63
68
 
64
- def initialize(raw_data, path, type = 'option')
69
+ def initialize(raw_data, path, type = 'option', logs = [])
65
70
  @path = Pathname.new(path).expand_path
66
71
 
67
72
  @project_file = @path + 'bolt-project.yaml'
68
73
 
69
- @warnings = []
74
+ @logs = logs
70
75
  @deprecations = []
71
76
  if (@path + 'bolt.yaml').file? && project_file?
72
77
  msg = "Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
@@ -88,7 +93,7 @@ module Bolt
88
93
  tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
89
94
  if tc.any?
90
95
  msg = "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}"
91
- @warnings << { msg: msg }
96
+ @logs << { warn: msg }
92
97
  end
93
98
 
94
99
  @data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }
@@ -98,7 +103,7 @@ module Bolt
98
103
  @config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
99
104
  if (@path + 'bolt.yaml').file?
100
105
  msg = "bolt-project.yaml contains valid config keys, bolt.yaml will be ignored"
101
- @warnings << { msg: msg }
106
+ @logs << { warn: msg }
102
107
  end
103
108
  @project_file
104
109
  else
@@ -114,7 +119,9 @@ module Bolt
114
119
  # This API is used to prepend the project as a module to Puppet's internal
115
120
  # module_references list. CHANGE AT YOUR OWN RISK
116
121
  def to_h
117
- { path: @path.to_s, name: name }
122
+ { path: @path.to_s,
123
+ name: name,
124
+ load_as_module?: load_as_module? }
118
125
  end
119
126
 
120
127
  def eql?(other)
@@ -126,6 +133,10 @@ module Bolt
126
133
  @project_file.file?
127
134
  end
128
135
 
136
+ def load_as_module?
137
+ !name.nil?
138
+ end
139
+
129
140
  def name
130
141
  @data['name']
131
142
  end
@@ -151,7 +162,7 @@ module Bolt
151
162
  end
152
163
  else
153
164
  message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
154
- @warnings << { msg: message }
165
+ @logs << { warn: message }
155
166
  end
156
167
 
157
168
  %w[tasks plans].each do |conf|
@@ -13,7 +13,7 @@ module Bolt
13
13
  @config = config
14
14
  @bad_urls = []
15
15
  @current_url = nil
16
- @logger = Logging.logger[self]
16
+ @logger = Bolt::Logger.logger(self)
17
17
  end
18
18
 
19
19
  def query_certnames(query)
@@ -35,7 +35,7 @@ module Bolt
35
35
  begin
36
36
  config = JSON.parse(File.read(filepath)) if filepath
37
37
  rescue StandardError => e
38
- Logging.logger[self].error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
38
+ Bolt::Logger.logger(self).error("Could not load puppetdb.conf from #{filepath}: #{e.message}")
39
39
  end
40
40
 
41
41
  config = config.fetch('puppetdb', {})
@@ -7,7 +7,7 @@ module Bolt
7
7
  def initialize
8
8
  super('bolt')
9
9
 
10
- @logger = Logging.logger[self]
10
+ @logger = Bolt::Logger.logger(self)
11
11
  end
12
12
 
13
13
  def canonical_log(event)
@@ -8,7 +8,7 @@ module Bolt
8
8
  def initialize(path, save_failures)
9
9
  @path = path
10
10
  @save_failures = save_failures
11
- @logger = Logging.logger[self]
11
+ @logger = Bolt::Logger.logger(self)
12
12
  end
13
13
 
14
14
  def data
@@ -65,6 +65,10 @@ module Bolt
65
65
  'msg' => msg,
66
66
  'details' => { 'exit_code' => exit_code } }
67
67
  end
68
+
69
+ if value.key?('_sensitive')
70
+ value['_sensitive'] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(value['_sensitive'])
71
+ end
68
72
  new(target, value: value, action: 'task', object: task)
69
73
  end
70
74
 
@@ -205,5 +209,9 @@ module Bolt
205
209
 
206
210
  end
207
211
  end
212
+
213
+ def sensitive
214
+ value['_sensitive']
215
+ end
208
216
  end
209
217
  end
@@ -7,7 +7,7 @@ module Bolt
7
7
  def initialize(target, conn)
8
8
  @target = target
9
9
  @conn = conn
10
- @logger = Logging.logger[@target.safe_name]
10
+ @logger = Bolt::Logger.logger(@target.safe_name)
11
11
  end
12
12
 
13
13
  def run_command(*_args)
@@ -26,7 +26,7 @@ module Bolt
26
26
  @metadata = metadata
27
27
  @files = files
28
28
  @remote = remote
29
- @logger = Logging.logger[self]
29
+ @logger = Bolt::Logger.logger(self)
30
30
 
31
31
  validate_metadata
32
32
  end
@@ -40,7 +40,7 @@ module Bolt
40
40
  attr_reader :logger
41
41
 
42
42
  def initialize
43
- @logger = Logging.logger[self]
43
+ @logger = Bolt::Logger.logger(self)
44
44
  end
45
45
 
46
46
  def with_events(target, callback, action)
@@ -10,7 +10,7 @@ module Bolt
10
10
  def initialize(target)
11
11
  raise Bolt::ValidationError, "Target #{target.safe_name} does not have a host" unless target.host
12
12
  @target = target
13
- @logger = Logging.logger[target.safe_name]
13
+ @logger = Bolt::Logger.logger(target.safe_name)
14
14
  @docker_host = @target.options['service-url']
15
15
  @logger.trace("Initializing docker connection to #{@target.safe_name}")
16
16
  end
@@ -16,7 +16,7 @@ module Bolt
16
16
  @target = target
17
17
  # The familiar problem: Etc.getlogin is broken on osx
18
18
  @user = ENV['USER'] || Etc.getlogin
19
- @logger = Logging.logger[self]
19
+ @logger = Bolt::Logger.logger(self)
20
20
  end
21
21
 
22
22
  def shell
@@ -17,7 +17,7 @@ module Bolt
17
17
  rescue LoadError
18
18
  logger.debug("Authentication method 'gssapi-with-mic' (Kerberos) is not available.")
19
19
  end
20
- @transport_logger = Logging.logger[Net::SSH]
20
+ @transport_logger = Bolt::Logger.logger(Net::SSH)
21
21
  @transport_logger.level = :warn
22
22
  end
23
23
 
@@ -26,7 +26,7 @@ module Bolt
26
26
  @user = @target.user || ssh_config[:user] || Etc.getlogin
27
27
  @strict_host_key_checking = ssh_config[:strict_host_key_checking]
28
28
 
29
- @logger = Logging.logger[@target.safe_name]
29
+ @logger = Bolt::Logger.logger(@target.safe_name)
30
30
  @transport_logger = transport_logger
31
31
  @logger.trace("Initializing ssh connection to #{@target.safe_name}")
32
32
 
@@ -14,7 +14,7 @@ module Bolt
14
14
  @target = target
15
15
  ssh_config = Net::SSH::Config.for(target.host)
16
16
  @user = @target.user || ssh_config[:user] || Etc.getlogin
17
- @logger = Logging.logger[self]
17
+ @logger = Bolt::Logger.logger(self)
18
18
  end
19
19
 
20
20
  # This is used to verify we can connect to targets with `connected?`
@@ -11,7 +11,7 @@ module Bolt
11
11
  require 'winrm'
12
12
  require 'winrm-fs'
13
13
 
14
- @transport_logger = Logging.logger[::WinRM]
14
+ @transport_logger = Bolt::Logger.logger(::WinRM)
15
15
  @transport_logger.level = :warn
16
16
  end
17
17
 
@@ -18,7 +18,7 @@ module Bolt
18
18
  @user = @target.user
19
19
  # Build set of extensions from extensions config as well as interpreters
20
20
 
21
- @logger = Logging.logger[@target.safe_name]
21
+ @logger = Bolt::Logger.logger(@target.safe_name)
22
22
  logger.trace("Initializing winrm connection to #{@target.safe_name}")
23
23
  @transport_logger = transport_logger
24
24
  end
@@ -6,7 +6,7 @@ module Bolt
6
6
  def read_yaml_hash(path, file_name)
7
7
  require 'yaml'
8
8
 
9
- logger = Logging.logger[self]
9
+ logger = Bolt::Logger.logger(self)
10
10
  path = File.expand_path(path)
11
11
  content = File.open(path, "r:UTF-8") { |f| YAML.safe_load(f.read) } || {}
12
12
  unless content.is_a?(Hash)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.25.0'
4
+ VERSION = '2.26.0'
5
5
  end
@@ -33,7 +33,7 @@ module BoltServer
33
33
  @executor = executor
34
34
  @cache_dir = config['cache-dir']
35
35
  @config = config
36
- @logger = Logging.logger[self]
36
+ @logger = Bolt::Logger.logger(self)
37
37
  @cache_dir_mutex = cache_dir_mutex
38
38
 
39
39
  if do_purge
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.25.0
4
+ version: 2.26.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-08-26 00:00:00.000000000 Z
11
+ date: 2020-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -182,20 +182,14 @@ dependencies:
182
182
  name: puppet
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
- - - ">="
186
- - !ruby/object:Gem::Version
187
- version: 6.16.0
188
- - - "<"
185
+ - - '='
189
186
  - !ruby/object:Gem::Version
190
187
  version: 6.18.0
191
188
  type: :runtime
192
189
  prerelease: false
193
190
  version_requirements: !ruby/object:Gem::Requirement
194
191
  requirements:
195
- - - ">="
196
- - !ruby/object:Gem::Version
197
- version: 6.16.0
198
- - - "<"
192
+ - - '='
199
193
  - !ruby/object:Gem::Version
200
194
  version: 6.18.0
201
195
  - !ruby/object:Gem::Dependency