bolt 3.23.1 → 3.24.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: 67f08b10db67ef79aadf22a542f6564aa502743f53619f41c40aff4761e2e0c0
4
- data.tar.gz: 81d8b927c1349299ba568358a2f155d624fb28f7b65ae5985bcfc8b6fe24eb6b
3
+ metadata.gz: 7ad3bbc9413aac278feeac8a7e31173bd065f0029fb211d7e1906c4803a4c390
4
+ data.tar.gz: 387d3a07844139eb29a1db06d1792acfdad6a09eab2b5576a9c4d34fdffdaca8
5
5
  SHA512:
6
- metadata.gz: a3e66f8d5adc727228b09e212de34c7de796c556a0d91153e1599cbf619b30b98c9358ea3683c2e00650375ecac8745fdf7a79977a42d6761967ce98c5c2ac71
7
- data.tar.gz: a6dedd218bc2db8e5365906366a1cbeef5e99b02f606c80677081eccc71172fa7f15f3798630847239228feafc861278938044e98b2107429c8e34cea7143f9c
6
+ metadata.gz: 83c1685fc238f049dad5cf841ea55f37c57a1aa59d66ef420d754b78d7b0ba8cca4533accb2cfbe53915587f1832beb7cbf956501486de63e135933b57f5fa98
7
+ data.tar.gz: c6900fde1fd9234a603171c79c2f2b3a22b2d2661797b94bf0afd2050f4ef5cc32844562734d3e41bd94464806e1efaabaac346d6887773f547c801c33cc3d6f
data/Puppetfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- forge "http://forge.puppetlabs.com"
3
+ forge 'https://forge.puppetlabs.com'
4
4
 
5
5
  moduledir File.join(File.dirname(__FILE__), 'modules')
6
6
 
@@ -26,7 +26,7 @@ mod 'puppetlabs-package', '2.2.0'
26
26
  mod 'puppetlabs-powershell_task_helper', '0.1.0'
27
27
  mod 'puppetlabs-puppet_conf', '1.3.0'
28
28
  mod 'puppetlabs-python_task_helper', '0.5.0'
29
- mod 'puppetlabs-reboot', '4.1.0'
29
+ mod 'puppetlabs-reboot', '4.2.0'
30
30
  mod 'puppetlabs-ruby_task_helper', '0.6.1'
31
31
  mod 'puppetlabs-ruby_plugin_helper', '0.2.0'
32
32
  mod 'puppetlabs-stdlib', '8.2.0'
@@ -49,7 +49,7 @@ module Bolt
49
49
  code
50
50
  end
51
51
 
52
- targets = inventory.get_targets(targets)
52
+ targets = inventory.get_targets(targets, ext_glob: true)
53
53
 
54
54
  Puppet[:tasks] = false
55
55
  ast = pal.parse_manifest(manifest_code, manifest)
@@ -90,7 +90,7 @@ module Bolt
90
90
  # @return [Bolt::ResultSet]
91
91
  #
92
92
  def run_command(command, targets, env_vars: nil)
93
- targets = inventory.get_targets(targets)
93
+ targets = inventory.get_targets(targets, ext_glob: true)
94
94
 
95
95
  with_benchmark do
96
96
  executor.run_command(targets, command, env_vars: env_vars)
@@ -106,7 +106,7 @@ module Bolt
106
106
  #
107
107
  def download_file(source, destination, targets)
108
108
  destination = File.expand_path(destination, Dir.pwd)
109
- targets = inventory.get_targets(targets)
109
+ targets = inventory.get_targets(targets, ext_glob: true)
110
110
 
111
111
  with_benchmark do
112
112
  executor.download_file(targets, source, destination)
@@ -122,7 +122,7 @@ module Bolt
122
122
  #
123
123
  def upload_file(source, destination, targets)
124
124
  source = find_file(source)
125
- targets = inventory.get_targets(targets)
125
+ targets = inventory.get_targets(targets, ext_glob: true)
126
126
 
127
127
  Bolt::Util.validate_file('source file', source, true)
128
128
 
@@ -225,7 +225,7 @@ module Bolt
225
225
 
226
226
  with_benchmark do
227
227
  pal.lookup(key,
228
- inventory.get_targets(targets),
228
+ inventory.get_targets(targets, ext_glob: true),
229
229
  inventory,
230
230
  executor,
231
231
  plan_vars: vars)
@@ -351,6 +351,7 @@ module Bolt
351
351
  # @return [Bolt::PlanResult]
352
352
  #
353
353
  def run_plan(plan, targets, params: {})
354
+ plan_params = pal.get_plan_info(plan)['parameters']
354
355
  if targets && targets.any?
355
356
  if params['nodes'] || params['targets']
356
357
  key = params.include?('nodes') ? 'nodes' : 'targets'
@@ -360,7 +361,6 @@ module Bolt
360
361
  "in the JSON data passed in the --params option"
361
362
  end
362
363
 
363
- plan_params = pal.get_plan_info(plan)['parameters']
364
364
  target_param = plan_params.dig('targets', 'type') =~ /TargetSpec/
365
365
  node_param = plan_params.include?('nodes')
366
366
 
@@ -375,13 +375,17 @@ module Bolt
375
375
  end
376
376
  end
377
377
 
378
- plan_context = { plan_name: plan, params: params }
378
+ sensitive_params = params.keys.select { |param| plan_params.dig(param, 'sensitive') }
379
+
380
+ plan_context = { plan_name: plan, params: params, sensitive: sensitive_params }
379
381
 
380
382
  executor.start_plan(plan_context)
381
383
  result = pal.run_plan(plan, params, executor, inventory, plugins.puppetdb_client)
382
384
  executor.finish_plan(result)
383
385
 
384
386
  result
387
+ rescue Bolt::Error => e
388
+ Bolt::PlanResult.new(e, 'failure')
385
389
  end
386
390
 
387
391
  # Show plan information.
@@ -620,7 +624,10 @@ module Bolt
620
624
  Bolt::Util.validate_file('script', script)
621
625
 
622
626
  with_benchmark do
623
- executor.run_script(inventory.get_targets(targets), script, arguments, env_vars: env_vars)
627
+ executor.run_script(inventory.get_targets(targets, ext_glob: true),
628
+ script,
629
+ arguments,
630
+ env_vars: env_vars)
624
631
  end
625
632
  end
626
633
 
@@ -676,7 +683,7 @@ module Bolt
676
683
  # @return [Bolt::ResultSet]
677
684
  #
678
685
  def run_task(task, targets, params: {})
679
- targets = inventory.get_targets(targets)
686
+ targets = inventory.get_targets(targets, ext_glob: true)
680
687
 
681
688
  with_benchmark do
682
689
  pal.run_task(task, targets, params, executor, inventory)
@@ -775,7 +782,7 @@ module Bolt
775
782
  # Retrieve the known group and target names. This needs to be done before
776
783
  # updating targets, as that will add adhoc targets to the inventory.
777
784
  known_names = inventory.target_names
778
- targets = inventory.get_targets(targets)
785
+ targets = inventory.get_targets(targets, ext_glob: true)
779
786
 
780
787
  inventory_targets, adhoc_targets = targets.partition do |target|
781
788
  known_names.include?(target.name)
@@ -9,6 +9,7 @@ module Bolt
9
9
  class LXD < Base
10
10
  OPTIONS = %w[
11
11
  cleanup
12
+ interpreters
12
13
  remote
13
14
  tmpdir
14
15
  ].concat(RUN_AS_OPTIONS).sort.freeze
@@ -17,6 +18,14 @@ module Bolt
17
18
  'cleanup' => true,
18
19
  'remote' => 'local'
19
20
  }.freeze
21
+
22
+ private def validate
23
+ super
24
+
25
+ if @config['interpreters']
26
+ @config['interpreters'] = normalize_interpreters(@config['interpreters'])
27
+ end
28
+ end
20
29
  end
21
30
  end
22
31
  end
@@ -8,6 +8,12 @@ module Bolt
8
8
  class Inventory
9
9
  attr_reader :plugins, :source, :targets, :transport
10
10
 
11
+ # Getting targets from the inventory using '--targets' supports extended glob pattern
12
+ # matching. In this case, use the extended regex so Bolt only uses commas outside
13
+ # brackets and braces as delimiters.
14
+ EXTENDED_TARGET_REGEX = /[[:space:],]+(?=[^\]}]*(?:[\[{]|$))/.freeze
15
+ TARGET_REGEX = /[[:space:],]+/.freeze
16
+
11
17
  class WildcardError < Bolt::Error
12
18
  def initialize(target)
13
19
  super("Found 0 targets matching wildcard pattern #{target}", 'bolt.inventory/wildcard-error')
@@ -85,8 +91,8 @@ module Bolt
85
91
  # alias for analytics
86
92
  alias node_names target_names
87
93
 
88
- def get_targets(targets)
89
- target_array = expand_targets(targets)
94
+ def get_targets(targets, ext_glob: false)
95
+ target_array = expand_targets(targets, ext_glob: ext_glob)
90
96
  if target_array.is_a? Array
91
97
  target_array.flatten.uniq(&:name)
92
98
  else
@@ -107,20 +113,31 @@ module Bolt
107
113
  groups.group_collect(target_name)
108
114
  end
109
115
 
116
+ # Does a target match the glob-style wildcard?
117
+ # Ignore case; use extended globs ({a,b}) when running from the CLI.
118
+ def match_wildcard?(wildcard, target_name, ext_glob: false)
119
+ if ext_glob
120
+ File.fnmatch(wildcard, target_name, File::FNM_CASEFOLD | File::FNM_EXTGLOB)
121
+ else
122
+ regexp = Regexp.new("^#{Regexp.escape(wildcard).gsub('\*', '.*?')}$", Regexp::IGNORECASE)
123
+ target_name =~ regexp
124
+ end
125
+ end
126
+
110
127
  # If target is a group name, expand it to the members of that group.
111
128
  # Else match against targets in inventory by name or alias.
112
129
  # If a wildcard string, error if no matches are found.
113
130
  # Else fall back to [target] if no matches are found.
114
- def resolve_name(target)
131
+ def resolve_name(target, ext_glob: false)
115
132
  if (group = group_lookup[target])
116
133
  group.all_targets
117
134
  else
118
135
  # Try to wildcard match targets in inventory
119
136
  # Ignore case because hostnames are generally case-insensitive
120
- regexp = Regexp.new("^#{Regexp.escape(target).gsub('\*', '.*?')}$", Regexp::IGNORECASE)
121
-
122
- targets = groups.all_targets.select { |targ| targ =~ regexp }
123
- targets += groups.target_aliases.select { |target_alias, _target| target_alias =~ regexp }.values
137
+ targets = groups.all_targets.select { |targ| match_wildcard?(target, targ, ext_glob: ext_glob) }
138
+ targets += groups.target_aliases
139
+ .select { |tgt_alias, _| match_wildcard?(target, tgt_alias, ext_glob: ext_glob) }
140
+ .values
124
141
 
125
142
  if targets.empty?
126
143
  raise(WildcardError, target) if target.include?('*')
@@ -132,16 +149,18 @@ module Bolt
132
149
  end
133
150
  private :resolve_name
134
151
 
135
- def expand_targets(targets)
152
+ def expand_targets(targets, ext_glob: false)
136
153
  case targets
137
154
  when Bolt::Target
138
155
  targets
139
156
  when Array
140
- targets.map { |tish| expand_targets(tish) }
157
+ targets.map { |tish| expand_targets(tish, ext_glob: ext_glob) }
141
158
  when String
142
159
  # Expand a comma-separated list
143
- targets.split(/[[:space:],]+/).reject(&:empty?).map do |name|
144
- ts = resolve_name(name)
160
+ # Regex magic below is required to workaround `{foo,bar}` glob syntax
161
+ regex = ext_glob ? EXTENDED_TARGET_REGEX : TARGET_REGEX
162
+ targets.split(regex).reject(&:empty?).map do |name|
163
+ ts = resolve_name(name, ext_glob: ext_glob)
145
164
  ts.map do |t|
146
165
  # If the target doesn't exist, evaluate it from the inventory.
147
166
  # Then return a Bolt::Target.
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative '../../bolt/container_result'
4
4
  require_relative '../../bolt/pal'
5
+ require_relative '../../bolt/util/format'
5
6
 
6
7
  module Bolt
7
8
  class Outputter
@@ -890,7 +891,7 @@ module Bolt
890
891
  end
891
892
 
892
893
  def print_message(message)
893
- @stream.puts(message)
894
+ @stream.puts(Bolt::Util::Format.stringify(message))
894
895
  end
895
896
 
896
897
  def print_error(message)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../bolt/util/format'
4
+
3
5
  module Bolt
4
6
  class Outputter
5
7
  class JSON < Bolt::Outputter
@@ -190,7 +192,7 @@ module Bolt
190
192
  end
191
193
 
192
194
  def print_message(message)
193
- $stderr.puts(message)
195
+ $stderr.puts(Bolt::Util::Format.stringify(message))
194
196
  end
195
197
  alias print_error print_message
196
198
 
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../../bolt/pal'
4
+ require_relative '../../bolt/util/format'
4
5
 
5
6
  module Bolt
6
7
  class Outputter
@@ -110,7 +111,7 @@ module Bolt
110
111
  end
111
112
 
112
113
  def print_message(message)
113
- @stream.puts colorize(:rainbow, message)
114
+ @stream.puts colorize(:rainbow, Bolt::Util::Format.stringify(message))
114
115
  end
115
116
  end
116
117
  end
@@ -6,7 +6,7 @@ module Bolt
6
6
  class Connection
7
7
  attr_reader :logger, :key
8
8
 
9
- CONTEXT_KEYS = Set.new(%i[plan_name description params]).freeze
9
+ CONTEXT_KEYS = Set.new(%i[plan_name description params sensitive]).freeze
10
10
 
11
11
  def self.get_key(opts)
12
12
  [
@@ -46,6 +46,7 @@ module Bolt
46
46
  if plan_context
47
47
  begin
48
48
  opts = plan_context.select { |k, _| CONTEXT_KEYS.include? k }
49
+ opts[:params] = opts[:params].reject { |k, _| plan_context[:sensitive].include?(k) }
49
50
  @client.command.plan_start(opts)['name']
50
51
  rescue OrchestratorClient::ApiError => e
51
52
  if e.code == '404'
@@ -53,9 +53,11 @@ module Bolt
53
53
  @connection = ::WinRM::Connection.new(options)
54
54
  @connection.logger = @transport_logger
55
55
 
56
- @session = @connection.shell(:powershell)
57
- @session.run('$PSVersionTable.PSVersion')
58
- @logger.trace { "Opened session" }
56
+ @connection.shell(:powershell) do |session|
57
+ session.run('$PSVersionTable.PSVersion')
58
+ end
59
+
60
+ @logger.trace { "Opened connection" }
59
61
  end
60
62
  rescue Timeout::Error
61
63
  # If we're using the default port with SSL, a timeout probably means the
@@ -95,9 +97,8 @@ module Bolt
95
97
  end
96
98
 
97
99
  def disconnect
98
- @session&.close
99
100
  @client&.disconnect!
100
- @logger.trace { "Closed session" }
101
+ @logger.trace { "Closed connection" }
101
102
  end
102
103
 
103
104
  def execute(command)
@@ -116,12 +117,18 @@ module Bolt
116
117
  # propagate to the main thread via the shell, there's no chance
117
118
  # they will be unhandled, so the default stack trace is unneeded.
118
119
  Thread.current.report_on_exception = false
119
- result = @session.run(command)
120
- out_wr << result.stdout
121
- err_wr << result.stderr
122
- out_wr.close
123
- err_wr.close
124
- result.exitcode
120
+
121
+ # Open a new shell instance for each command executed. PowerShell is
122
+ # unable to unload any DLLs loaded when running a PowerShell script
123
+ # or task from the same shell instance they were loaded in, which
124
+ # prevents Bolt from cleaning up the temp directory successfully.
125
+ # Using a new PowerShell instance avoids this limitation.
126
+ @connection.shell(:powershell) do |session|
127
+ result = session.run(command)
128
+ out_wr << result.stdout
129
+ err_wr << result.stderr
130
+ result.exitcode
131
+ end
125
132
  ensure
126
133
  # Close the streams to avoid the caller deadlocking
127
134
  out_wr.close
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '3.23.1'
4
+ VERSION = '3.24.0'
5
5
  end
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: 3.23.1
4
+ version: 3.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-06-16 00:00:00.000000000 Z
11
+ date: 2022-06-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -163,6 +163,9 @@ dependencies:
163
163
  - - ">="
164
164
  - !ruby/object:Gem::Version
165
165
  version: '4.0'
166
+ - - "<"
167
+ - !ruby/object:Gem::Version
168
+ version: '7.0'
166
169
  type: :runtime
167
170
  prerelease: false
168
171
  version_requirements: !ruby/object:Gem::Requirement
@@ -170,6 +173,9 @@ dependencies:
170
173
  - - ">="
171
174
  - !ruby/object:Gem::Version
172
175
  version: '4.0'
176
+ - - "<"
177
+ - !ruby/object:Gem::Version
178
+ version: '7.0'
173
179
  - !ruby/object:Gem::Dependency
174
180
  name: net-ssh-krb
175
181
  requirement: !ruby/object:Gem::Requirement