vagrant-ansible_auto 0.1.5 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +172 -0
  5. data/README.md +53 -12
  6. data/Rakefile +9 -7
  7. data/TODO.md +14 -0
  8. data/Vagrantfile +37 -15
  9. data/lib/vagrant/ansible_auto/cap/guest/posix/check_open_port.rb +22 -3
  10. data/lib/vagrant/ansible_auto/cap/guest/posix/executable_installed.rb +10 -2
  11. data/lib/vagrant/ansible_auto/cap/guest/posix/gateway_addresses.rb +8 -23
  12. data/lib/vagrant/ansible_auto/cap/guest/posix/private_key.rb +16 -1
  13. data/lib/vagrant/ansible_auto/cap/guest/posix/public_key.rb +18 -3
  14. data/lib/vagrant/ansible_auto/cap/guest/posix/ssh_server_address.rb +22 -12
  15. data/lib/vagrant/ansible_auto/cap/guest/posix.rb +16 -0
  16. data/lib/vagrant/ansible_auto/command/inventory.rb +37 -11
  17. data/lib/vagrant/ansible_auto/command/root.rb +34 -31
  18. data/lib/vagrant/ansible_auto/config.rb +74 -33
  19. data/lib/vagrant/ansible_auto/errors.rb +30 -1
  20. data/lib/vagrant/ansible_auto/host.rb +123 -34
  21. data/lib/vagrant/ansible_auto/inventory.rb +196 -34
  22. data/lib/vagrant/ansible_auto/plugin.rb +23 -8
  23. data/lib/vagrant/ansible_auto/provisioner.rb +121 -79
  24. data/lib/vagrant/ansible_auto/util/config.rb +61 -0
  25. data/lib/vagrant/ansible_auto/util/hash_with_indifferent_access.rb +58 -0
  26. data/lib/vagrant/ansible_auto/util/keys.rb +49 -0
  27. data/lib/vagrant/ansible_auto/util/shell_quote.rb +24 -0
  28. data/lib/vagrant/ansible_auto/version.rb +2 -1
  29. data/lib/vagrant/ansible_auto.rb +15 -0
  30. data/locales/en.yml +34 -0
  31. data/spec/spec_helper.rb +5 -85
  32. data/spec/support/context.rb +111 -0
  33. data/spec/support/matchers.rb +45 -0
  34. data/spec/unit/vagrant/ansible_auto/config_spec.rb +72 -0
  35. data/spec/unit/vagrant/ansible_auto/host_spec.rb +131 -0
  36. data/spec/unit/vagrant/ansible_auto/inventory_spec.rb +349 -0
  37. data/spec/unit/vagrant/ansible_auto/provisioner_spec.rb +248 -0
  38. data/spec/unit/vagrant/ansible_auto/util/config_spec.rb +63 -0
  39. data/spec/unit/vagrant/ansible_auto/util/keys_spec.rb +66 -0
  40. data/vagrant-ansible_auto.gemspec +6 -4
  41. data/vagrant-spec.config.rb +3 -0
  42. data/yard/extensions.rb +45 -0
  43. metadata +36 -11
  44. data/Vagrantfile2 +0 -4
  45. data/Vagrantfile3 +0 -8
  46. data/Vagrantfile4 +0 -31
  47. data/lib/vagrant/ansible_auto/cap/guest/posix/bash_installed.rb +0 -30
  48. data/lib/vagrant/ansible_auto/util.rb +0 -24
  49. data/spec/vagrant/ansible_auto/host_spec.rb +0 -43
  50. data/spec/vagrant/ansible_auto/inventory_spec.rb +0 -79
@@ -0,0 +1,349 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'vagrant/ansible_auto/errors'
6
+ require 'vagrant/ansible_auto/inventory'
7
+
8
+ describe VagrantPlugins::AnsibleAuto::Inventory do
9
+ include_context 'inventory'
10
+
11
+ let(:inventory_hosts) { %w[db bastion firewall] }
12
+
13
+ let(:complex_group) do
14
+ {
15
+ hosts: inventory_hosts,
16
+ vars: {
17
+ this: 'n',
18
+ uh: 'that',
19
+ n: 'uh'
20
+ },
21
+ children: %w[staging qa]
22
+ }
23
+ end
24
+
25
+ describe '#add_group' do
26
+ it 'adds a list of hosts to the group cache' do
27
+ inventory.add_group(:mygroup, *inventory_hosts)
28
+ inventory.groups.tap do |groups|
29
+ expect(groups).to have_key('mygroup')
30
+ expect(groups['mygroup']).to include(*inventory_hosts)
31
+ end
32
+ end
33
+
34
+ it 'appends new hosts to existing groups' do
35
+ inventory.add_group(:mygroup, *inventory_hosts)
36
+ inventory.add_group(:mygroup, 'repo')
37
+ inventory.groups.tap do |groups|
38
+ expect(groups).to have_key('mygroup')
39
+ expect(groups['mygroup']).to include('repo', *inventory_hosts)
40
+ end
41
+ end
42
+
43
+ it 'recognizes a hash in the style returned by executable inventories' do
44
+ inventory.add_group(:mygroup, complex_group)
45
+
46
+ inventory.groups.tap do |groups|
47
+ expect(groups).to have_key('mygroup')
48
+ expect(groups['mygroup']).to include(*inventory_hosts)
49
+ end
50
+
51
+ inventory.vars.tap do |vars|
52
+ expect(vars).to have_key('mygroup')
53
+ expect(vars['mygroup']).to include(this: 'n', uh: 'that', n: 'uh')
54
+ end
55
+
56
+ inventory.children.tap do |children|
57
+ expect(children).to have_key('mygroup')
58
+ expect(children['mygroup']).to include('staging', 'qa')
59
+ end
60
+ end
61
+
62
+ context 'given an invalid group name' do
63
+ it 'raises an error' do
64
+ expect { inventory.add_group('_') }.to raise_error do |error|
65
+ expect(error).to be_a(VagrantPlugins::AnsibleAuto::InvalidGroupNameError)
66
+ expect(error.message).to match(/_ is not a valid group name/)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe '#add_host' do
73
+ include_context 'host'
74
+
75
+ it 'adds a host to the host cache' do
76
+ inventory.add_host(host)
77
+ expect(inventory.hosts).to include(host)
78
+ end
79
+
80
+ it 'does not allow duplicates' do
81
+ 2.times { inventory.add_host(host) }
82
+ expect(inventory.hosts.size).to eq 1
83
+ end
84
+
85
+ it 'coerces instances of Vagrant::Machine to instances of Host' do
86
+ inventory.add_host(machine)
87
+ expect(inventory.hosts).to all(be_a(VagrantPlugins::AnsibleAuto::Host))
88
+ end
89
+
90
+ it 'coerces names + hostvars to instances of Host' do
91
+ inventory.add_host('mymachine', ssh_host: 'foo.bar.net')
92
+ mymachine = VagrantPlugins::AnsibleAuto::Host.new('mymachine', ssh_host: 'foo.bar.net')
93
+ expect(inventory.hosts).to all(be_a(VagrantPlugins::AnsibleAuto::Host))
94
+ expect(inventory.hosts).to include(mymachine)
95
+ end
96
+
97
+ context 'given an object that cannot be converted to a Host or HostMachine' do
98
+ it 'raises an error' do
99
+ expect { inventory.add_host(6) }.to raise_error(
100
+ VagrantPlugins::AnsibleAuto::Errors::InvalidHostTypeError,
101
+ /cannot add object of type \w+ as inventory host/
102
+ )
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#groups=' do
108
+ let(:new_groups) do
109
+ {
110
+ 'foo' => %w[bar baz quux],
111
+ 'pink' => %w[elephants on parade]
112
+ }
113
+ end
114
+
115
+ before do
116
+ inventory.groups = new_groups
117
+ end
118
+
119
+ it 'assigns the groups to the inventory' do
120
+ expect(inventory.groups['foo']).to include('bar', 'baz', 'quux')
121
+ expect(inventory.groups['pink']).to include('elephants', 'on', 'parade')
122
+ end
123
+
124
+ context 'given a :vars group' do
125
+ let(:group_vars) do
126
+ {
127
+ 'this' => 'that',
128
+ 'some' => 'thing'
129
+ }
130
+ end
131
+
132
+ let(:new_groups) do
133
+ {
134
+ 'foo' => %w[mee maw],
135
+ 'foo:vars' => group_vars
136
+ }
137
+ end
138
+
139
+ it 'assigns the vars to the given group' do
140
+ expect(inventory.vars['foo']).to include(group_vars)
141
+ end
142
+ end
143
+
144
+ context 'given a :children group' do
145
+ let(:group_children) do
146
+ %w[baz quux]
147
+ end
148
+
149
+ let(:new_groups) do
150
+ {
151
+ 'foo' => %w[d chain],
152
+ 'foo:children' => group_children
153
+ }
154
+ end
155
+
156
+ it 'assigns the provided groups as children of the parent group' do
157
+ expect(inventory.children['foo']).to include(*group_children)
158
+ end
159
+ end
160
+
161
+ context 'given a colon-separated group name without :vars or :children' do
162
+ let(:new_groups) do
163
+ {
164
+ 'bleep' => %w[ing computer],
165
+ 'bleep:bloop' => %w[er reel]
166
+ }
167
+ end
168
+
169
+ it 'treats the group heading as a simple group name' do
170
+ expect(inventory.groups).to include('bleep:bloop')
171
+ end
172
+ end
173
+
174
+ context 'given a colon-separated group with backslash-escaped :vars or :children' do
175
+ let(:new_groups) do
176
+ {
177
+ 'bleep' => %w[ing computer],
178
+ 'bleep\:vars' => %w[ity lacrosse],
179
+ 'bleep\:children' => %w[of the corn]
180
+ }
181
+ end
182
+
183
+ it 'treats the group heading as a simple group name' do
184
+ expect(inventory.groups).to include('bleep\:vars')
185
+ expect(inventory.groups).to include('bleep\:children')
186
+ expect(inventory.vars[:bleep]).to be_empty
187
+ expect(inventory.children[:bleep]).to be_empty
188
+ end
189
+ end
190
+ end
191
+
192
+ describe '#groups' do
193
+ it 'returns a hash of sets' do
194
+ expect(inventory.groups).to be_a(Hash)
195
+ expect(inventory.groups[:foo]).to be_a(Set)
196
+ end
197
+ end
198
+
199
+ describe '#hosts=' do
200
+ it 'sets the hosts for the inventory' do
201
+ inventory.hosts = %w[huey dewey louie]
202
+ expect(inventory.hosts.map(&:name)).to eq(%w[huey dewey louie])
203
+ end
204
+
205
+ it 'wipes out any existing hosts' do
206
+ inventory.hosts = %w[huey dewey louie]
207
+ expect(inventory.hosts.map(&:name)).to eq(%w[huey dewey louie])
208
+ inventory.hosts = %w[heckle jeckle]
209
+ expect(inventory.hosts.map(&:name)).to eq(%w[heckle jeckle])
210
+ end
211
+ end
212
+
213
+ describe '#hosts' do
214
+ it 'returns a set' do
215
+ expect(inventory.hosts).to be_a(Set)
216
+ end
217
+ end
218
+
219
+ describe '#vars=' do
220
+ it 'sets the vars for the inventory' do
221
+ inventory.vars = { bah: { hum: 'bug' } }
222
+ expect(inventory.vars).to eq('bah' => { 'hum' => 'bug' })
223
+ end
224
+
225
+ it 'wipes out existing vars' do
226
+ inventory.vars = { bah: { hum: 'bug' } }
227
+ expect(inventory.vars).to eq('bah' => { 'hum' => 'bug' })
228
+ inventory.vars = { bah: { ram: 'ewe' } }
229
+ expect(inventory.vars).to eq('bah' => { 'ram' => 'ewe' })
230
+ end
231
+ end
232
+
233
+ describe '#vars' do
234
+ it 'returns a hash of hashes' do
235
+ expect(inventory.vars).to be_a(Hash)
236
+ expect(inventory.vars[:meh]).to be_a(Hash)
237
+ end
238
+ end
239
+
240
+ describe '#children=' do
241
+ it 'sets the group children for the inventory' do
242
+ inventory.children = { cronus: ['zeus'] }
243
+ expect(inventory.children.keys).to include('cronus')
244
+ expect(inventory.children[:cronus]).to include('zeus')
245
+ end
246
+
247
+ it 'wipes out any existing children' do
248
+ inventory.children = { cronus: ['zeus'] }
249
+ expect(inventory.children.keys).to include('cronus')
250
+ expect(inventory.children[:cronus]).to include('zeus')
251
+ inventory.children = { saturn: ['jupiter'] }
252
+ expect(inventory.children.keys).to include('saturn')
253
+ expect(inventory.children.keys).not_to include('cronus')
254
+ expect(inventory.children[:saturn]).to include('jupiter')
255
+ expect(inventory.children[:cronus]).not_to include('zeus')
256
+ end
257
+ end
258
+
259
+ describe '#children' do
260
+ it 'returns a hash of sets' do
261
+ expect(inventory.children).to be_a(Hash)
262
+ expect(inventory.children[:quux]).to be_a(Set)
263
+ end
264
+ end
265
+
266
+ describe 'merge' do
267
+ let(:inventory2) { inventory.clone }
268
+
269
+ it 'returns a new inventory with hosts, groups, vars, and children merged' do
270
+ inventory.hosts = %w[huey dewey louie]
271
+ inventory.groups = { ducks: %w[huey dewey louie] }
272
+ inventory.children = { birds: %w[ducks] }
273
+ inventory.vars = { birds: { of: 'a feather' }, ducks: { out: 'quick' } }
274
+
275
+ inventory2.hosts = %w[heckle jeckle launchpad]
276
+ inventory2.groups = { crows: %w[heckle jeckle], ducks: %w[launchpad] }
277
+ inventory2.children = { birds: %w[crows] }
278
+ inventory2.vars = { birds: { on: 'the wing' }, crows: { with: 'delight' } }
279
+
280
+ inventory3 = inventory.merge(inventory2)
281
+ expect(inventory3.hosts.map(&:name)).to include('huey', 'dewey', 'louie', 'launchpad', 'heckle', 'jeckle')
282
+ expect(inventory3.vars).to eq('birds' => { 'on' => 'the wing', 'of' => 'a feather' }, 'ducks' => { 'out' => 'quick' }, 'crows' => { 'with' => 'delight' })
283
+ expect(inventory3.groups.keys).to include('ducks', 'crows')
284
+ expect(inventory3.children['birds']).to include('ducks', 'crows')
285
+ end
286
+ end
287
+
288
+ describe '#hostvars' do
289
+ it "returns a hash each host's hostvars mapped to its inventory hostname"
290
+ end
291
+
292
+ describe '#validate!' do
293
+ context 'when nonextant children are defined for a group' do
294
+ it 'raises an error' do
295
+ inventory.groups = ['parent']
296
+ inventory.children_of('parent', 'child')
297
+ expect { inventory.validate! }.to raise_error(
298
+ VagrantPlugins::AnsibleAuto::Errors::GroupMissingChildError,
299
+ /group parent defines nonextant child group child/
300
+ )
301
+ end
302
+ end
303
+ end
304
+
305
+ describe '#to_ini' do
306
+ include_context 'host'
307
+
308
+ let(:host1) do
309
+ VagrantPlugins::AnsibleAuto::Host.new('blurgh').tap do |h|
310
+ h.inventory_hostname = 'tweedledee'
311
+ h.ansible_ssh_user = 'me'
312
+ h.ansible_ssh_host = '10.10.0.20'
313
+ h.ansible_ssh_port = 2200
314
+ h.ansible_ssh_private_key_file = 'me_id_rsa'
315
+ end
316
+ end
317
+
318
+ let(:host2) do
319
+ VagrantPlugins::AnsibleAuto::Host.new('bleh').tap do |h|
320
+ h.inventory_hostname = 'tweedledum'
321
+ h.ansible_ssh_user = 'you'
322
+ h.ansible_ssh_host = '192.168.1.88'
323
+ h.ansible_ssh_port = 2201
324
+ h.ansible_ssh_private_key_file = 'you_id_rsa'
325
+ end
326
+ end
327
+
328
+ it 'returns the inventory as an INI-style document' do
329
+ inventory.add_group(:mygroup, complex_group)
330
+ inventory.add_host(host1)
331
+ inventory.add_host(host2)
332
+ expect(inventory.to_ini).to eq(unindent(<<-INVENTORY).chomp)
333
+ tweedledee ansible_ssh_host=10.10.0.20 ansible_ssh_port=2200 ansible_ssh_private_key_file=me_id_rsa ansible_ssh_user=me
334
+ tweedledum ansible_ssh_host=192.168.1.88 ansible_ssh_port=2201 ansible_ssh_private_key_file=you_id_rsa ansible_ssh_user=you
335
+ [mygroup]
336
+ bastion
337
+ db
338
+ firewall
339
+ [mygroup:children]
340
+ qa
341
+ staging
342
+ [mygroup:vars]
343
+ n = uh
344
+ this = n
345
+ uh = that
346
+ INVENTORY
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'log4r'
6
+
7
+ require 'vagrant/config/v2/dummy_config'
8
+
9
+ require 'vagrant/ansible_auto/provisioner'
10
+
11
+ describe VagrantPlugins::AnsibleAuto::Provisioner do
12
+ include_context 'inventory'
13
+ include_context 'config'
14
+
15
+ def mock_capabilities(machine)
16
+ capabilities = {
17
+ ssh_server_address: ->(other) { [other.ssh_info[:host], other.ssh_info[:port]] },
18
+ authorized_key?: ->(content, *args) { [true] },
19
+ fetch_public_key: ->(path) {},
20
+ insert_public_key: ->(key) { [] },
21
+ ansible_installed: ->(*) { true }
22
+ }
23
+
24
+ allow(machine.guest).to receive(:capability?) { |cap| capabilities.key? cap }
25
+
26
+ allow(machine.guest).to receive(:capability) do |cap, *args, &block|
27
+ capabilities[cap].call(*args, &block) if capabilities.key? cap
28
+ end
29
+ end
30
+
31
+ let(:provisioner) { described_class.new(machine, config) }
32
+ let(:root_config) { Vagrant::Config::V2::DummyConfig.new }
33
+ let(:existing_file) { File.expand_path(__FILE__) }
34
+ let(:logger) { Log4r::Logger.new('vagrant::provisioners::ansible_auto::spec') }
35
+
36
+ before do |example|
37
+ unless example.metadata.fetch(:skip_before) { false }
38
+ # Allows us to set expectations on the provisioner's @logger instance
39
+ # variable
40
+ log4r_logger_new_original = Log4r::Logger.method(:new)
41
+ allow(Log4r::Logger).to receive(:new) do |namespace, *args|
42
+ if namespace == 'vagrant::provisioners::ansible_auto'
43
+ logger
44
+ else
45
+ log4r_logger_new_original.call(namespace, *args)
46
+ end
47
+ end
48
+
49
+ allow(root_config).to receive(:ansible).and_return(VagrantPlugins::AnsibleAuto::Config.new)
50
+
51
+ config.playbook = existing_file
52
+ config.finalize!
53
+ root_config.finalize!
54
+ machines.each do |m|
55
+ m.config.ansible.finalize!
56
+ mock_capabilities(m)
57
+ end
58
+
59
+ provisioner.configure(root_config)
60
+
61
+ allow(machine.communicate).to receive(:execute) do |*args|
62
+ 0
63
+ end
64
+
65
+ allow(machine.communicate).to receive(:upload) do |*args|
66
+ true
67
+ end
68
+
69
+ allow(machine.communicate).to receive(:test) do |*args|
70
+ true
71
+ end
72
+ end
73
+ end
74
+
75
+ after do |example|
76
+ provisioner.provision unless example.metadata.fetch(:skip_after) { false }
77
+ end
78
+
79
+ describe '#provision' do
80
+ it 'creates an ansible inventory', skip_after: true do
81
+ expect(machine.communicate).to receive(:upload) do |from, to|
82
+ contents = File.read(from)
83
+ expect(contents).to match(/ansible_connection=local/)
84
+ end
85
+ expect { provisioner.provision }.not_to raise_error
86
+ expect(provisioner.config.inventory).to be_a VagrantPlugins::AnsibleAuto::Inventory
87
+ end
88
+
89
+ context 'when strict host key checking' do
90
+ let(:strict_host_key_checking_options) do
91
+ "ansible_ssh_extra_args='-o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -o StrictHostKeyChecking=no'"
92
+ end
93
+
94
+ context 'is enabled' do
95
+ before do
96
+ config.strict_host_key_checking = true
97
+ end
98
+
99
+ it 'omits StrictHostKeyChecking=yes and other SSH options from the inventory' do
100
+ expect(machine.communicate).to receive(:upload) do |from, to|
101
+ contents = File.read(from)
102
+ expect(contents).not_to match(/#{strict_host_key_checking_options}/)
103
+ end
104
+ end
105
+ end
106
+
107
+ context 'is disabled' do
108
+ before do
109
+ config.strict_host_key_checking = false
110
+ end
111
+
112
+ it 'includes StrictHostKeyChecking=yes and other SSH options in the inventory' do
113
+ expect(machine.communicate).to receive(:upload) do |from, to|
114
+ contents = File.read(from)
115
+ expect(contents).to match(/#{strict_host_key_checking_options}/)
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ context 'when control machine public key insertion is enabled' do
122
+ let(:public_key_content) { 'iamapublickey' }
123
+ let(:private_key_content) { 'iamaprivatekey' }
124
+ let(:openssh_content) { 'ssh iamsomebinarystuff vagrant' }
125
+
126
+ before do
127
+ config.insert_control_machine_public_key = true
128
+ config.upload_inventory_host_private_keys = false
129
+ end
130
+
131
+ context 'and the control machine does not yet have a keypair configured' do
132
+ before do
133
+ allow(machine.communicate).to receive(:test).with(/^test -r/).and_return(false)
134
+ allow(Vagrant::Util::Keypair).to receive(:create).and_return([nil, private_key_content, openssh_content])
135
+ end
136
+
137
+ it 'creates and uploads a new keypair' do
138
+ expect(provisioner).to receive(:write_and_chown_and_chmod_remote_file).with(private_key_content, anything).and_call_original
139
+ expect(provisioner).to receive(:write_and_chown_and_chmod_remote_file).with(openssh_content, anything).and_call_original
140
+ end
141
+ end
142
+
143
+ context 'and the keypair is configured' do
144
+ before do
145
+ allow(provisioner).to receive(:control_machine_public_key).and_return(public_key_content)
146
+ end
147
+
148
+ context 'and the public key' do
149
+ context 'is already authorized' do
150
+ before do
151
+ machines[1..-1].each do |m|
152
+ allow(m.guest).to receive(:capability).with(:authorized_key?, public_key_content).and_return(true)
153
+ end
154
+ end
155
+
156
+ it 'does not attempt to insert the public key' do
157
+ machines[1..-1].each do |m|
158
+ expect(m.guest).not_to receive(:capability).with(:insert_public_key, public_key_content)
159
+ end
160
+ end
161
+ end
162
+
163
+ context 'is not authorized' do
164
+ before do
165
+ machines[1..-1].each do |m|
166
+ allow(m.guest).to receive(:capability).with(:authorized_key?, public_key_content).and_return(false)
167
+ end
168
+ end
169
+
170
+ context 'and the other host permits public key insertion' do
171
+ it 'attempts to insert the public key' do
172
+ machines[1..-1].each do |m|
173
+ expect(m.guest).to receive(:capability).with(:insert_public_key, public_key_content)
174
+ end
175
+ end
176
+ end
177
+
178
+ context 'and the other host does not permit public key insertion' do
179
+ it 'issues a warning' do
180
+ machines[1..-1].each do |m|
181
+ allow(m.guest).to receive(:capability?).with(:insert_public_key).and_return(false)
182
+ expect(logger).to receive(:warn).with(/\ACannot insert control machine public key on/)
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ context 'when inventory host private key upload is enabled' do
192
+ let(:private_key_content) { 'iamaprivatekey' }
193
+
194
+ before do
195
+ config.insert_control_machine_public_key = false
196
+ config.upload_inventory_host_private_keys = true
197
+ end
198
+
199
+ context 'and the host has a private key configured' do
200
+ it 'uploads the private key to the control machine' do
201
+ machines[1..-1].each do |m|
202
+ expect(provisioner).to receive(:fetch_private_key).with(m).and_return(private_key_content)
203
+ end
204
+ end
205
+ end
206
+
207
+ context 'and the host does not have a private key configured' do
208
+ it 'issues a warning' do
209
+ machines[1..-1].each do |m|
210
+ allow(provisioner).to receive(:fetch_private_key).with(m).and_return(nil)
211
+ expect(logger).to receive(:warn).with(/\APrivate key for .*? not available for upload; provisioner will likely fail/)
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ context 'when both public key insertion and private key upload are disabled' do
218
+ before do
219
+ config.insert_control_machine_public_key = false
220
+ config.upload_inventory_host_private_keys = false
221
+ end
222
+
223
+ it 'issues a warning message' do
224
+ expect(machine.ui).to receive(:warn).with(/unable to insert public key or upload existing private key/).at_least(:once)
225
+ end
226
+ end
227
+
228
+ context 'given a machine that is not ready for SSH' do
229
+ before do
230
+ config.host_connect_tries = 1
231
+ end
232
+
233
+ it 'errors out if a machine is not ready for SSH', skip_after: true do
234
+ allow(machines[1].communicate).to receive(:ready?).and_return(false)
235
+ expect { provisioner.provision }.to raise_error(Vagrant::Errors::SSHNotReady)
236
+ end
237
+ end
238
+
239
+ context 'given a machine that is not configured for the current environment' do
240
+ it 'carps and ignores the machine', skip_after: true do
241
+ expect(logger).to receive(:warn).with(/\AMachine .*? not configured for this environment/).at_least(:once)
242
+ expect(logger).to receive(:warn).with(/\AUnable to find machine/).at_least(:once)
243
+ allow(machine.env).to receive(:machine).and_raise(Vagrant::Errors::MachineNotFound, name: 'dummy')
244
+ provisioner.provision
245
+ end
246
+ end
247
+ end
248
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'vagrant/ansible_auto/util/config'
6
+
7
+ describe VagrantPlugins::AnsibleAuto::Util::Config do
8
+ let(:including_klass) do
9
+ Class.new do
10
+ include VagrantPlugins::AnsibleAuto::Util::Config
11
+ end
12
+ end
13
+
14
+ before do
15
+ stub_const("#{including_klass.name}::UNSET_VALUE", Object.new)
16
+ end
17
+
18
+ let(:including_instance) { including_klass.new }
19
+ let(:unset_value) { including_klass.const_get :UNSET_VALUE }
20
+ let(:unset_values) { [nil, @meh, unset_value] }
21
+
22
+ describe '#unset?' do
23
+ context 'given a nil value, undefined value, or the special UNSET_VALUE object' do
24
+ it 'returns true' do
25
+ expect(unset_values.map { |o| including_instance.unset? o }).to all(be true)
26
+ end
27
+ end
28
+
29
+ context 'given anything else' do
30
+ it 'returns false' do
31
+ expect(['hey', 10, Object.new].map { |o| including_instance.unset? o }).to all(be false)
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#conditional_merge' do
37
+ context 'given unset values in the first argument' do
38
+ let(:righthand) { Object.new }
39
+
40
+ it 'returns the second argument' do
41
+ expect(unset_values.map { |o| including_instance.conditional_merge(o, righthand) }).to all(eq(righthand))
42
+ end
43
+ end
44
+
45
+ context 'given unset values in the second argument' do
46
+ let(:lefthand) { Object.new }
47
+
48
+ it 'returns the second argument' do
49
+ expect(unset_values.map { |o| including_instance.conditional_merge(lefthand, o) }).to all(eq(lefthand))
50
+ end
51
+ end
52
+
53
+ context 'given two hashes' do
54
+ let(:lefthand) { { a: 'hash', b: { real: 'dude' } } }
55
+ let(:righthand) { { b: { real: 'calm', and: %w[do not move] }, c: 'you later' } }
56
+ let(:merged) { { a: 'hash', b: { real: 'calm', and: %w[do not move] }, c: 'you later' } }
57
+
58
+ it 'merges the second argument into the first' do
59
+ expect(including_instance.conditional_merge(lefthand, righthand)).to eq(merged)
60
+ end
61
+ end
62
+ end
63
+ end