bolt 2.0.1 → 2.1.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: 30a51dcd7670a136af567ff1778431fd36801d05c8c45345093df6602e877385
4
- data.tar.gz: 98a234f0f9f47c860d59d0a34de317d9f9e9b20935ace99ef97ec239dcbea8f6
3
+ metadata.gz: 5d912a5ba19a113e4e5a57aed09f04f1a72a659b05fdb7cc09164146b59fc0cc
4
+ data.tar.gz: 9344eaf150b512c769e8b6fd7ac91fef7ece7de62cb39bede4f22ebd15f52cd3
5
5
  SHA512:
6
- metadata.gz: 6a32a48d4b957a811290e44f63ef6c2e1f3f672bf18ec51637c92601286652b5363222ebd747913e4e9a6e4936ace14e2f0a6aeea44d46923ba90a78d11d4b12
7
- data.tar.gz: 28e5168898c9e49c694b82511ee68e89b1d409f116367fbd7200a87697259a0ab4f45c2fc1e59663de72920195d27523ddffafd6550037d6556cfbdafdcaf807
6
+ metadata.gz: 69040995cbcff577cfa377f50a80dc76825ba3aa0fd107788f146c26e5d562131ec089ce79f69c052b47b2fa9b2d7af3c9feb587829e6a4b9297ee9df2b9d9bd
7
+ data.tar.gz: ef5b45003158f3f4801877e2c19768b990fa4c09d84fcce40888f4236bf025e0244ac5e5e4b5edc5397c3a49691390e650b0724a8450c3e10382fdee44b80f73
data/Puppetfile CHANGED
@@ -25,7 +25,7 @@ mod 'puppetlabs-zone_core', '1.0.3'
25
25
  mod 'puppetlabs-package', '0.7.0'
26
26
  mod 'puppetlabs-puppet_conf', '0.4.0'
27
27
  mod 'puppetlabs-python_task_helper', '0.3.0'
28
- mod 'puppetlabs-reboot', '2.2.0'
28
+ mod 'puppetlabs-reboot', '3.0.0'
29
29
  mod 'puppetlabs-ruby_task_helper', '0.4.0'
30
30
  mod 'puppetlabs-ruby_plugin_helper', '0.1.0'
31
31
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ # Write contents to a file on the given set of targets.
6
+ #
7
+ # > **Note:** Not available in apply block
8
+ Puppet::Functions.create_function(:write_file) do
9
+ # @param targets A pattern identifying zero or more targets. See {get_targets} for accepted patterns.
10
+ # @param content File content to write.
11
+ # @param destination An absolute path on the target(s).
12
+ # @option options [Boolean] _catch_errors Whether to catch raised errors.
13
+ # @option options [String] _run_as User to run as using privilege escalation.
14
+ # @return A list of results, one entry per target.
15
+ # @example Write a file to a target
16
+ # $content = 'Hello, world!'
17
+ # write_file($targets, $content, '/Users/me/hello.txt')
18
+ dispatch :write_file do
19
+ required_param 'String', :content
20
+ required_param 'String[1]', :destination
21
+ required_param 'Boltlib::TargetSpec', :targets
22
+ optional_param 'Hash[String[1], Any]', :options
23
+ return_type 'ResultSet'
24
+ end
25
+
26
+ def write_file(content, destination, target_spec, options = {})
27
+ unless Puppet[:tasks]
28
+ raise Puppet::ParseErrorWithIssue
29
+ .from_issue_and_stack(Bolt::PAL::Issues::PLAN_OPERATION_NOT_SUPPORTED_WHEN_COMPILING,
30
+ action: 'write_file')
31
+ end
32
+
33
+ executor = Puppet.lookup(:bolt_executor)
34
+ executor.report_function_call(self.class.name)
35
+
36
+ inventory = Puppet.lookup(:bolt_inventory)
37
+ targets = inventory.get_targets(target_spec)
38
+
39
+ executor.log_action("write file #{destination}", targets) do
40
+ executor.without_default_logging do
41
+ Tempfile.create do |tmp|
42
+ call_function('file::write', tmp.path, content)
43
+ call_function('upload_file', tmp.path, destination, targets, options)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -88,7 +88,7 @@ module Bolt
88
88
  when 'puppetfile'
89
89
  case action
90
90
  when 'install'
91
- { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
91
+ { flags: OPTIONS[:global] + OPTIONS[:global_config_setters] + %w[puppetfile],
92
92
  banner: PUPPETFILE_INSTALL_HELP }
93
93
  when 'show-modules'
94
94
  { flags: OPTIONS[:global] + OPTIONS[:global_config_setters],
@@ -706,7 +706,7 @@ module Bolt
706
706
  @options[:boltdir] = path
707
707
  end
708
708
  define('--configfile FILEPATH',
709
- 'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml). ' \
709
+ 'Specify where to load config from (default: ~/.puppetlabs/bolt/bolt.yaml).',
710
710
  'Directory containing bolt.yaml will be used as the Boltdir.') do |path|
711
711
  @options[:configfile] = path
712
712
  end
@@ -715,7 +715,12 @@ module Bolt
715
715
  if ENV.include?(Bolt::Inventory::ENVIRONMENT_VAR)
716
716
  raise Bolt::CLIError, "Cannot pass inventory file when #{Bolt::Inventory::ENVIRONMENT_VAR} is set"
717
717
  end
718
- @options[:inventoryfile] = File.expand_path(path)
718
+ @options[:inventoryfile] = Pathname.new(File.expand_path(path))
719
+ end
720
+ define('--puppetfile FILEPATH',
721
+ 'Specify a Puppetfile to use when installing modules. (default: ~/.puppetlabs/bolt/Puppetfile)',
722
+ 'Modules are installed in the current Boltdir.') do |path|
723
+ @options[:puppetfile] = Pathname.new(File.expand_path(path))
719
724
  end
720
725
  define('--[no-]save-rerun', 'Whether to update the rerun file after this command.') do |save|
721
726
  @options[:'save-rerun'] = save
@@ -223,12 +223,6 @@ module Bolt
223
223
  Bolt::Util.deep_clone(self)
224
224
  end
225
225
 
226
- def normalize_interpreters(interpreters)
227
- Bolt::Util.walk_keys(interpreters) do |key|
228
- key.chars[0] == '.' ? key : '.' + key
229
- end
230
- end
231
-
232
226
  def normalize_log(target)
233
227
  return target if target == 'console'
234
228
  target = target[5..-1] if target.start_with?('file:')
@@ -308,6 +302,8 @@ module Bolt
308
302
  send("#{key}=", options[key]) if options.key?(key)
309
303
  end
310
304
 
305
+ @puppetfile = options[:puppetfile] if options.key?(:puppetfile)
306
+
311
307
  @save_rerun = options[:'save-rerun'] if options.key?(:'save-rerun')
312
308
 
313
309
  if options[:debug]
@@ -345,6 +341,11 @@ module Bolt
345
341
  end
346
342
 
347
343
  def update_transports(data)
344
+ self.class.update_transport_hash(@boltdir.path, @transports, data)
345
+ @transport = data['transport'] if data.key?('transport')
346
+ end
347
+
348
+ def self.update_transport_hash(boltdir, existing, data)
348
349
  TRANSPORTS.each do |key, impl|
349
350
  if data[key.to_s]
350
351
  selected = impl.filter_options(data[key.to_s])
@@ -352,17 +353,21 @@ module Bolt
352
353
  # Expand file paths relative to the Boltdir
353
354
  to_expand = %w[private-key cacert token-file] & selected.keys
354
355
  to_expand.each do |opt|
355
- selected[opt] = File.expand_path(selected[opt], @boltdir.path) if selected[opt].is_a?(String)
356
+ selected[opt] = File.expand_path(selected[opt], boltdir) if selected[opt].is_a?(String)
356
357
  end
357
358
 
358
- @transports[key] = Bolt::Util.deep_merge(@transports[key], selected)
359
+ existing[key] = Bolt::Util.deep_merge(existing[key], selected)
359
360
  end
360
- if @transports[key]['interpreters']
361
- @transports[key]['interpreters'] = normalize_interpreters(@transports[key]['interpreters'])
361
+ if existing[key]['interpreters']
362
+ existing[key]['interpreters'] = normalize_interpreters(existing[key]['interpreters'])
362
363
  end
363
364
  end
365
+ end
364
366
 
365
- @transport = data['transport'] if data.key?('transport')
367
+ def self.normalize_interpreters(interpreters)
368
+ Bolt::Util.walk_keys(interpreters) do |key|
369
+ key.chars[0] == '.' ? key : '.' + key
370
+ end
366
371
  end
367
372
 
368
373
  def transport_conf
@@ -383,7 +388,7 @@ module Bolt
383
388
  end
384
389
 
385
390
  def puppetfile
386
- @boltdir.puppetfile
391
+ @puppetfile || @boltdir.puppetfile
387
392
  end
388
393
 
389
394
  def modulepath
@@ -116,7 +116,7 @@ module Bolt
116
116
  raise ValidationError.new("Target name must be ASCII characters: #{target}", @name)
117
117
  end
118
118
 
119
- if local_targets.include?(t_name)
119
+ if contains_target?(t_name)
120
120
  @logger.warn("Ignoring duplicate target in #{@name}: #{target}")
121
121
  return
122
122
  end
@@ -189,9 +189,6 @@ module Bolt
189
189
  end
190
190
 
191
191
  def resolve_string_targets(aliases, known_targets)
192
- # Use a single copy of this because recomputing it for every target is
193
- # prohibitively expensive for large groups
194
- cached_local_targets = local_targets
195
192
  @string_targets.each do |string_target|
196
193
  # If this is the name of a target defined elsewhere, then insert the
197
194
  # target into this group as just a name. Otherwise, add a new target
@@ -200,19 +197,17 @@ module Bolt
200
197
  @unresolved_targets[string_target] = { 'name' => string_target }
201
198
  # If this is an alias for an existing target, then add it to this group
202
199
  elsif (canonical_name = aliases[string_target])
203
- if cached_local_targets.include?(canonical_name)
200
+ if contains_target?(canonical_name)
204
201
  @logger.warn("Ignoring duplicate target in #{@name}: #{canonical_name}")
205
202
  else
206
203
  @unresolved_targets[canonical_name] = { 'name' => canonical_name }
207
- cached_local_targets << canonical_name
208
204
  end
209
205
  # If it's not the name or alias of an existing target, then make a
210
206
  # new target using the string as the URI
211
- elsif cached_local_targets.include?(string_target)
207
+ elsif contains_target?(string_target)
212
208
  @logger.warn("Ignoring duplicate target in #{@name}: #{string_target}")
213
209
  else
214
210
  @unresolved_targets[string_target] = { 'uri' => string_target }
215
- cached_local_targets << string_target
216
211
  end
217
212
  end
218
213
  @groups.each { |g| g.resolve_string_targets(aliases, known_targets) }
@@ -364,6 +359,10 @@ module Bolt
364
359
  Set.new(@unresolved_targets.keys) + Set.new(@resolved_targets.keys)
365
360
  end
366
361
 
362
+ def contains_target?(target_name)
363
+ @unresolved_targets.key?(target_name) || @resolved_targets.key?(target_name)
364
+ end
365
+
367
366
  # Returns all targets contained within the group, which includes targets from subgroups.
368
367
  def all_targets
369
368
  @groups.inject(local_targets) do |acc, g|
@@ -404,7 +403,7 @@ module Bolt
404
403
 
405
404
  # If this group has the target or one of the child groups has the
406
405
  # target, return the data, otherwise return nil
407
- if child_result || local_targets.include?(target_name)
406
+ if child_result || contains_target?(target_name)
408
407
  # Children override the parent
409
408
  data_merge(group_data, child_result)
410
409
  end
@@ -168,7 +168,6 @@ module Bolt
168
168
  def add_target(current_group, target, desired_group)
169
169
  if current_group.name == desired_group
170
170
  current_group.add_target(target)
171
- @groups.validate
172
171
  target.invalidate_group_cache!
173
172
  return true
174
173
  end
@@ -200,6 +199,8 @@ module Bolt
200
199
  # If target already exists, delete old and replace with new, otherwise add to new to all group
201
200
  new_target = Bolt::Inventory::Target.new(data, self)
202
201
  existing_target = @targets.key?(new_target.name)
202
+
203
+ validate_target_from_hash(new_target)
203
204
  @targets[new_target.name] = new_target
204
205
 
205
206
  if existing_target
@@ -208,17 +209,43 @@ module Bolt
208
209
  add_to_group([new_target], 'all')
209
210
  end
210
211
 
211
- if (aliases = new_target.target_alias)
212
- aliases = [aliases] if aliases.is_a?(String)
213
- unless aliases.is_a?(Array)
212
+ if new_target.target_alias
213
+ @groups.insert_alia(new_target.name, Array(new_target.target_alias))
214
+ end
215
+
216
+ new_target
217
+ end
218
+
219
+ def validate_target_from_hash(target)
220
+ groups = Set.new(group_names)
221
+ targets = target_names
222
+
223
+ # Make sure there are no group name conflicts
224
+ if groups.include?(target.name)
225
+ raise ValidationError.new("Target name #{target.name} conflicts with group of the same name", nil)
226
+ end
227
+
228
+ # Validate any aliases
229
+ if (aliases = target.target_alias)
230
+ unless aliases.is_a?(Array) || aliases.is_a?(String)
214
231
  msg = "Alias entry on #{t_name} must be a String or Array, not #{aliases.class}"
215
232
  raise ValidationError.new(msg, @name)
216
233
  end
217
-
218
- @groups.insert_alia(new_target.name, aliases)
219
234
  end
220
235
 
221
- new_target
236
+ # Make sure there are no conflicts with the new target aliases
237
+ used_aliases = @groups.target_aliases
238
+ Array(target.target_alias).each do |alia|
239
+ if groups.include?(alia)
240
+ raise ValidationError.new("Alias #{alia} conflicts with group of the same name", nil)
241
+ elsif targets.include?(alia)
242
+ raise ValidationError.new("Alias #{alia} conflicts with target of the same name", nil)
243
+ elsif used_aliases[alia] && used_aliases[alia] != target.name
244
+ raise ValidationError.new(
245
+ "Alias #{alia} refers to multiple targets: #{used_aliases[alia]} and #{target.name}", nil
246
+ )
247
+ end
248
+ end
222
249
  end
223
250
 
224
251
  def clear_alia_from_group(group, target_name)
@@ -249,9 +276,6 @@ module Bolt
249
276
  def add_to_group(targets, desired_group)
250
277
  if group_names.include?(desired_group)
251
278
  targets.each do |target|
252
- if group_names.include?(target.name)
253
- raise ValidationError.new("Group #{target.name} conflicts with target of the same name", target.name)
254
- end
255
279
  # Add the inventory copy of the target
256
280
  add_target(@groups, @targets[target.name], desired_group)
257
281
  end
@@ -109,14 +109,16 @@ module Bolt
109
109
  def transport_config_cache
110
110
  if @transport_config_cache.nil?
111
111
  merged_config = Bolt::Util.deep_merge(group_cache['config'], @config)
112
- # Use the base config to ensure we handle the config validation and
113
- # munging correctly
114
- config = @inventory.config.deep_clone
115
- config.update_from_inventory(merged_config)
116
- config.validate
112
+ base_config = @inventory.config
113
+ transport_data = Bolt::Util.deep_clone(base_config.transports)
114
+ Bolt::Config.update_transport_hash(base_config.boltdir.path, transport_data, merged_config)
115
+ transport = merged_config['transport'] || base_config.transport
116
+ Bolt::TRANSPORTS.each do |name, impl|
117
+ impl.validate(transport_data[name])
118
+ end
117
119
  @transport_config_cache = {
118
- 'transport' => config.transport_conf[:transport],
119
- 'transports' => config.transport_conf[:transports]
120
+ 'transport' => transport,
121
+ 'transports' => transport_data
120
122
  }
121
123
  end
122
124
 
@@ -336,11 +336,16 @@ module Bolt
336
336
  end
337
337
 
338
338
  defaults = plan.parameters.reject { |_, value| value.nil? }.to_h
339
+ signature_params = Set.new(plan.parameters.map(&:first))
339
340
  parameters = plan.tags(:param).each_with_object({}) do |param, params|
340
341
  name = param.name
341
- params[name] = { 'type' => param.types.first }
342
- params[name]['default_value'] = defaults[name] if defaults.key?(name)
343
- params[name]['description'] = param.text unless param.text.empty?
342
+ if signature_params.include?(name)
343
+ params[name] = { 'type' => param.types.first }
344
+ params[name]['default_value'] = defaults[name] if defaults.key?(name)
345
+ params[name]['description'] = param.text unless param.text.empty?
346
+ else
347
+ @logger.warn("The documented parameter '#{name}' does not exist in plan signature")
348
+ end
344
349
  end
345
350
 
346
351
  {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '2.0.1'
4
+ VERSION = '2.1.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: 2.0.1
4
+ version: 2.1.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-02-25 00:00:00.000000000 Z
11
+ date: 2020-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -375,6 +375,7 @@ files:
375
375
  - bolt-modules/boltlib/lib/puppet/functions/vars.rb
376
376
  - bolt-modules/boltlib/lib/puppet/functions/wait_until_available.rb
377
377
  - bolt-modules/boltlib/lib/puppet/functions/without_default_logging.rb
378
+ - bolt-modules/boltlib/lib/puppet/functions/write_file.rb
378
379
  - bolt-modules/boltlib/types/planresult.pp
379
380
  - bolt-modules/boltlib/types/targetspec.pp
380
381
  - bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb