bolt 1.34.0 → 1.35.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 +4 -4
- data/bolt-modules/boltlib/lib/puppet/functions/apply_prep.rb +1 -1
- data/bolt-modules/ctrl/lib/puppet/functions/ctrl/do_until.rb +16 -3
- data/lib/bolt/config.rb +1 -1
- data/lib/bolt/inventory.rb +1 -1
- data/lib/bolt/inventory/group.rb +1 -1
- data/lib/bolt/inventory/group2.rb +232 -216
- data/lib/bolt/inventory/inventory2.rb +64 -219
- data/lib/bolt/inventory/target.rb +227 -0
- data/lib/bolt/module.rb +4 -4
- data/lib/bolt/plugin.rb +0 -3
- data/lib/bolt/plugin/module.rb +52 -5
- data/lib/bolt/plugin/task.rb +5 -1
- data/lib/bolt/target.rb +24 -46
- data/lib/bolt/util.rb +18 -0
- data/lib/bolt/version.rb +1 -1
- metadata +3 -5
- data/lib/bolt/plugin/aws_inventory.rb +0 -102
- data/lib/bolt/plugin/terraform.rb +0 -175
- data/lib/bolt/plugin/vault.rb +0 -206
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bolt/inventory/group2'
|
4
|
+
require 'bolt/inventory/target'
|
4
5
|
|
5
6
|
module Bolt
|
6
7
|
class Inventory
|
@@ -23,7 +24,7 @@ module Bolt
|
|
23
24
|
@group_lookup = {}
|
24
25
|
# The targets hash is the canonical source for all targets in inventory
|
25
26
|
@targets = {}
|
26
|
-
@groups.
|
27
|
+
@groups.resolve_string_targets(@groups.target_aliases, @groups.all_targets)
|
27
28
|
collect_groups
|
28
29
|
end
|
29
30
|
|
@@ -49,34 +50,26 @@ module Bolt
|
|
49
50
|
end
|
50
51
|
|
51
52
|
def target_names
|
52
|
-
@groups.
|
53
|
+
@groups.all_targets
|
53
54
|
end
|
54
55
|
# alias for analytics
|
55
56
|
alias node_names target_names
|
56
57
|
|
57
58
|
def get_targets(targets)
|
58
|
-
|
59
|
+
target_array = expand_targets(targets)
|
60
|
+
if target_array.is_a? Array
|
61
|
+
target_array.flatten.uniq(&:name)
|
62
|
+
else
|
63
|
+
[target_array]
|
64
|
+
end
|
59
65
|
end
|
60
66
|
|
61
67
|
def get_target(target)
|
62
|
-
target_array =
|
68
|
+
target_array = get_targets(target)
|
63
69
|
if target_array.count > 1
|
64
70
|
raise ValidationError.new("'#{target}' refers to #{target_array.count} targets", nil)
|
65
71
|
end
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
def add_to_group(targets, desired_group)
|
70
|
-
if group_names.include?(desired_group)
|
71
|
-
targets.each do |target|
|
72
|
-
if group_names.include?(target.name)
|
73
|
-
raise ValidationError.new("Group #{target.name} conflicts with target of the same name", target.name)
|
74
|
-
end
|
75
|
-
add_target(@groups, target, desired_group)
|
76
|
-
end
|
77
|
-
else
|
78
|
-
raise ValidationError.new("Group #{desired_group} does not exist in inventory", nil)
|
79
|
-
end
|
72
|
+
target_array.first
|
80
73
|
end
|
81
74
|
|
82
75
|
def data_hash
|
@@ -92,79 +85,9 @@ module Bolt
|
|
92
85
|
end
|
93
86
|
|
94
87
|
#### PRIVATE ####
|
95
|
-
|
96
|
-
|
97
|
-
def groups_in(target_name)
|
98
|
-
@groups.data_for(target_name)['groups'] || {}
|
99
|
-
end
|
100
|
-
private :groups_in
|
101
|
-
|
102
|
-
# Look for _plugins
|
103
|
-
def config_plugin(data)
|
104
|
-
Bolt::Util.walk_vals(data) do |val|
|
105
|
-
if val.is_a?(Concurrent::Delay)
|
106
|
-
# We should raise any error from the delay now
|
107
|
-
val.value!
|
108
|
-
else
|
109
|
-
val
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
private :config_plugin
|
114
|
-
|
115
|
-
# Pass a target to get_targets for a public version of this
|
116
|
-
def update_target(target)
|
117
|
-
# Ensure all targets in inventory are included in the all group.
|
118
|
-
unless @groups.target_names.include?(target.name)
|
119
|
-
add_to_group([target], 'all')
|
120
|
-
end
|
121
|
-
|
122
|
-
# Get merged data between targets and groups
|
123
|
-
data = @groups.data_for(target.name)
|
124
|
-
data ||= {}
|
125
|
-
|
126
|
-
unless data['config']
|
127
|
-
@logger.debug("Did not find config for #{target.name} in inventory")
|
128
|
-
data['config'] = {}
|
129
|
-
end
|
130
|
-
|
131
|
-
# Add defaults for special 'localhost' target (currently just config and features)
|
132
|
-
if target.name == 'localhost'
|
133
|
-
data = Bolt::Inventory.localhost_defaults(data)
|
134
|
-
end
|
135
|
-
|
136
|
-
# Data from inventory
|
137
|
-
data['config'] = config_plugin(data['config'])
|
138
|
-
# Data from set_config (make sure to resolve plugins)
|
139
|
-
resolved_target_config = config_plugin(@targets[target.name]['config'] || {})
|
140
|
-
data['config'] = Bolt::Util.deep_merge(data['config'], resolved_target_config)
|
141
|
-
|
142
|
-
# Use Config object to ensure config section is treated consistently with config file
|
143
|
-
conf = @config.deep_clone
|
144
|
-
conf.update_from_inventory(data['config'])
|
145
|
-
conf.validate
|
146
|
-
|
147
|
-
# Recompute the target cached state with the merged data
|
148
|
-
update_target_state(target, conf, data)
|
149
|
-
|
150
|
-
unless target.transport.nil? || Bolt::TRANSPORTS.include?(target.transport.to_sym)
|
151
|
-
raise Bolt::UnknownTransportError.new(target.transport, target.uri)
|
152
|
-
end
|
153
|
-
|
154
|
-
target
|
155
|
-
end
|
156
|
-
private :update_target
|
157
|
-
|
158
|
-
# This algorithm for getting a flat list of targets is used several times.
|
159
|
-
def flat_target_list(targets)
|
160
|
-
target_array = expand_targets(targets)
|
161
|
-
if target_array.is_a? Array
|
162
|
-
target_array.flatten.uniq(&:name)
|
163
|
-
else
|
164
|
-
[target_array]
|
165
|
-
end
|
88
|
+
def group_data_for(target_name)
|
89
|
+
@groups.group_collect(target_name)
|
166
90
|
end
|
167
|
-
private :flat_target_list
|
168
91
|
|
169
92
|
# If target is a group name, expand it to the members of that group.
|
170
93
|
# Else match against targets in inventory by name or alias.
|
@@ -172,13 +95,13 @@ module Bolt
|
|
172
95
|
# Else fall back to [target] if no matches are found.
|
173
96
|
def resolve_name(target)
|
174
97
|
if (group = @group_lookup[target])
|
175
|
-
group.
|
98
|
+
group.all_targets
|
176
99
|
else
|
177
100
|
# Try to wildcard match targets in inventory
|
178
101
|
# Ignore case because hostnames are generally case-insensitive
|
179
102
|
regexp = Regexp.new("^#{Regexp.escape(target).gsub('\*', '.*?')}$", Regexp::IGNORECASE)
|
180
103
|
|
181
|
-
targets = @groups.
|
104
|
+
targets = @groups.all_targets.select { |targ| targ =~ regexp }
|
182
105
|
targets += @groups.target_aliases.select { |target_alias, _target| target_alias =~ regexp }.values
|
183
106
|
|
184
107
|
if targets.empty?
|
@@ -201,8 +124,12 @@ module Bolt
|
|
201
124
|
targets.split(/[[:space:],]+/).reject(&:empty?).map do |name|
|
202
125
|
ts = resolve_name(name)
|
203
126
|
ts.map do |t|
|
204
|
-
# If the target
|
205
|
-
|
127
|
+
# If the target doesn't exist, evaluate it from the inventory.
|
128
|
+
# Then return a Bolt::Target2.
|
129
|
+
unless @targets.key?(t)
|
130
|
+
@targets[t] = create_target_from_inventory(t)
|
131
|
+
end
|
132
|
+
Bolt::Target2.new(t, self)
|
206
133
|
end
|
207
134
|
end
|
208
135
|
end
|
@@ -211,9 +138,9 @@ module Bolt
|
|
211
138
|
|
212
139
|
def add_target(current_group, target, desired_group)
|
213
140
|
if current_group.name == desired_group
|
214
|
-
current_group.add_target(target
|
141
|
+
current_group.add_target(target)
|
215
142
|
@groups.validate
|
216
|
-
|
143
|
+
target.invalidate_group_cache!
|
217
144
|
return true
|
218
145
|
end
|
219
146
|
# Recurse on children Groups if not desired_group
|
@@ -223,170 +150,88 @@ module Bolt
|
|
223
150
|
end
|
224
151
|
private :add_target
|
225
152
|
|
226
|
-
#
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
data['safe_name'] = data['name']
|
238
|
-
else
|
239
|
-
data['name'] = target_name
|
240
|
-
data['safe_name'] = if data['uri_obj']
|
241
|
-
data['uri_obj'].omit(:password).to_str.sub(%r{^//}, '')
|
242
|
-
else
|
243
|
-
target_name
|
244
|
-
end
|
245
|
-
end
|
246
|
-
unless data['name'].ascii_only?
|
247
|
-
raise ValidationError.new("Target name must be ASCII characters: #{data['name']}", nil)
|
248
|
-
end
|
249
|
-
# Data set on target itself (either in inventory, target.new or with set_config)
|
250
|
-
data['config'] ||= {}
|
251
|
-
data['vars'] ||= {}
|
252
|
-
data['facts'] ||= {}
|
253
|
-
data['features'] = data['features'] ? Set.new(data['features']) : Set.new
|
254
|
-
data['groups'] ||= []
|
255
|
-
data['options'] ||= {}
|
256
|
-
data['plugin_hooks'] ||= {}
|
257
|
-
data['target_alias'] ||= []
|
258
|
-
|
259
|
-
# Every call to update_target will rebuild this state based on merging together target, group, and config data
|
260
|
-
data['cached_state'] = {}
|
261
|
-
|
262
|
-
target = Target2.new(nil, data['name'])
|
263
|
-
target.inventory = self
|
264
|
-
data['self'] = target
|
265
|
-
@targets[data['name']] = data
|
153
|
+
# Pull in a target definition from the inventory file and evaluate any
|
154
|
+
# associated references. This is used when a target is resolved by
|
155
|
+
# get_targets.
|
156
|
+
def create_target_from_inventory(target_name)
|
157
|
+
target_data = @groups.target_collect(target_name) || { 'uri' => target_name }
|
158
|
+
|
159
|
+
target = Bolt::Inventory::Target.new(target_data, self)
|
160
|
+
@targets[target.name] = target
|
161
|
+
|
162
|
+
add_to_group([target], 'all')
|
163
|
+
|
266
164
|
target
|
267
165
|
end
|
268
|
-
private :create_target
|
269
166
|
|
167
|
+
# Add a brand new target, overriding any existing target with the same
|
168
|
+
# name. This method does not honor target config from the inventory. This
|
169
|
+
# is used when Target.new is called from a plan.
|
270
170
|
def create_target_from_plan(data)
|
271
|
-
t_name = data['name'] || data['uri']
|
272
|
-
|
273
171
|
# If target already exists, delete old and replace with new, otherwise add to new to all group
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
update_target(t)
|
281
|
-
add_to_group([t], 'all')
|
172
|
+
new_target = Bolt::Inventory::Target.new(data, self)
|
173
|
+
existing_target = @targets.key?(new_target.name)
|
174
|
+
@targets[new_target.name] = new_target
|
175
|
+
|
176
|
+
unless existing_target
|
177
|
+
add_to_group([new_target], 'all')
|
282
178
|
end
|
283
|
-
|
179
|
+
|
180
|
+
new_target
|
284
181
|
end
|
285
182
|
|
286
|
-
def
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
183
|
+
def add_to_group(targets, desired_group)
|
184
|
+
if group_names.include?(desired_group)
|
185
|
+
targets.each do |target|
|
186
|
+
if group_names.include?(target.name)
|
187
|
+
raise ValidationError.new("Group #{target.name} conflicts with target of the same name", target.name)
|
188
|
+
end
|
189
|
+
# Add the inventory copy of the target
|
190
|
+
add_target(@groups, @targets[target.name], desired_group)
|
191
|
+
end
|
295
192
|
else
|
296
|
-
|
297
|
-
Addressable::URI.parse("//#{string}")
|
193
|
+
raise ValidationError.new("Group #{desired_group} does not exist in inventory", nil)
|
298
194
|
end
|
299
|
-
rescue Addressable::URI::InvalidURIError => e
|
300
|
-
raise Bolt::ParseError, "Could not parse target URI: #{e.message}"
|
301
195
|
end
|
302
196
|
|
303
197
|
def set_var(target, var_hash)
|
304
|
-
@targets[target.name]
|
305
|
-
update_target(target)
|
198
|
+
@targets[target.name].set_var(var_hash)
|
306
199
|
end
|
307
200
|
|
308
201
|
def vars(target)
|
309
|
-
@targets[target.name]
|
202
|
+
@targets[target.name].vars
|
310
203
|
end
|
311
204
|
|
312
205
|
def add_facts(target, new_facts = {})
|
313
|
-
@targets[target.name]
|
314
|
-
update_target(target)
|
206
|
+
@targets[target.name].add_facts(new_facts)
|
315
207
|
# rubocop:disable Style/GlobalVars
|
316
208
|
$future ? target : facts(target)
|
317
209
|
# rubocop:enable Style/GlobalVars
|
318
210
|
end
|
319
211
|
|
320
212
|
def facts(target)
|
321
|
-
@targets[target.name]
|
213
|
+
@targets[target.name].facts
|
322
214
|
end
|
323
215
|
|
324
216
|
def set_feature(target, feature, value = true)
|
325
|
-
|
326
|
-
@targets[target.name]['features'] << feature
|
327
|
-
else
|
328
|
-
@targets[target.name]['features'].delete(feature)
|
329
|
-
end
|
330
|
-
update_target(target)
|
217
|
+
@targets[target.name].set_feature(feature, value)
|
331
218
|
end
|
332
219
|
|
333
220
|
def features(target)
|
334
|
-
|
335
|
-
Set.new(@targets[target.name]['cached_state']['features'])
|
336
|
-
else
|
337
|
-
Set.new
|
338
|
-
end
|
221
|
+
@targets[target.name].features
|
339
222
|
end
|
340
223
|
|
341
224
|
def plugin_hooks(target)
|
342
|
-
@targets[target.name]
|
225
|
+
@targets[target.name].plugin_hooks
|
343
226
|
end
|
344
227
|
|
345
228
|
def set_config(target, key_or_key_path, value)
|
346
|
-
|
347
|
-
@targets[target.name]['config'] = @targets[target.name]['config'].merge(config)
|
348
|
-
update_target(target)
|
229
|
+
@targets[target.name].set_config(key_or_key_path, value)
|
349
230
|
end
|
350
231
|
|
351
232
|
def target_config(target)
|
352
|
-
@targets[target.name]
|
353
|
-
end
|
354
|
-
|
355
|
-
def build_config_hash(key_or_key_path, value)
|
356
|
-
# https://stackoverflow.com/questions/5095077/ruby-convert-array-to-nested-hash
|
357
|
-
key_or_key_path.reverse.inject(value) { |acc, key| { key => acc } }
|
358
|
-
end
|
359
|
-
private :build_config_hash
|
360
|
-
|
361
|
-
def update_target_state(target, conf, merged_data)
|
362
|
-
@targets[target.name]['protocol'] = conf.transport_conf[:transport]
|
363
|
-
t_conf = conf.transport_conf[:transports][target.transport.to_sym] || {}
|
364
|
-
@targets[target.name]['user'] = t_conf['user']
|
365
|
-
@targets[target.name]['password'] = t_conf['password']
|
366
|
-
@targets[target.name]['port'] = t_conf['port']
|
367
|
-
@targets[target.name]['host'] = t_conf['host']
|
368
|
-
@targets[target.name]['options'] = t_conf
|
369
|
-
|
370
|
-
@targets[target.name]['cached_state'] = merged_data
|
371
|
-
|
372
|
-
target_facts = @targets[target.name]['facts'] || {}
|
373
|
-
new_facts = merged_data['facts'] || {}
|
374
|
-
@targets[target.name]['cached_state']['facts'] = Bolt::Util.deep_merge(new_facts, target_facts)
|
375
|
-
|
376
|
-
target_vars = @targets[target.name]['vars'] || {}
|
377
|
-
new_vars = merged_data['vars'] || {}
|
378
|
-
@targets[target.name]['cached_state']['vars'] = new_vars.merge(target_vars)
|
379
|
-
|
380
|
-
target_features = Set.new(@targets[target.name]['features'])
|
381
|
-
new_features = Set.new(merged_data['features'])
|
382
|
-
@targets[target.name]['cached_state']['features'] = new_features.merge(target_features)
|
383
|
-
|
384
|
-
target_plugin_hooks = @targets[target.name]['plugin_hooks'] || {}
|
385
|
-
new_plugin_hooks = merged_data['plugin_hooks'] || {}
|
386
|
-
plugin_hooks_from_inv = new_plugin_hooks.merge(target_plugin_hooks)
|
387
|
-
@targets[target.name]['cached_state']['plugin_hooks'] = conf.plugin_hooks.merge(plugin_hooks_from_inv)
|
233
|
+
@targets[target.name].config
|
388
234
|
end
|
389
|
-
private :update_target_state
|
390
235
|
end
|
391
236
|
end
|
392
237
|
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
class Inventory
|
5
|
+
# This class represents the active state of a target within the inventory.
|
6
|
+
class Target
|
7
|
+
attr_reader :name, :uri, :safe_name
|
8
|
+
|
9
|
+
def initialize(target_data, inventory)
|
10
|
+
unless target_data['name'] || target_data['uri']
|
11
|
+
raise Bolt::Inventory::ValidationError.new("Target must have either a name or uri", nil)
|
12
|
+
end
|
13
|
+
|
14
|
+
@logger = Logging.logger[inventory]
|
15
|
+
|
16
|
+
# If the target isn't mentioned by any groups, it won't have a uri or
|
17
|
+
# name and we will use the target_name as both
|
18
|
+
@uri = target_data['uri']
|
19
|
+
@uri_obj = self.class.parse_uri(@uri)
|
20
|
+
|
21
|
+
# If the target has a name, use that as the safe name. Otherwise, turn
|
22
|
+
# the uri into a safe name by omitting the password.
|
23
|
+
if target_data['name']
|
24
|
+
@name = target_data['name']
|
25
|
+
@safe_name = target_data['name']
|
26
|
+
else
|
27
|
+
@name = @uri
|
28
|
+
@safe_name = @uri_obj.omit(:password).to_str.sub(%r{^//}, '')
|
29
|
+
end
|
30
|
+
|
31
|
+
@config = target_data['config'] || {}
|
32
|
+
@vars = target_data['vars'] || {}
|
33
|
+
@facts = target_data['facts'] || {}
|
34
|
+
@features = target_data['features'] || Set.new
|
35
|
+
@options = target_data['options'] || {}
|
36
|
+
@plugin_hooks = target_data['plugin_hooks'] || {}
|
37
|
+
@target_alias = target_data['target_alias'] || []
|
38
|
+
|
39
|
+
@inventory = inventory
|
40
|
+
|
41
|
+
validate
|
42
|
+
end
|
43
|
+
|
44
|
+
def vars
|
45
|
+
# XXX Return vars from the cache
|
46
|
+
group_cache['vars'].merge(@vars)
|
47
|
+
end
|
48
|
+
|
49
|
+
# This method isn't actually an accessor and we want the name to
|
50
|
+
# correspond to the Puppet function
|
51
|
+
# rubocop:disable Naming/AccessorMethodName
|
52
|
+
def set_var(var_hash)
|
53
|
+
@vars.merge!(var_hash)
|
54
|
+
end
|
55
|
+
# rubocop:enable Naming/AccessorMethodName
|
56
|
+
|
57
|
+
def facts
|
58
|
+
# XXX Return facts from the cache
|
59
|
+
Bolt::Util.deep_merge(group_cache['facts'], @facts)
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_facts(new_facts = {})
|
63
|
+
@facts = Bolt::Util.deep_merge(@facts, new_facts)
|
64
|
+
end
|
65
|
+
|
66
|
+
def features
|
67
|
+
group_cache['features'] + @features
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_feature(feature, value = true)
|
71
|
+
if value
|
72
|
+
@features << feature
|
73
|
+
else
|
74
|
+
@features.delete(feature)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def plugin_hooks
|
79
|
+
# Merge plugin_hooks from the config file with any defined by the group
|
80
|
+
# or assigned dynamically to the target
|
81
|
+
@inventory.config.plugin_hooks.merge(group_cache['plugin_hooks']).merge(@plugin_hooks)
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_config(key_or_key_path, value)
|
85
|
+
if key_or_key_path.empty?
|
86
|
+
@config = value
|
87
|
+
else
|
88
|
+
*path, key = Array(key_or_key_path)
|
89
|
+
location = path.inject(@config) do |working_object, p|
|
90
|
+
working_object[p] ||= {}
|
91
|
+
end
|
92
|
+
location[key] = value
|
93
|
+
end
|
94
|
+
invalidate_config_cache!
|
95
|
+
end
|
96
|
+
|
97
|
+
def invalidate_group_cache!
|
98
|
+
@group_cache = nil
|
99
|
+
# The config cache depends on the group cache, so invalidate it as well
|
100
|
+
invalidate_config_cache!
|
101
|
+
end
|
102
|
+
|
103
|
+
def invalidate_config_cache!
|
104
|
+
@transport_config_cache = nil
|
105
|
+
end
|
106
|
+
|
107
|
+
# Computing the transport config is expensive as it requires cloning the
|
108
|
+
# base config, so we cache the effective config
|
109
|
+
def transport_config_cache
|
110
|
+
if @transport_config_cache.nil?
|
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
|
117
|
+
@transport_config_cache = {
|
118
|
+
'transport' => config.transport_conf[:transport],
|
119
|
+
'transports' => config.transport_conf[:transports]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
@transport_config_cache
|
124
|
+
end
|
125
|
+
|
126
|
+
# Validate the target. This implicitly also primes the group and config
|
127
|
+
# caches and resolves any config references in the target's groups.
|
128
|
+
def validate
|
129
|
+
unless name.ascii_only?
|
130
|
+
raise Bolt::Inventory::ValidationError.new("Target name must be ASCII characters: #{@name}", nil)
|
131
|
+
end
|
132
|
+
|
133
|
+
unless transport.nil? || Bolt::TRANSPORTS.include?(transport.to_sym)
|
134
|
+
raise Bolt::UnknownTransportError.new(transport, uri)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def host
|
139
|
+
@uri_obj.hostname || transport_config['host']
|
140
|
+
end
|
141
|
+
|
142
|
+
def port
|
143
|
+
@uri_obj.port || transport_config['port']
|
144
|
+
end
|
145
|
+
|
146
|
+
def protocol
|
147
|
+
transport
|
148
|
+
end
|
149
|
+
|
150
|
+
def transport
|
151
|
+
@uri_obj.scheme || transport_config_cache['transport']
|
152
|
+
end
|
153
|
+
|
154
|
+
def user
|
155
|
+
Addressable::URI.unencode_component(@uri_obj.user) || transport_config['user']
|
156
|
+
end
|
157
|
+
|
158
|
+
def password
|
159
|
+
Addressable::URI.unencode_component(@uri_obj.password) || transport_config['password']
|
160
|
+
end
|
161
|
+
|
162
|
+
def options
|
163
|
+
transport_config.dup
|
164
|
+
end
|
165
|
+
|
166
|
+
# We only want to look up transport config keys for the configured
|
167
|
+
# transport
|
168
|
+
def transport_config
|
169
|
+
transport_config_cache['transports'][transport.to_sym]
|
170
|
+
end
|
171
|
+
|
172
|
+
def config
|
173
|
+
Bolt::Util.deep_merge(group_cache['config'], @config)
|
174
|
+
end
|
175
|
+
|
176
|
+
def group_cache
|
177
|
+
if @group_cache.nil?
|
178
|
+
group_data = @inventory.group_data_for(@name)
|
179
|
+
|
180
|
+
unless group_data && group_data['config']
|
181
|
+
@logger.debug("Did not find config for #{self} in inventory")
|
182
|
+
end
|
183
|
+
|
184
|
+
group_data ||= {
|
185
|
+
'config' => {},
|
186
|
+
'vars' => {},
|
187
|
+
'facts' => {},
|
188
|
+
'features' => Set.new,
|
189
|
+
'options' => {},
|
190
|
+
'plugin_hooks' => {},
|
191
|
+
'target_alias' => []
|
192
|
+
}
|
193
|
+
|
194
|
+
# This should be handled by `get_targets`
|
195
|
+
if @name == 'localhost'
|
196
|
+
group_data = Bolt::Inventory.localhost_defaults(group_data)
|
197
|
+
end
|
198
|
+
|
199
|
+
@group_cache = group_data
|
200
|
+
end
|
201
|
+
|
202
|
+
@group_cache
|
203
|
+
end
|
204
|
+
|
205
|
+
def to_s
|
206
|
+
@safe_name
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.parse_uri(string)
|
210
|
+
require 'addressable/uri'
|
211
|
+
if string.nil?
|
212
|
+
Addressable::URI.new
|
213
|
+
# Forbid empty uri
|
214
|
+
elsif string.empty?
|
215
|
+
raise Bolt::ParseError, "Could not parse target URI: URI is empty string"
|
216
|
+
elsif string =~ %r{^[^:]+://}
|
217
|
+
Addressable::URI.parse(string)
|
218
|
+
else
|
219
|
+
# Initialize with an empty scheme to ensure we parse the hostname correctly
|
220
|
+
Addressable::URI.parse("//#{string}")
|
221
|
+
end
|
222
|
+
rescue Addressable::URI::InvalidURIError => e
|
223
|
+
raise Bolt::ParseError, "Could not parse target URI: #{e.message}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|