bolt 2.22.0 → 2.23.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 60e995704052fee2778474c532df844e51ca0888017e28c883866e6b61a9678f
4
- data.tar.gz: 8133b33137d67ba8880270f4ed20aa8346e13b2aa7ae2b0c390e3fbbba660172
3
+ metadata.gz: f58ca99a18fca1c109d531c61b55bcee45d73c14e55a074575ffaaf1d9f40aac
4
+ data.tar.gz: ea55e4c9b9ba4ccd8afed70a6c62c603a119a46f406b573f429bd2cd89983672
5
5
  SHA512:
6
- metadata.gz: a057aebe5bd7ce01a8369f3c7a05371f0dab79143cd465f6bd2f0a0ad23fe897fcff6624d03f07ead07b86c888512166232182c294cd1188fcd8fb45b55f463d
7
- data.tar.gz: 4c30348d86636398f25568dcd77af25e6eb8e54af5c9ce44125a110b8089c15415a4a051aa8aac066f7404f9ca7faf253dd69e350780ef8d24e34f385c1dd8de
6
+ metadata.gz: dc8845ebe2a171ce79b704c474594a9679e7078feaca324eb7f26b88e9e903de43361a4dbaa357de3cf47be4ae72b4db4690a76e0910c9595f52aab384768fbc
7
+ data.tar.gz: 60b30a31debf0d031902d8df21372b2524246ee98749eeccfd588c00b3458bcb9c0ed2a1fbdeab266cce86332537bde1005aead37fece867d440d88989c2486d
@@ -4,30 +4,36 @@
4
4
  Puppet::Functions.create_function(:'ctrl::do_until') do
5
5
  # @param options A hash of additional options.
6
6
  # @option options [Numeric] limit The number of times to repeat the block.
7
+ # @option options [Numeric] interval The number of seconds to wait before repeating the block.
7
8
  # @example Run a task until it succeeds
8
9
  # ctrl::do_until() || {
9
- # run_task('test', $target, _catch_errors => true).ok()
10
+ # run_task('test', $target, '_catch_errors' => true).ok()
10
11
  # }
11
- #
12
12
  # @example Run a task until it succeeds or fails 10 times
13
13
  # ctrl::do_until('limit' => 10) || {
14
- # run_task('test', $target, _catch_errors => true).ok()
14
+ # run_task('test', $target, '_catch_errors' => true).ok()
15
+ # }
16
+ # @example Run a task and wait 10 seconds before running it again
17
+ # ctrl::do_until('interval' => 10) || {
18
+ # run_task('test', $target, '_catch_errors' => true).ok()
15
19
  # }
16
- #
17
20
  dispatch :do_until do
18
21
  optional_param 'Hash[String[1], Any]', :options
19
22
  block_param
20
23
  end
21
24
 
22
- def do_until(options = { 'limit' => 0 })
25
+ def do_until(options = {})
23
26
  # Send Analytics Report
24
27
  Puppet.lookup(:bolt_executor) {}&.report_function_call(self.class.name)
25
28
 
26
- limit = options['limit']
29
+ limit = options['limit'] || 0
30
+ interval = options['interval']
31
+
27
32
  i = 0
28
33
  until (x = yield)
29
34
  i += 1
30
35
  break if limit != 0 && i >= limit
36
+ Kernel.sleep(interval) if interval
31
37
  end
32
38
  x
33
39
  end
@@ -12,7 +12,7 @@ Puppet::Functions.create_function(:'out::message') do
12
12
  # @example Print a message
13
13
  # out::message('Something went wrong')
14
14
  dispatch :output_message do
15
- param 'String', :message
15
+ param 'Any', :message
16
16
  return_type 'Undef'
17
17
  end
18
18
 
@@ -72,7 +72,7 @@ module Bolt
72
72
 
73
73
  def self.load_config(filename, logger)
74
74
  if File.exist?(filename)
75
- YAML.load_file(filename)
75
+ Bolt::Util.read_optional_yaml_hash(filename, 'analytics')
76
76
  else
77
77
  unless ENV['BOLT_DISABLE_ANALYTICS']
78
78
  logger.warn <<~ANALYTICS
@@ -420,19 +420,18 @@ module Bolt
420
420
  init
421
421
 
422
422
  USAGE
423
- bolt project init [directory] [options]
423
+ bolt project init [name] [options]
424
424
 
425
425
  DESCRIPTION
426
- Create a new Bolt project.
426
+ Create a new Bolt project in the current working directory.
427
427
 
428
- Specify a directory to create a Bolt project in. Defaults to the
429
- curent working directory.
428
+ Specify a name for the Bolt project. Defaults to the basename of the current working directory.
430
429
 
431
430
  EXAMPLES
432
- Create a new Bolt project in the current working directory.
431
+ Create a new Bolt project using the directory as the project name.
433
432
  bolt project init
434
- Create a new Bolt project at a specified path.
435
- bolt project init ~/path/to/project
433
+ Create a new Bolt project with a specified name.
434
+ bolt project init myproject
436
435
  Create a new Bolt project with existing modules.
437
436
  bolt project init --modules puppetlabs-apt,puppetlabs-ntp
438
437
  HELP
@@ -694,9 +693,9 @@ module Bolt
694
693
  @options[:password] = password
695
694
  end
696
695
  define('--password-prompt', 'Prompt for user to input password') do |_password|
697
- STDERR.print "Please enter your password: "
698
- @options[:password] = STDIN.noecho(&:gets).chomp
699
- STDERR.puts
696
+ $stderr.print "Please enter your password: "
697
+ @options[:password] = $stdin.noecho(&:gets).chomp
698
+ $stderr.puts
700
699
  end
701
700
  define('--private-key KEY', 'Path to private ssh key to authenticate with') do |key|
702
701
  @options[:'private-key'] = File.expand_path(key)
@@ -720,9 +719,9 @@ module Bolt
720
719
  @options[:'sudo-password'] = password
721
720
  end
722
721
  define('--sudo-password-prompt', 'Prompt for user to input escalation password') do |_password|
723
- STDERR.print "Please enter your privilege escalation password: "
724
- @options[:'sudo-password'] = STDIN.noecho(&:gets).chomp
725
- STDERR.puts
722
+ $stderr.print "Please enter your privilege escalation password: "
723
+ @options[:'sudo-password'] = $stdin.noecho(&:gets).chomp
724
+ $stderr.puts
726
725
  end
727
726
  define('--sudo-executable EXEC', "Specify an executable for running as another user.",
728
727
  "This option is experimental.") do |exec|
@@ -905,7 +904,7 @@ module Bolt
905
904
  file = value.sub(/^@/, '')
906
905
  read_arg_file(file)
907
906
  elsif value == '-'
908
- STDIN.read
907
+ $stdin.read
909
908
  else
910
909
  value
911
910
  end
@@ -526,7 +526,7 @@ module Bolt
526
526
  tasks = pal.list_tasks
527
527
  tasks.select! { |task| task.first.include?(options[:filter]) } if options[:filter]
528
528
  tasks.select! { |task| config.project.tasks.include?(task.first) } unless config.project.tasks.nil?
529
- outputter.print_tasks(tasks, pal.list_modulepath)
529
+ outputter.print_tasks(tasks, pal.user_modulepath)
530
530
  end
531
531
 
532
532
  def show_plan(plan_name)
@@ -537,7 +537,7 @@ module Bolt
537
537
  plans = pal.list_plans
538
538
  plans.select! { |plan| plan.first.include?(options[:filter]) } if options[:filter]
539
539
  plans.select! { |plan| config.project.plans.include?(plan.first) } unless config.project.plans.nil?
540
- outputter.print_plans(plans, pal.list_modulepath)
540
+ outputter.print_plans(plans, pal.user_modulepath)
541
541
  end
542
542
 
543
543
  def list_targets
@@ -770,8 +770,26 @@ module Bolt
770
770
  # Initializes a specified directory as a Bolt project and installs any modules
771
771
  # specified by the user, along with their dependencies
772
772
  def initialize_project
773
- project = Pathname.new(File.expand_path(options[:object] || Dir.pwd))
774
- config = project + 'bolt.yaml'
773
+ # Dir.pwd will return backslashes on Windows, but Pathname always uses
774
+ # forward slashes to concatenate paths. This results in paths like
775
+ # C:\User\Administrator/modules, which fail module install. This ensure
776
+ # forward slashes in the cwd path.
777
+ dir = File.expand_path(Dir.pwd)
778
+ name = options[:object] || File.basename(dir)
779
+ if name !~ Bolt::Module::MODULE_NAME_REGEX
780
+ if options[:object]
781
+ raise Bolt::ValidationError, "The provided project name '#{name}' is invalid; "\
782
+ "project name must begin with a lowercase letter and can include lowercase "\
783
+ "letters, numbers, and underscores."
784
+ else
785
+ raise Bolt::ValidationError, "The current directory name '#{name}' is an invalid "\
786
+ "project name. Please specify a name using 'bolt project init <name>'."
787
+ end
788
+ end
789
+
790
+ project = Pathname.new(dir)
791
+ old_config = project + 'bolt.yaml'
792
+ config = project + 'bolt-project.yaml'
775
793
  puppetfile = project + 'Puppetfile'
776
794
  modulepath = [project + 'modules']
777
795
 
@@ -792,18 +810,24 @@ module Bolt
792
810
 
793
811
  # Warn the user if the project directory already exists. We don't error here since users
794
812
  # might not have installed any modules yet.
813
+ # If both bolt.yaml and bolt-project.yaml exist, this will just warn
814
+ # about bolt-project.yaml and subsequent Bolt actions will warn about
815
+ # both files existing
795
816
  if config.exist?
796
- @logger.warn "Found existing project directory at #{project}"
797
- end
798
-
799
- # Create the project directory
800
- FileUtils.mkdir_p(project)
801
-
817
+ @logger.warn "Found existing project directory at #{project}. Skipping file creation."
818
+ # This won't get called if bolt-project.yaml exists
819
+ elsif old_config.exist?
820
+ @logger.warn "Found existing #{old_config.basename} at #{project}. "\
821
+ "#{old_config.basename} is deprecated, please rename to #{config.basename}."
802
822
  # Bless the project directory as a...wait for it...project
803
- if FileUtils.touch(config)
804
- outputter.print_message "Successfully created Bolt project at #{project}"
805
823
  else
806
- raise Bolt::FileError.new("Could not create Bolt project directory at #{project}", nil)
824
+ begin
825
+ content = { 'name' => name }
826
+ File.write(config.to_path, content.to_yaml)
827
+ outputter.print_message "Successfully created Bolt project at #{project}"
828
+ rescue StandardError => e
829
+ raise Bolt::FileError.new("Could not create bolt-project.yaml at #{project}: #{e.message}", nil)
830
+ end
807
831
  end
808
832
 
809
833
  # Write the generated Puppetfile to the fancy new project
@@ -381,19 +381,19 @@ module Bolt
381
381
  end
382
382
 
383
383
  def prompt(prompt, options)
384
- unless STDIN.tty?
384
+ unless $stdin.tty?
385
385
  raise Bolt::Error.new('STDIN is not a tty, unable to prompt', 'bolt/no-tty-error')
386
386
  end
387
387
 
388
- STDERR.print("#{prompt}: ")
388
+ $stderr.print("#{prompt}: ")
389
389
 
390
390
  value = if options[:sensitive]
391
- STDIN.noecho(&:gets).to_s.chomp
391
+ $stdin.noecho(&:gets).to_s.chomp
392
392
  else
393
- STDIN.gets.to_s.chomp
393
+ $stdin.gets.to_s.chomp
394
394
  end
395
395
 
396
- STDERR.puts if options[:sensitive]
396
+ $stderr.puts if options[:sensitive]
397
397
 
398
398
  value
399
399
  end
@@ -21,6 +21,62 @@ module Bolt
21
21
  @trace = trace
22
22
  @stream = stream
23
23
  end
24
+
25
+ def indent(indent, string)
26
+ indent = ' ' * indent
27
+ string.gsub(/^/, indent.to_s)
28
+ end
29
+
30
+ def print_message_event(event)
31
+ print_message(stringify(event[:message]))
32
+ end
33
+
34
+ def print_message
35
+ raise NotImplementedError, "print_message() must be implemented by the outputter class"
36
+ end
37
+
38
+ def stringify(message)
39
+ formatted = format_message(message)
40
+ if formatted.is_a?(Hash) || formatted.is_a?(Array)
41
+ ::JSON.pretty_generate(formatted)
42
+ else
43
+ formatted
44
+ end
45
+ end
46
+
47
+ def format_message(message)
48
+ case message
49
+ when Array
50
+ message.map { |item| format_message(item) }
51
+ when Bolt::ApplyResult
52
+ format_apply_result(message)
53
+ when Bolt::Result, Bolt::ResultSet
54
+ # This is equivalent to to_s, but formattable
55
+ message.to_data
56
+ when Bolt::RunFailure
57
+ formatted_resultset = message.result_set.to_data
58
+ message.to_h.merge('result_set' => formatted_resultset)
59
+ when Hash
60
+ message.each_with_object({}) do |(k, v), h|
61
+ h[format_message(k)] = format_message(v)
62
+ end
63
+ when Integer, Float, NilClass
64
+ message
65
+ else
66
+ message.to_s
67
+ end
68
+ end
69
+
70
+ def format_apply_result(result)
71
+ logs = result.resource_logs&.map do |log|
72
+ # Omit low-level info/debug messages
73
+ next if %w[info debug].include?(log['level'])
74
+ indent(2, format_log(log))
75
+ end
76
+ hash = result.to_data
77
+ hash['logs'] = logs unless logs.empty?
78
+ hash
79
+ end
24
80
  end
25
81
  end
26
82
 
@@ -27,11 +27,6 @@ module Bolt
27
27
  end
28
28
  end
29
29
 
30
- def indent(indent, string)
31
- indent = ' ' * indent
32
- string.gsub(/^/, indent.to_s)
33
- end
34
-
35
30
  def remove_trail(string)
36
31
  string.sub(/\s\z/, '')
37
32
  end
@@ -372,10 +367,6 @@ module Bolt
372
367
  end
373
368
  end
374
369
 
375
- def print_message_event(event)
376
- print_message(event[:message])
377
- end
378
-
379
370
  def fatal_error(err)
380
371
  @stream.puts(colorize(:red, err.message))
381
372
  if err.is_a? Bolt::RunFailure
@@ -121,10 +121,6 @@ module Bolt
121
121
  @stream.puts '}' if @object_open
122
122
  end
123
123
 
124
- def print_message_event(event)
125
- print_message(event[:message])
126
- end
127
-
128
124
  def print_message(message)
129
125
  $stderr.puts(message)
130
126
  end
@@ -48,7 +48,7 @@ module Bolt
48
48
  end
49
49
  end
50
50
 
51
- attr_reader :modulepath
51
+ attr_reader :modulepath, :user_modulepath
52
52
 
53
53
  def initialize(modulepath, hiera_config, resource_types, max_compiles = Etc.nprocessors,
54
54
  trusted_external = nil, apply_settings = {}, project = nil)
@@ -56,7 +56,7 @@ module Bolt
56
56
  # is safe and in practice only happens in tests
57
57
  self.class.load_puppet
58
58
 
59
- @original_modulepath = modulepath
59
+ @user_modulepath = modulepath
60
60
  @modulepath = [BOLTLIB_PATH, *modulepath, MODULES_PATH]
61
61
  @hiera_config = hiera_config
62
62
  @trusted_external = trusted_external
@@ -208,7 +208,7 @@ module Bolt
208
208
  # Skip syncing built-in plugins, since we vendor some Puppet 6
209
209
  # versions of "core" types, which are already present on the agent,
210
210
  # but may cause issues on Puppet 5 agents.
211
- @original_modulepath,
211
+ @user_modulepath,
212
212
  @project,
213
213
  pdb_client,
214
214
  @hiera_config,
@@ -278,10 +278,6 @@ module Bolt
278
278
  end
279
279
  end
280
280
 
281
- def list_modulepath
282
- @modulepath - [BOLTLIB_PATH, MODULES_PATH]
283
- end
284
-
285
281
  def parse_params(type, object_name, params)
286
282
  in_bolt_compiler do |compiler|
287
283
  case type
@@ -109,7 +109,7 @@ module Bolt
109
109
  end
110
110
 
111
111
  def message_step(scope, step)
112
- scope.call_function('out::message', step['message'])
112
+ scope.call_function('out::message', [step['message']])
113
113
  end
114
114
 
115
115
  def generate_manifest(resources)
@@ -18,9 +18,9 @@ module Bolt
18
18
  end
19
19
 
20
20
  def resolve_reference(opts)
21
- STDERR.print("#{opts['message']}: ")
22
- value = STDIN.noecho(&:gets).to_s.chomp
23
- STDERR.puts
21
+ $stderr.print("#{opts['message']}: ")
22
+ value = $stdin.noecho(&:gets).to_s.chomp
23
+ $stderr.puts
24
24
 
25
25
  value
26
26
  end
@@ -141,8 +141,10 @@ module Bolt
141
141
  def validate
142
142
  if name
143
143
  if name !~ Bolt::Module::MODULE_NAME_REGEX
144
- raise Bolt::ValidationError, "Invalid project name #{name.inspect.tr('"', "'")} in bolt-project.yaml; "\
145
- "project name must match #{Bolt::Module::MODULE_NAME_REGEX.inspect}"
144
+ raise Bolt::ValidationError, <<~ERROR_STRING
145
+ Invalid project name '#{name}' in bolt-project.yaml; project name must begin with a lowercase letter
146
+ and can include lowercase letters, numbers, and underscores.
147
+ ERROR_STRING
146
148
  elsif Dir.children(Bolt::PAL::BOLTLIB_PATH).include?(name)
147
149
  raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
148
150
  "with a built-in Bolt module of the same name."
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.22.0'
4
+ VERSION = '2.23.0'
5
5
  end
@@ -58,7 +58,7 @@ module BoltServer
58
58
  # Bolt::PAL::MODULES_PATH which would be more complex if we tried to use @modulepath since
59
59
  # we need to append our modulepaths and exclude modules shiped in bolt gem code
60
60
  modulepath_dirs = environment.modulepath
61
- @original_modulepath = modulepath_dirs
61
+ @user_modulepath = modulepath_dirs
62
62
  @modulepath = [PE_BOLTLIB_PATH, Bolt::PAL::BOLTLIB_PATH, *modulepath_dirs]
63
63
  end
64
64
  end
@@ -177,7 +177,7 @@ module BoltSpec
177
177
  if data['msg'] && data['kind'] && (data.keys - %w[msg kind details issue_code]).empty?
178
178
  @data[:default] = clazz.new(data['msg'], data['kind'], data['details'], data['issue_code'])
179
179
  else
180
- STDERR.puts "In the future 'error_with()' may require msg and kind, and " \
180
+ $stderr.puts "In the future 'error_with()' may require msg and kind, and " \
181
181
  "optionally accept only details and issue_code."
182
182
  @data[:default] = data
183
183
  end
@@ -9,7 +9,7 @@ require 'puppet/module_tool/tar'
9
9
  require 'securerandom'
10
10
  require 'tempfile'
11
11
 
12
- args = JSON.parse(ARGV[0] ? File.read(ARGV[0]) : STDIN.read)
12
+ args = JSON.parse(ARGV[0] ? File.read(ARGV[0]) : $stdin.read)
13
13
 
14
14
  # Create temporary directories for all core Puppet settings so we don't clobber
15
15
  # existing state or read from puppet.conf. Also create a temporary modulepath.
@@ -110,7 +110,7 @@ ensure
110
110
  begin
111
111
  FileUtils.remove_dir(puppet_root)
112
112
  rescue Errno::ENOTEMPTY => e
113
- STDERR.puts("Could not cleanup temporary directory: #{e}")
113
+ $stderr.puts("Could not cleanup temporary directory: #{e}")
114
114
  end
115
115
  end
116
116
 
@@ -40,7 +40,7 @@ when "compile"
40
40
  request = if ARGV[1]
41
41
  File.open(ARGV[1]) { |fh| JSON.parse(fh.read) }
42
42
  else
43
- JSON.parse(STDIN.read)
43
+ JSON.parse($stdin.read)
44
44
  end
45
45
  begin
46
46
  catalog = Bolt::Catalog.new.compile_catalog(request)
@@ -6,7 +6,7 @@ require 'puppet'
6
6
  require 'puppet/module_tool/tar'
7
7
  require 'tempfile'
8
8
 
9
- args = JSON.parse(STDIN.read)
9
+ args = JSON.parse($stdin.read)
10
10
 
11
11
  Dir.mktmpdir do |puppet_root|
12
12
  # Create temporary directories for all core Puppet settings so we don't clobber
@@ -6,7 +6,7 @@ require 'puppet'
6
6
  require 'puppet/module_tool/tar'
7
7
  require 'tempfile'
8
8
 
9
- args = JSON.parse(STDIN.read)
9
+ args = JSON.parse($stdin.read)
10
10
 
11
11
  RESOURCE_INSTANCE = /^([^\[]+)\[([^\]]+)\]$/.freeze
12
12
 
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.22.0
4
+ version: 2.23.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-10 00:00:00.000000000 Z
11
+ date: 2020-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable