vagrant-ansible_auto 0.1.5 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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