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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +172 -0
- data/README.md +53 -12
- data/Rakefile +9 -7
- data/TODO.md +14 -0
- data/Vagrantfile +37 -15
- data/lib/vagrant/ansible_auto/cap/guest/posix/check_open_port.rb +22 -3
- data/lib/vagrant/ansible_auto/cap/guest/posix/executable_installed.rb +10 -2
- data/lib/vagrant/ansible_auto/cap/guest/posix/gateway_addresses.rb +8 -23
- data/lib/vagrant/ansible_auto/cap/guest/posix/private_key.rb +16 -1
- data/lib/vagrant/ansible_auto/cap/guest/posix/public_key.rb +18 -3
- data/lib/vagrant/ansible_auto/cap/guest/posix/ssh_server_address.rb +22 -12
- data/lib/vagrant/ansible_auto/cap/guest/posix.rb +16 -0
- data/lib/vagrant/ansible_auto/command/inventory.rb +37 -11
- data/lib/vagrant/ansible_auto/command/root.rb +34 -31
- data/lib/vagrant/ansible_auto/config.rb +74 -33
- data/lib/vagrant/ansible_auto/errors.rb +30 -1
- data/lib/vagrant/ansible_auto/host.rb +123 -34
- data/lib/vagrant/ansible_auto/inventory.rb +196 -34
- data/lib/vagrant/ansible_auto/plugin.rb +23 -8
- data/lib/vagrant/ansible_auto/provisioner.rb +121 -79
- data/lib/vagrant/ansible_auto/util/config.rb +61 -0
- data/lib/vagrant/ansible_auto/util/hash_with_indifferent_access.rb +58 -0
- data/lib/vagrant/ansible_auto/util/keys.rb +49 -0
- data/lib/vagrant/ansible_auto/util/shell_quote.rb +24 -0
- data/lib/vagrant/ansible_auto/version.rb +2 -1
- data/lib/vagrant/ansible_auto.rb +15 -0
- data/locales/en.yml +34 -0
- data/spec/spec_helper.rb +5 -85
- data/spec/support/context.rb +111 -0
- data/spec/support/matchers.rb +45 -0
- data/spec/unit/vagrant/ansible_auto/config_spec.rb +72 -0
- data/spec/unit/vagrant/ansible_auto/host_spec.rb +131 -0
- data/spec/unit/vagrant/ansible_auto/inventory_spec.rb +349 -0
- data/spec/unit/vagrant/ansible_auto/provisioner_spec.rb +248 -0
- data/spec/unit/vagrant/ansible_auto/util/config_spec.rb +63 -0
- data/spec/unit/vagrant/ansible_auto/util/keys_spec.rb +66 -0
- data/vagrant-ansible_auto.gemspec +6 -4
- data/vagrant-spec.config.rb +3 -0
- data/yard/extensions.rb +45 -0
- metadata +36 -11
- data/Vagrantfile2 +0 -4
- data/Vagrantfile3 +0 -8
- data/Vagrantfile4 +0 -31
- data/lib/vagrant/ansible_auto/cap/guest/posix/bash_installed.rb +0 -30
- data/lib/vagrant/ansible_auto/util.rb +0 -24
- data/spec/vagrant/ansible_auto/host_spec.rb +0 -43
- 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
|