bolt 1.31.1 → 1.32.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.

data/lib/bolt/target.rb CHANGED
@@ -1,8 +1,154 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bolt/error'
4
+ require 'bolt/util'
4
5
 
5
6
  module Bolt
7
+ class Target2
8
+ attr_accessor :inventory
9
+
10
+ # Target.new from a plan initialized with a hash
11
+ def self.from_asserted_hash(hash)
12
+ inventory = Puppet.lookup(:bolt_inventory)
13
+ inventory.create_target_from_plan(hash)
14
+ end
15
+
16
+ # Target.new from a plan with just a uri
17
+ # rubocop:disable UnusedMethodArgument
18
+ def self.from_asserted_args(uri = nil,
19
+ name = nil,
20
+ target_alias = nil,
21
+ config = nil,
22
+ facts = nil,
23
+ vars = nil,
24
+ features = nil,
25
+ plugin_hooks = nil)
26
+ inventory = Puppet.lookup(:bolt_inventory)
27
+ inventory.create_target_from_plan('uri' => uri)
28
+ end
29
+
30
+ # URI can be passes as nil
31
+ def initialize(uri = nil,
32
+ name = nil,
33
+ target_alias = nil,
34
+ config = nil,
35
+ facts = nil,
36
+ vars = nil,
37
+ features = nil,
38
+ plugin_hooks = nil)
39
+ @name = name
40
+ end
41
+ # rubocop:enable UnusedMethodArgument
42
+
43
+ # Used for munging target + group data
44
+ def target_data_hash
45
+ {
46
+ 'config' => @inventory.targets[@name]['config'],
47
+ 'vars' => @inventory.targets[@name]['vars'],
48
+ 'facts' => @inventory.targets[@name]['facts'],
49
+ 'features' => @inventory.targets[@name]['features'].to_a,
50
+ 'plugin_hooks' => @inventory.targets[@name]['plugin_hooks'],
51
+ 'name' => @inventory.targets[@name]['name'],
52
+ 'uri' => @inventory.targets[@name]['uri'],
53
+ 'alias' => @inventory.targets[@name]['target_alias']
54
+ }
55
+ end
56
+
57
+ # features returns an array to be compatible with plans
58
+ def features
59
+ @inventory.features(self).to_a
60
+ end
61
+
62
+ # Use feature_set internally to access set
63
+ def feature_set
64
+ @inventory.features(self)
65
+ end
66
+
67
+ def vars
68
+ @inventory.vars(self)
69
+ end
70
+
71
+ def facts
72
+ @inventory.facts(self)
73
+ end
74
+
75
+ def to_s
76
+ safe_name
77
+ end
78
+
79
+ def config
80
+ @inventory.target_config(self)
81
+ end
82
+
83
+ def safe_name
84
+ @inventory.targets[@name]['safe_name']
85
+ end
86
+
87
+ def target_alias
88
+ @inventory.targets[@name]['target_alias']
89
+ end
90
+
91
+ def to_h
92
+ options.merge(
93
+ 'name' => name,
94
+ 'uri' => uri,
95
+ 'protocol' => protocol,
96
+ 'user' => user,
97
+ 'password' => password,
98
+ 'host' => host,
99
+ 'port' => port
100
+ )
101
+ end
102
+
103
+ def host
104
+ @inventory.targets[@name]['uri_obj']&.hostname || @inventory.targets[@name]['host']
105
+ end
106
+
107
+ attr_reader :name
108
+
109
+ def uri
110
+ @inventory.targets[@name]['uri']
111
+ end
112
+
113
+ def remote?
114
+ @inventory.targets[@name]['uri_obj']&.scheme == 'remote' || @inventory.targets[@name]['protocol'] == 'remote'
115
+ end
116
+
117
+ def port
118
+ @inventory.targets[@name]['uri_obj']&.port || @inventory.targets[@name]['port']
119
+ end
120
+
121
+ # transport is separate from protocol for remote targets.
122
+ def transport
123
+ remote? ? 'remote' : protocol
124
+ end
125
+
126
+ def protocol
127
+ @inventory.targets[@name]['uri_obj']&.scheme || @inventory.targets[@name]['protocol']
128
+ end
129
+
130
+ def user
131
+ unencode(@inventory.targets[@name]['uri_obj']&.user) || @inventory.targets[@name]['user']
132
+ end
133
+
134
+ def password
135
+ unencode(@inventory.targets[@name]['uri_obj']&.password) || @inventory.targets[@name]['password']
136
+ end
137
+
138
+ def options
139
+ @inventory.targets[@name]['options']
140
+ end
141
+
142
+ def plugin_hooks
143
+ @inventory.targets[@name]['cached_state']['plugin_hooks']
144
+ end
145
+
146
+ def unencode(component)
147
+ Addressable::URI.unencode_component(component)
148
+ end
149
+ private :unencode
150
+ end
151
+
6
152
  class Target
7
153
  attr_reader :options
8
154
  # CODEREVIEW: this feels wrong. The altertative is threading inventory through the
@@ -87,6 +233,7 @@ module Bolt
87
233
  Set.new
88
234
  end
89
235
  end
236
+ alias feature_set features
90
237
 
91
238
  def plugin_hooks
92
239
  if @inventory
data/lib/bolt/task.rb CHANGED
@@ -73,7 +73,7 @@ module Bolt
73
73
  # and any additional files (name and path)
74
74
  def select_implementation(target, provided_features = [])
75
75
  impl = if (impls = implementations)
76
- available_features = target.features + provided_features
76
+ available_features = target.feature_set + provided_features
77
77
  impl = impls.find do |imp|
78
78
  remote_impl = imp['remote']
79
79
  remote_impl = metadata['remote'] if remote_impl.nil?
@@ -52,16 +52,23 @@ module Bolt
52
52
  "[Environment]::SetEnvironmentVariable('#{arg}', @'\n#{val}\n'@)"
53
53
  end
54
54
 
55
+ def quote_string(string)
56
+ "'" + string.gsub("'", "''") + "'"
57
+ end
58
+
55
59
  def execute_process(path, arguments, stdin = nil)
56
- quoted_args = arguments.map do |arg|
57
- "'" + arg.gsub("'", "''") + "'"
58
- end.join(' ')
60
+ quoted_args = arguments.map { |arg| quote_string(arg) }.join(' ')
59
61
 
62
+ quoted_path = if path =~ /^'.*'$/ || path =~ /^".*"$/
63
+ path
64
+ else
65
+ quote_string(path)
66
+ end
60
67
  exec_cmd =
61
68
  if stdin.nil?
62
- "& #{path} #{quoted_args}"
69
+ "& #{quoted_path} #{quoted_args}"
63
70
  else
64
- "@'\n#{stdin}\n'@ | & #{path} #{quoted_args}"
71
+ "@'\n#{stdin}\n'@ | & #{quoted_path} #{quoted_args}"
65
72
  end
66
73
  <<-PS
67
74
  $OutputEncoding = [Console]::OutputEncoding
@@ -163,8 +163,7 @@ module Bolt
163
163
  if Powershell.powershell_file?(remote_task_path) && stdin.nil?
164
164
  conn.execute(Powershell.run_ps_task(arguments, remote_task_path, input_method))
165
165
  else
166
- interpreter = select_interpreter(remote_task_path, target.options['interpreters'])
167
- if interpreter
166
+ if (interpreter = select_interpreter(remote_task_path, target.options['interpreters']))
168
167
  path = interpreter
169
168
  args = [remote_task_path]
170
169
  else
data/lib/bolt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bolt
4
- VERSION = '1.31.1'
4
+ VERSION = '1.32.0'
5
5
  end
@@ -36,7 +36,10 @@
36
36
  ]
37
37
  },
38
38
  "arguments": {
39
- "type": "string"
39
+ "type": "array",
40
+ "items": {
41
+ "type": "string"
42
+ }
40
43
  },
41
44
  "target": { "$ref": "partial:target-any" }
42
45
  },
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bolt_spec/plans/action_stubs'
4
+ require 'bolt_spec/plans/publish_stub'
4
5
  require 'bolt/error'
5
6
  require 'bolt/result_set'
6
7
  require 'bolt/result'
@@ -25,6 +26,7 @@ module BoltSpec
25
26
  @allow_apply = false
26
27
  @modulepath = [modulepath].flatten.map { |path| File.absolute_path(path) }
27
28
  MOCKED_ACTIONS.each { |action| instance_variable_set(:"@#{action}_doubles", {}) }
29
+ @stub_out_message = nil
28
30
  end
29
31
 
30
32
  def module_file_id(file)
@@ -99,6 +101,7 @@ module BoltSpec
99
101
  doub.assert_called(object)
100
102
  end
101
103
  end
104
+ @stub_out_message.assert_called('out::message') if @stub_out_message
102
105
  end
103
106
 
104
107
  MOCKED_ACTIONS.each do |action|
@@ -107,6 +110,10 @@ module BoltSpec
107
110
  end
108
111
  end
109
112
 
113
+ def stub_out_message
114
+ @stub_out_message ||= ActionDouble.new(:PublishStub)
115
+ end
116
+
110
117
  def stub_apply
111
118
  @allow_apply = true
112
119
  end
@@ -133,6 +140,16 @@ module BoltSpec
133
140
 
134
141
  def report_apply(_statements, _resources); end
135
142
 
143
+ def publish_event(event)
144
+ if event[:type] == :message
145
+ unless @stub_out_message
146
+ @error_message = "Unexpected call to 'out::message(#{event[:message]})'"
147
+ raise UnexpectedInvocation, @error_message
148
+ end
149
+ @stub_out_message.process(event[:message])
150
+ end
151
+ end
152
+
136
153
  # Mocked for Apply so it does not compile and execute.
137
154
  def with_node_logging(_description, targets)
138
155
  raise "Unexpected call to apply(#{targets})" unless @allow_apply
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bolt/result'
4
+ require 'bolt/util'
5
+
6
+ module BoltSpec
7
+ module Plans
8
+ class PublishStub < ActionStub
9
+ def return
10
+ raise "return is not implemented for out_message"
11
+ end
12
+
13
+ def return_for_targets(_data)
14
+ raise "return_for_targets is not implemented for out_message"
15
+ end
16
+
17
+ def always_return(_data)
18
+ raise "always_return is not implemented for out_message"
19
+ end
20
+
21
+ def error_with(_data)
22
+ raise "error_with is not implemented for out_message"
23
+ end
24
+
25
+ def matches(message)
26
+ if @invocation[:options] && message != @invocation[:options]
27
+ return false
28
+ end
29
+
30
+ true
31
+ end
32
+
33
+ def call(_event)
34
+ @calls += 1
35
+ end
36
+
37
+ def parameters
38
+ @invocation[:options]
39
+ end
40
+
41
+ # Public methods
42
+
43
+ def with_params(params)
44
+ @invocation[:options] = params
45
+ self
46
+ end
47
+ end
48
+ end
49
+ end
@@ -63,6 +63,8 @@ require 'bolt/pal'
63
63
  # - allow_upload(file), expect_upload(file): expect the identified source file
64
64
  # - allow_apply_prep: allows `apply_prep` to be invoked in the plan but does not allow modifiers
65
65
  # - allow_apply: allows `apply` to be invoked in the plan but does not allow modifiers
66
+ # - allow_out_message, expect_out_message: expect a message to be passed to out::message (only modifiers are
67
+ # be_called_times(n), with_params(params), and not_be_called)
66
68
  #
67
69
  # Stub modifiers:
68
70
  # - be_called_times(n): if allowed, fail if the action is called more than 'n' times
@@ -128,6 +130,12 @@ require 'bolt/pal'
128
130
  # end
129
131
  # expect(run_plan('my_plan', { 'param1' => 10 })).to eq(10)
130
132
  # end
133
+
134
+ # it 'expects multiple messages to out::message' do
135
+ # expect_out_message.be_called_times(2).with_params(message)
136
+ # result = run_plan(plan_name, 'messages' => [message, message])
137
+ # expect(result).to be_ok
138
+ # end
131
139
  # end
132
140
  #
133
141
  # See spec/bolt_spec/plan_spec.rb for more examples.
@@ -172,7 +180,7 @@ module BoltSpec
172
180
  end
173
181
 
174
182
  def run_plan(name, params)
175
- pal = Bolt::PAL.new(config.modulepath, config.hiera_config)
183
+ pal = Bolt::PAL.new(config.modulepath, config.hiera_config, config.boltdir.resource_types)
176
184
  result = pal.run_plan(name, params, executor, inventory, puppetdb_client)
177
185
 
178
186
  if executor.error_message
@@ -221,6 +229,15 @@ module BoltSpec
221
229
  nil
222
230
  end
223
231
 
232
+ def allow_out_message
233
+ executor.stub_out_message.add_stub
234
+ end
235
+ alias allow_any_out_message allow_out_message
236
+
237
+ def expect_out_message
238
+ allow_out_message.expect_call
239
+ end
240
+
224
241
  # Example helpers to mock other run functions
225
242
  # The with_targets method makes sense for all stubs
226
243
  # with_params could be reused for options
data/lib/bolt_spec/run.rb CHANGED
@@ -110,7 +110,7 @@ module BoltSpec
110
110
  else
111
111
  begin
112
112
  unless File.stat(manifest).readable?
113
- raise BOLT::FileError.new("The manifest '#{manifest}' is unreadable", manifest)
113
+ raise Bolt::FileError.new("The manifest '#{manifest}' is unreadable", manifest)
114
114
  end
115
115
  rescue Errno::ENOENT
116
116
  raise Bolt::FileError.new("The manifest '#{manifest}' does not exist", manifest)
@@ -152,7 +152,10 @@ module BoltSpec
152
152
  end
153
153
 
154
154
  def pal
155
- @pal ||= Bolt::PAL.new(config.modulepath, config.hiera_config, config.compile_concurrency)
155
+ @pal ||= Bolt::PAL.new(config.modulepath,
156
+ config.hiera_config,
157
+ config.boltdir.resource_types,
158
+ config.compile_concurrency)
156
159
  end
157
160
 
158
161
  def resolve_targets(target_spec)
@@ -39,7 +39,7 @@ module PlanExecutor
39
39
  # functional will be making changes to Puppet that remove the need for
40
40
  # global Puppet state.
41
41
  # https://github.com/puppetlabs/bolt/blob/master/lib/bolt/pal.rb#L166
42
- @pal = Bolt::PAL.new(config['modulepath'], nil)
42
+ @pal = Bolt::PAL.new(config['modulepath'], nil, nil)
43
43
 
44
44
  @schema = JSON.parse(File.read(File.join(__dir__, 'schemas', 'run_plan.json')))
45
45
  @worker = Concurrent::SingleThreadExecutor.new
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: 1.31.1
4
+ version: 1.32.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Puppet
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-27 00:00:00.000000000 Z
11
+ date: 2019-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: addressable
@@ -330,6 +330,7 @@ files:
330
330
  - bolt-modules/boltlib/lib/puppet/functions/facts.rb
331
331
  - bolt-modules/boltlib/lib/puppet/functions/fail_plan.rb
332
332
  - bolt-modules/boltlib/lib/puppet/functions/get_resources.rb
333
+ - bolt-modules/boltlib/lib/puppet/functions/get_target.rb
333
334
  - bolt-modules/boltlib/lib/puppet/functions/get_targets.rb
334
335
  - bolt-modules/boltlib/lib/puppet/functions/puppetdb_fact.rb
335
336
  - bolt-modules/boltlib/lib/puppet/functions/puppetdb_query.rb
@@ -337,6 +338,7 @@ files:
337
338
  - bolt-modules/boltlib/lib/puppet/functions/run_plan.rb
338
339
  - bolt-modules/boltlib/lib/puppet/functions/run_script.rb
339
340
  - bolt-modules/boltlib/lib/puppet/functions/run_task.rb
341
+ - bolt-modules/boltlib/lib/puppet/functions/set_config.rb
340
342
  - bolt-modules/boltlib/lib/puppet/functions/set_feature.rb
341
343
  - bolt-modules/boltlib/lib/puppet/functions/set_var.rb
342
344
  - bolt-modules/boltlib/lib/puppet/functions/upload_file.rb
@@ -463,6 +465,7 @@ files:
463
465
  - lib/bolt_spec/plans/action_stubs/task_stub.rb
464
466
  - lib/bolt_spec/plans/action_stubs/upload_stub.rb
465
467
  - lib/bolt_spec/plans/mock_executor.rb
468
+ - lib/bolt_spec/plans/publish_stub.rb
466
469
  - lib/bolt_spec/run.rb
467
470
  - lib/logging_extensions/logging.rb
468
471
  - lib/plan_executor/app.rb