beaker-docker 0.6.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,14 +1,6 @@
1
1
  require 'spec_helper'
2
2
  require 'fakefs/spec_helpers'
3
3
 
4
- # fake the docker-api
5
- module Docker
6
- class Image
7
- end
8
- class Container
9
- end
10
- end
11
-
12
4
  module Beaker
13
5
  platforms = [
14
6
  "ubuntu-14.04-x86_64",
@@ -20,6 +12,8 @@ module Beaker
20
12
  ]
21
13
 
22
14
  describe Docker do
15
+ require 'docker'
16
+
23
17
  let(:hosts) {
24
18
  the_hosts = make_hosts
25
19
  the_hosts[2]['dockeropts'] = {
@@ -60,14 +54,15 @@ module Beaker
60
54
  image
61
55
  end
62
56
 
63
- let(:container) do
64
- container = double('Docker::Container')
65
- allow( container ).to receive(:id).and_return('abcdef')
66
- allow( container ).to receive(:start)
67
- allow( container ).to receive(:info).and_return(
68
- *(0..2).map { |index| { 'Names' => ["/spec-container-#{index}"] } }
69
- )
70
- allow( container ).to receive(:json).and_return({
57
+ let(:container_mode) do
58
+ 'rootless'
59
+ end
60
+
61
+ let(:container_config) do
62
+ conf = {
63
+ 'HostConfig' => {
64
+ 'NetworkMode' => 'slirp4netns'
65
+ },
71
66
  'NetworkSettings' => {
72
67
  'IPAddress' => '192.0.2.1',
73
68
  'Ports' => {
@@ -80,7 +75,24 @@ module Beaker
80
75
  },
81
76
  'Gateway' => '192.0.2.254'
82
77
  }
83
- })
78
+ }
79
+
80
+ unless container_mode == 'rootless'
81
+ conf['HostConfig']['NetworkMode'] = 'bridge'
82
+ end
83
+
84
+ conf
85
+ end
86
+
87
+ let(:container) do
88
+ container = double('Docker::Container')
89
+ allow( container ).to receive(:id).and_return('abcdef')
90
+ allow( container ).to receive(:start)
91
+ allow( container ).to receive(:stats)
92
+ allow( container ).to receive(:info).and_return(
93
+ *(0..2).map { |index| { 'Names' => ["/spec-container-#{index}"] } }
94
+ )
95
+ allow( container ).to receive(:json).and_return(container_config)
84
96
  allow( container ).to receive(:kill)
85
97
  allow( container ).to receive(:delete)
86
98
  allow( container ).to receive(:exec)
@@ -88,603 +100,621 @@ module Beaker
88
100
  end
89
101
 
90
102
  let (:docker) { ::Beaker::Docker.new( hosts, options ) }
103
+
91
104
  let(:docker_options) { nil }
105
+
92
106
  let (:version) { {"ApiVersion"=>"1.18", "Arch"=>"amd64", "GitCommit"=>"4749651", "GoVersion"=>"go1.4.2", "KernelVersion"=>"3.16.0-37-generic", "Os"=>"linux", "Version"=>"1.6.0"} }
93
107
 
94
- before :each do
95
- # Stub out all of the docker-api gem. we should never really call it
96
- # from these tests
97
- allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker')
98
- allow( ::Docker ).to receive(:options).and_return(docker_options)
99
- allow( ::Docker ).to receive(:options=)
100
- allow( ::Docker ).to receive(:logger=)
101
- allow( ::Docker ).to receive(:version).and_return(version)
102
- allow( ::Docker::Image ).to receive(:build).and_return(image)
103
- allow( ::Docker::Image ).to receive(:create).and_return(image)
104
- allow( ::Docker::Container ).to receive(:create).and_return(container)
105
- allow_any_instance_of( ::Docker::Container ).to receive(:start)
106
- end
108
+ context 'with connection failure' do
109
+ describe '#initialize' do
110
+ before :each do
111
+ require 'excon'
112
+ expect( ::Docker ).to receive(:version).and_raise(Excon::Errors::SocketError.new( StandardError.new('oops') )).exactly(4).times
113
+ end
107
114
 
108
- describe '#initialize, failure to validate' do
109
- before :each do
110
- require 'excon'
111
- allow( ::Docker ).to receive(:validate_version!).and_raise(Excon::Errors::SocketError.new( StandardError.new('oops') ))
112
- end
113
- it 'should fail when docker not present' do
114
- expect { docker }.to raise_error(RuntimeError, /Docker instance not connectable./)
115
- expect { docker }.to raise_error(RuntimeError, /Check your DOCKER_HOST variable has been set/)
116
- expect { docker }.to raise_error(RuntimeError, /If you are on OSX or Windows, you might not have Docker Machine setup correctly/)
117
- expect { docker }.to raise_error(RuntimeError, /Error was: oops/)
115
+ it 'should fail when docker not present' do
116
+ expect { docker }.to raise_error(RuntimeError, /Docker instance not connectable/)
117
+ expect { docker }.to raise_error(RuntimeError, /Check your DOCKER_HOST variable has been set/)
118
+ expect { docker }.to raise_error(RuntimeError, /If you are on OSX or Windows, you might not have Docker Machine setup correctly/)
119
+ expect { docker }.to raise_error(RuntimeError, /Error was: oops/)
120
+ end
118
121
  end
119
122
  end
120
123
 
121
- describe '#initialize' do
124
+
125
+ context 'with a working connection' do
122
126
  before :each do
123
- allow( ::Docker ).to receive(:validate_version!)
124
- end
127
+ # Stub out all of the docker-api gem. we should never really call it
128
+ # from these tests
129
+ allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker')
130
+ allow( ::Docker ).to receive(:options).and_return(docker_options)
131
+ allow( ::Docker ).to receive(:options=)
132
+ allow( ::Docker ).to receive(:logger=)
133
+ allow( ::Docker ).to receive(:version).and_return(version)
134
+ allow( ::Docker::Image ).to receive(:build).and_return(image)
135
+ allow( ::Docker::Image ).to receive(:create).and_return(image)
136
+ allow( ::Docker::Container ).to receive(:create).and_return(container)
137
+ allow_any_instance_of( ::Docker::Container ).to receive(:start)
138
+ end
139
+
140
+ describe '#initialize' do
141
+ it 'should require the docker gem' do
142
+ expect_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').once
125
143
 
126
- it 'should require the docker gem' do
127
- expect_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').once
144
+ docker
145
+ end
128
146
 
129
- docker
130
- end
147
+ it 'should fail when the gem is absent' do
148
+ allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').and_raise(LoadError)
149
+ expect { docker }.to raise_error(LoadError)
150
+ end
131
151
 
132
- it 'should fail when the gem is absent' do
133
- allow_any_instance_of( ::Beaker::Docker ).to receive(:require).with('docker').and_raise(LoadError)
134
- expect { docker }.to raise_error(LoadError)
135
- end
152
+ it 'should set Docker options' do
153
+ expect( ::Docker ).to receive(:options=).with({:write_timeout => 300, :read_timeout => 300}).once
136
154
 
137
- it 'should set Docker options' do
138
- expect( ::Docker ).to receive(:options=).with({:write_timeout => 300, :read_timeout => 300}).once
155
+ docker
156
+ end
139
157
 
140
- docker
141
- end
158
+ context 'when Docker options are already set' do
159
+ let(:docker_options) {{:write_timeout => 600, :foo => :bar}}
142
160
 
143
- context 'when Docker options are already set' do
144
- let(:docker_options) {{:write_timeout => 600, :foo => :bar}}
161
+ it 'should not override Docker options' do
162
+ expect( ::Docker ).to receive(:options=).with({:write_timeout => 600, :read_timeout => 300, :foo => :bar}).once
145
163
 
146
- it 'should not override Docker options' do
147
- expect( ::Docker ).to receive(:options=).with({:write_timeout => 600, :read_timeout => 300, :foo => :bar}).once
164
+ docker
165
+ end
166
+ end
148
167
 
168
+ it 'should check the Docker gem can work with the api' do
149
169
  docker
150
170
  end
151
- end
152
171
 
153
- it 'should check the Docker gem can work with the api' do
154
- expect( ::Docker ).to receive(:validate_version!).once
172
+ it 'should hook the Beaker logger into the Docker one' do
173
+ expect( ::Docker ).to receive(:logger=).with(logger)
155
174
 
156
- docker
175
+ docker
176
+ end
157
177
  end
158
178
 
159
- it 'should hook the Beaker logger into the Docker one' do
160
- expect( ::Docker ).to receive(:logger=).with(logger)
161
-
162
- docker
163
- end
164
- end
179
+ describe '#install_ssh_components' do
180
+ let(:test_container) { double('container') }
181
+ let(:host) {hosts[0]}
182
+ before :each do
183
+ allow( docker ).to receive(:dockerfile_for)
184
+ end
165
185
 
166
- describe '#install_ssh_components' do
167
- let(:test_container) { double('container') }
168
- let(:host) {hosts[0]}
169
- before :each do
170
- allow( ::Docker ).to receive(:validate_version!)
171
- allow( docker ).to receive(:dockerfile_for)
172
- end
186
+ platforms.each do |platform|
187
+ it 'should call exec at least twice' do
188
+ host['platform'] = platform
189
+ expect(test_container).to receive(:exec).at_least(:twice)
190
+ docker.install_ssh_components(test_container, host)
191
+ end
192
+ end
173
193
 
174
- platforms.each do |platform|
175
- it 'should call exec at least twice' do
176
- host['platform'] = platform
194
+ it 'should accept alpine as valid platform' do
195
+ host['platform'] = 'alpine-3.8-x86_64'
177
196
  expect(test_container).to receive(:exec).at_least(:twice)
178
197
  docker.install_ssh_components(test_container, host)
179
198
  end
180
- end
181
199
 
182
- it 'should accept alpine as valid platform' do
183
- host['platform'] = 'alpine-3.8-x86_64'
184
- expect(test_container).to receive(:exec).at_least(:twice)
185
- docker.install_ssh_components(test_container, host)
200
+ it 'should raise an error with an unsupported platform' do
201
+ host['platform'] = 'boogeyman-2000-x86_64'
202
+ expect{docker.install_ssh_components(test_container, host)}.to raise_error(RuntimeError, /boogeyman/)
203
+ end
186
204
  end
187
205
 
188
- it 'should raise an error with an unsupported platform' do
189
- host['platform'] = 'boogeyman-2000-x86_64'
190
- expect{docker.install_ssh_components(test_container, host)}.to raise_error(RuntimeError, /boogeyman/)
191
- end
192
- end
206
+ describe '#provision' do
207
+ before :each do
208
+ allow( docker ).to receive(:dockerfile_for)
209
+ end
193
210
 
194
- describe '#provision' do
195
- before :each do
196
- allow( ::Docker ).to receive(:validate_version!)
197
- allow( docker ).to receive(:dockerfile_for)
198
- end
211
+ context 'when the host has "tag" defined' do
212
+ before :each do
213
+ hosts.each do |host|
214
+ host['tag'] = 'my_tag'
215
+ end
216
+ end
199
217
 
200
- context 'when the host has "tag" defined' do
201
- before :each do
202
- hosts.each do |host|
203
- host['tag'] = 'my_tag'
218
+ it 'will tag the image with the value of the tag' do
219
+ expect( image ).to receive(:tag).with({:repo => 'my_tag'}).exactly(3).times
220
+ docker.provision
204
221
  end
205
222
  end
206
223
 
207
- it 'will tag the image with the value of the tag' do
208
- expect( image ).to receive(:tag).with({:repo => 'my_tag'}).exactly(3).times
209
- docker.provision
210
- end
211
- end
224
+ context 'when the host has "use_image_entry_point" set to true on the host' do
212
225
 
213
- context 'when the host has "use_image_entry_point" set to true on the host' do
226
+ before :each do
227
+ hosts.each do |host|
228
+ host['use_image_entry_point'] = true
229
+ end
230
+ end
214
231
 
215
- before :each do
216
- hosts.each do |host|
217
- host['use_image_entry_point'] = true
232
+ it 'should not call #dockerfile_for but run methods necessary for ssh installation' do
233
+ expect( docker ).not_to receive(:dockerfile_for)
234
+ expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host
235
+ expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host
236
+ docker.provision
218
237
  end
219
238
  end
220
239
 
221
- it 'should not call #dockerfile_for but run methods necessary for ssh installation' do
222
- expect( docker ).not_to receive(:dockerfile_for)
223
- expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host
224
- expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host
225
- docker.provision
226
- end
227
- end
240
+ context 'when the host has a "dockerfile" for the host' do
228
241
 
229
- context 'when the host has a "dockerfile" for the host' do
242
+ before :each do
243
+ allow( docker ).to receive(:buildargs_for).and_return('buildargs')
244
+ hosts.each do |host|
245
+ host['dockerfile'] = 'mydockerfile'
246
+ end
247
+ end
230
248
 
231
- before :each do
232
- allow( docker ).to receive(:buildargs_for).and_return('buildargs')
233
- hosts.each do |host|
234
- host['dockerfile'] = 'mydockerfile'
249
+ it 'should not call #dockerfile_for but run methods necessary for ssh installation' do
250
+ allow( File ).to receive(:exist?).with('mydockerfile').and_return(true)
251
+ allow( ::Docker::Image ).to receive(:build_from_dir).with("/", hash_including(:rm => true, :buildargs => 'buildargs')).and_return(image)
252
+ expect( docker ).not_to receive(:dockerfile_for)
253
+ expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host
254
+ expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host
255
+ docker.provision
235
256
  end
236
257
  end
237
258
 
238
- it 'should not call #dockerfile_for but run methods necessary for ssh installation' do
239
- allow( File ).to receive(:exist?).with('mydockerfile').and_return(true)
240
- allow( ::Docker::Image ).to receive(:build_from_dir).with("/", hash_including(:rm => true, :buildargs => 'buildargs')).and_return(image)
241
- expect( docker ).not_to receive(:dockerfile_for)
242
- expect( docker ).to receive(:install_ssh_components).exactly(3).times #once per host
243
- expect( docker ).to receive(:fix_ssh).exactly(3).times #once per host
244
- docker.provision
245
- end
246
- end
259
+ it 'should call image create for hosts when use_image_as_is is defined' do
260
+ hosts.each do |host|
261
+ host['use_image_as_is'] = true
262
+ expect( docker ).not_to receive(:install_ssh_components)
263
+ expect( docker ).not_to receive(:fix_ssh)
264
+ expect( ::Docker::Image ).to receive(:create).with('fromImage' => host['image']) #once per host
265
+ expect( ::Docker::Image ).not_to receive(:build)
266
+ expect( ::Docker::Image ).not_to receive(:build_from_dir)
267
+ end
247
268
 
248
- it 'should call image create for hosts when use_image_as_is is defined' do
249
- hosts.each do |host|
250
- host['use_image_as_is'] = true
251
- expect( docker ).not_to receive(:install_ssh_components)
252
- expect( docker ).not_to receive(:fix_ssh)
253
- expect( ::Docker::Image ).to receive(:create).with('fromImage' => host['image']) #once per host
254
- expect( ::Docker::Image ).not_to receive(:build)
255
- expect( ::Docker::Image ).not_to receive(:build_from_dir)
269
+ docker.provision
256
270
  end
257
271
 
258
- docker.provision
259
- end
272
+ it 'should call dockerfile_for with all the hosts' do
273
+ hosts.each do |host|
274
+ expect( docker ).not_to receive(:install_ssh_components)
275
+ expect( docker ).not_to receive(:fix_ssh)
276
+ expect( docker ).to receive(:dockerfile_for).with(host).and_return('')
277
+ end
260
278
 
261
- it 'should call dockerfile_for with all the hosts' do
262
- hosts.each do |host|
263
- expect( docker ).not_to receive(:install_ssh_components)
264
- expect( docker ).not_to receive(:fix_ssh)
265
- expect( docker ).to receive(:dockerfile_for).with(host).and_return('')
279
+ docker.provision
266
280
  end
267
281
 
268
- docker.provision
269
- end
282
+ it 'should pass the Dockerfile on to Docker::Image.create' do
283
+ allow( docker ).to receive(:dockerfile_for).and_return('special testing value')
284
+ expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => '{}' })
270
285
 
271
- it 'should pass the Dockerfile on to Docker::Image.create' do
272
- allow( docker ).to receive(:dockerfile_for).and_return('special testing value')
273
- expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => '{}' })
274
-
275
- docker.provision
276
- end
286
+ docker.provision
287
+ end
277
288
 
278
- it 'should pass the buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do
279
- allow( docker ).to receive(:dockerfile_for).and_return('special testing value')
280
- ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128'
281
- expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\"}" })
289
+ it 'should pass the buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do
290
+ allow( docker ).to receive(:dockerfile_for).and_return('special testing value')
291
+ ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128'
292
+ expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\"}" })
282
293
 
283
- docker.provision
284
- end
294
+ docker.provision
295
+ end
285
296
 
286
- it 'should create a container based on the Image (identified by image.id)' do
287
- hosts.each do |host|
288
- expect( ::Docker::Container ).to receive(:create).with({
289
- 'Image' => image.id,
290
- 'Hostname' => host.name,
291
- 'HostConfig' => {
292
- 'PortBindings' => {
293
- '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
297
+ it 'should create a container based on the Image (identified by image.id)' do
298
+ hosts.each_with_index do |host,index|
299
+ expect( ::Docker::Container ).to receive(:create).with({
300
+ 'Image' => image.id,
301
+ 'Hostname' => host.name,
302
+ 'HostConfig' => {
303
+ 'PortBindings' => {
304
+ '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
305
+ },
306
+ 'Privileged' => true,
307
+ 'PublishAllPorts' => true,
308
+ 'RestartPolicy' => {
309
+ 'Name' => 'always'
310
+ }
294
311
  },
295
- 'PublishAllPorts' => true,
296
- 'Privileged' => true,
297
- 'RestartPolicy' => {
298
- 'Name' => 'always'
299
- }
300
- },
301
- 'Labels' => {
302
- 'one' => 1,
303
- 'two' => 2,
304
- },
305
- }).with(hash_excluding('name'))
306
- end
312
+ 'Labels' => {
313
+ 'one' => (index == 2 ? 3 : 1),
314
+ 'two' => (index == 2 ? 4 : 2),
315
+ },
316
+ 'name' => /\Abeaker-/
317
+ })
318
+ end
307
319
 
308
- docker.provision
309
- end
320
+ docker.provision
321
+ end
310
322
 
311
- it 'should pass the multiple buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do
312
- allow( docker ).to receive(:dockerfile_for).and_return('special testing value')
313
- ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128 HTTPS_PROXY=https://1.1.1.1:3129'
314
- expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\",\"HTTPS_PROXY\":\"https://1.1.1.1:3129\"}" })
323
+ it 'should pass the multiple buildargs from ENV DOCKER_BUILDARGS on to Docker::Image.create' do
324
+ allow( docker ).to receive(:dockerfile_for).and_return('special testing value')
325
+ ENV['DOCKER_BUILDARGS'] = 'HTTP_PROXY=http://1.1.1.1:3128 HTTPS_PROXY=https://1.1.1.1:3129'
326
+ expect( ::Docker::Image ).to receive(:build).with('special testing value', { :rm => true, :buildargs => "{\"HTTP_PROXY\":\"http://1.1.1.1:3128\",\"HTTPS_PROXY\":\"https://1.1.1.1:3129\"}" })
315
327
 
316
- docker.provision
317
- end
328
+ docker.provision
329
+ end
318
330
 
319
- it 'should create a container based on the Image (identified by image.id)' do
320
- hosts.each do |host|
321
- expect( ::Docker::Container ).to receive(:create).with({
322
- 'Image' => image.id,
323
- 'Hostname' => host.name,
324
- 'HostConfig' => {
325
- 'PortBindings' => {
326
- '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
331
+ it 'should create a container based on the Image (identified by image.id)' do
332
+ hosts.each_with_index do |host,index|
333
+ expect( ::Docker::Container ).to receive(:create).with({
334
+ 'Image' => image.id,
335
+ 'Hostname' => host.name,
336
+ 'HostConfig' => {
337
+ 'PortBindings' => {
338
+ '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
339
+ },
340
+ 'PublishAllPorts' => true,
341
+ 'Privileged' => true,
342
+ 'RestartPolicy' => {
343
+ 'Name' => 'always'
344
+ }
327
345
  },
328
- 'PublishAllPorts' => true,
329
- 'Privileged' => true,
330
- 'RestartPolicy' => {
331
- 'Name' => 'always'
332
- }
333
- },
334
- 'Labels' => {
335
- 'one' => 1,
336
- 'two' => 2,
337
- },
338
- }).with(hash_excluding('name'))
346
+ 'Labels' => {
347
+ 'one' => (index == 2 ? 3 : 1),
348
+ 'two' => (index == 2 ? 4 : 2),
349
+ },
350
+ 'name' => /\Abeaker-/
351
+ })
352
+ end
353
+
354
+ docker.provision
339
355
  end
340
356
 
341
- docker.provision
342
- end
357
+ it 'should create a named container based on the Image (identified by image.id)' do
358
+ hosts.each_with_index do |host, index|
359
+ container_name = "spec-container-#{index}"
360
+ host['docker_container_name'] = container_name
343
361
 
344
- it 'should create a named container based on the Image (identified by image.id)' do
345
- hosts.each_with_index do |host, index|
346
- container_name = "spec-container-#{index}"
347
- host['docker_container_name'] = container_name
348
-
349
- allow(::Docker::Container).to receive(:all).and_return([])
350
- expect( ::Docker::Container ).to receive(:create).with({
351
- 'Image' => image.id,
352
- 'Hostname' => host.name,
353
- 'name' => container_name,
354
- 'HostConfig' => {
355
- 'PortBindings' => {
356
- '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
362
+ allow(::Docker::Container).to receive(:all).and_return([])
363
+ expect( ::Docker::Container ).to receive(:create).with({
364
+ 'Image' => image.id,
365
+ 'Hostname' => host.name,
366
+ 'name' => container_name,
367
+ 'HostConfig' => {
368
+ 'PortBindings' => {
369
+ '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
370
+ },
371
+ 'PublishAllPorts' => true,
372
+ 'Privileged' => true,
373
+ 'RestartPolicy' => {
374
+ 'Name' => 'always'
375
+ }
357
376
  },
358
- 'PublishAllPorts' => true,
359
- 'Privileged' => true,
360
- 'RestartPolicy' => {
361
- 'Name' => 'always'
362
- }
363
- },
364
- 'Labels' => {
365
- 'one' => (index == 2 ? 3 : 1),
366
- 'two' => (index == 2 ? 4 : 2),
367
- },
368
- })
369
- end
377
+ 'Labels' => {
378
+ 'one' => (index == 2 ? 3 : 1),
379
+ 'two' => (index == 2 ? 4 : 2),
380
+ },
381
+ })
382
+ end
370
383
 
371
- docker.provision
372
- end
384
+ docker.provision
385
+ end
373
386
 
374
387
 
375
- it 'should create a container with volumes bound' do
376
- hosts.each_with_index do |host, index|
377
- host['mount_folders'] = {
378
- 'mount1' => {
379
- 'host_path' => '/source_folder',
380
- 'container_path' => '/mount_point',
381
- },
382
- 'mount2' => {
383
- 'host_path' => '/another_folder',
384
- 'container_path' => '/another_mount',
385
- 'opts' => 'ro',
386
- },
387
- 'mount3' => {
388
- 'host_path' => '/different_folder',
389
- 'container_path' => '/different_mount',
390
- 'opts' => 'rw',
391
- },
392
- 'mount4' => {
393
- 'host_path' => './',
394
- 'container_path' => '/relative_mount',
395
- },
396
- 'mount5' => {
397
- 'host_path' => 'local_folder',
398
- 'container_path' => '/another_relative_mount',
399
- }
400
- }
401
-
402
- expect( ::Docker::Container ).to receive(:create).with({
403
- 'Image' => image.id,
404
- 'Hostname' => host.name,
405
- 'HostConfig' => {
406
- 'Binds' => [
407
- '/source_folder:/mount_point',
408
- '/another_folder:/another_mount:ro',
409
- '/different_folder:/different_mount:rw',
410
- "#{File.expand_path('./')}:/relative_mount",
411
- "#{File.expand_path('local_folder')}:/another_relative_mount",
412
- ],
413
- 'PortBindings' => {
414
- '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
388
+ it 'should create a container with volumes bound' do
389
+ hosts.each_with_index do |host, index|
390
+ host['mount_folders'] = {
391
+ 'mount1' => {
392
+ 'host_path' => '/source_folder',
393
+ 'container_path' => '/mount_point',
394
+ },
395
+ 'mount2' => {
396
+ 'host_path' => '/another_folder',
397
+ 'container_path' => '/another_mount',
398
+ 'opts' => 'ro',
415
399
  },
416
- 'PublishAllPorts' => true,
417
- 'Privileged' => true,
418
- 'RestartPolicy' => {
419
- 'Name' => 'always'
400
+ 'mount3' => {
401
+ 'host_path' => '/different_folder',
402
+ 'container_path' => '/different_mount',
403
+ 'opts' => 'rw',
404
+ },
405
+ 'mount4' => {
406
+ 'host_path' => './',
407
+ 'container_path' => '/relative_mount',
408
+ },
409
+ 'mount5' => {
410
+ 'host_path' => 'local_folder',
411
+ 'container_path' => '/another_relative_mount',
420
412
  }
421
- },
422
- 'Labels' => {
423
- 'one' => (index == 2 ? 3 : 1),
424
- 'two' => (index == 2 ? 4 : 2),
425
- },
426
- })
427
- end
413
+ }
428
414
 
429
- docker.provision
430
- end
415
+ expect( ::Docker::Container ).to receive(:create).with({
416
+ 'Image' => image.id,
417
+ 'Hostname' => host.name,
418
+ 'HostConfig' => {
419
+ 'Binds' => [
420
+ '/source_folder:/mount_point:z',
421
+ '/another_folder:/another_mount:ro',
422
+ '/different_folder:/different_mount:rw',
423
+ "#{File.expand_path('./')}:/relative_mount:z",
424
+ "#{File.expand_path('local_folder')}:/another_relative_mount:z",
425
+ ],
426
+ 'PortBindings' => {
427
+ '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
428
+ },
429
+ 'PublishAllPorts' => true,
430
+ 'Privileged' => true,
431
+ 'RestartPolicy' => {
432
+ 'Name' => 'always'
433
+ }
434
+ },
435
+ 'Labels' => {
436
+ 'one' => (index == 2 ? 3 : 1),
437
+ 'two' => (index == 2 ? 4 : 2),
438
+ },
439
+ 'name' => /\Abeaker-/
440
+ })
441
+ end
431
442
 
432
- it 'should create a container with capabilities added' do
433
- hosts.each_with_index do |host, index|
434
- host['docker_cap_add'] = ['NET_ADMIN', 'SYS_ADMIN']
443
+ docker.provision
444
+ end
435
445
 
436
- expect( ::Docker::Container ).to receive(:create).with({
437
- 'Image' => image.id,
438
- 'Hostname' => host.name,
439
- 'HostConfig' => {
440
- 'PortBindings' => {
441
- '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
446
+ it 'should create a container with capabilities added' do
447
+ hosts.each_with_index do |host, index|
448
+ host['docker_cap_add'] = ['NET_ADMIN', 'SYS_ADMIN']
449
+
450
+ expect( ::Docker::Container ).to receive(:create).with({
451
+ 'Image' => image.id,
452
+ 'Hostname' => host.name,
453
+ 'HostConfig' => {
454
+ 'PortBindings' => {
455
+ '22/tcp' => [{ 'HostPort' => /\b\d{4}\b/, 'HostIp' => '0.0.0.0'}]
456
+ },
457
+ 'PublishAllPorts' => true,
458
+ 'RestartPolicy' => {
459
+ 'Name' => 'always'
460
+ },
461
+ 'CapAdd' => ['NET_ADMIN', 'SYS_ADMIN']
442
462
  },
443
- 'PublishAllPorts' => true,
444
- 'Privileged' => true,
445
- 'RestartPolicy' => {
446
- 'Name' => 'always'
463
+ 'Labels' => {
464
+ 'one' => (index == 2 ? 3 : 1),
465
+ 'two' => (index == 2 ? 4 : 2),
447
466
  },
448
- 'CapAdd' => ['NET_ADMIN', 'SYS_ADMIN']
449
- },
450
- 'Labels' => {
451
- 'one' => (index == 2 ? 3 : 1),
452
- 'two' => (index == 2 ? 4 : 2),
453
- },
454
- })
467
+ 'name' => /\Abeaker-/
468
+ })
469
+ end
470
+
471
+ docker.provision
455
472
  end
456
473
 
457
- docker.provision
458
- end
474
+ it 'should start the container' do
475
+ expect( container ).to receive(:start)
459
476
 
460
- it 'should start the container' do
461
- expect( container ).to receive(:start)
477
+ docker.provision
478
+ end
462
479
 
463
- docker.provision
464
- end
480
+ context "connecting to ssh" do
481
+ context "rootless" do
482
+ before { @docker_host = ENV['DOCKER_HOST'] }
483
+ after { ENV['DOCKER_HOST'] = @docker_host }
484
+
485
+ it 'should expose port 22 to beaker' do
486
+ ENV['DOCKER_HOST'] = nil
487
+ docker.provision
488
+
489
+ expect( hosts[0]['ip'] ).to be === '127.0.1.1'
490
+ expect( hosts[0]['port'] ).to be === 8022
491
+ end
492
+
493
+ it 'should expose port 22 to beaker when using DOCKER_HOST' do
494
+ ENV['DOCKER_HOST'] = "tcp://192.0.2.2:2375"
495
+ docker.provision
496
+
497
+ expect( hosts[0]['ip'] ).to be === '192.0.2.2'
498
+ expect( hosts[0]['port'] ).to be === 8022
499
+ end
500
+
501
+ it 'should have ssh agent forwarding enabled' do
502
+ ENV['DOCKER_HOST'] = nil
503
+ docker.provision
504
+
505
+ expect( hosts[0]['ip'] ).to be === '127.0.1.1'
506
+ expect( hosts[0]['port'] ).to be === 8022
507
+ expect( hosts[0]['ssh'][:password] ).to be === 'root'
508
+ expect( hosts[0]['ssh'][:port] ).to be === 8022
509
+ expect( hosts[0]['ssh'][:forward_agent] ).to be === true
510
+ end
511
+
512
+ it 'should connect to gateway ip' do
513
+ FakeFS do
514
+ File.open('/.dockerenv', 'w') { }
515
+ docker.provision
516
+
517
+ expect( hosts[0]['ip'] ).to be === '192.0.2.254'
518
+ expect( hosts[0]['port'] ).to be === 8022
519
+ end
520
+ end
521
+ end
465
522
 
466
- context "connecting to ssh" do
467
- before { @docker_host = ENV['DOCKER_HOST'] }
468
- after { ENV['DOCKER_HOST'] = @docker_host }
523
+ context 'rootful' do
524
+ before { @docker_host = ENV['DOCKER_HOST'] }
525
+ after { ENV['DOCKER_HOST'] = @docker_host }
469
526
 
470
- it 'should expose port 22 to beaker' do
471
- ENV['DOCKER_HOST'] = nil
472
- docker.provision
527
+ let(:container_mode) do
528
+ 'rootful'
529
+ end
473
530
 
474
- expect( hosts[0]['ip'] ).to be === '127.0.1.1'
475
- expect( hosts[0]['port'] ).to be === 8022
476
- end
531
+ it 'should expose port 22 to beaker' do
532
+ ENV['DOCKER_HOST'] = nil
533
+ docker.provision
477
534
 
478
- it 'should expose port 22 to beaker when using DOCKER_HOST' do
479
- ENV['DOCKER_HOST'] = "tcp://192.0.2.2:2375"
480
- docker.provision
535
+ expect( hosts[0]['ip'] ).to be === '192.0.2.1'
536
+ expect( hosts[0]['port'] ).to be === '22'
537
+ end
538
+ end
481
539
 
482
- expect( hosts[0]['ip'] ).to be === '192.0.2.2'
483
- expect( hosts[0]['port'] ).to be === 8022
484
540
  end
485
541
 
486
- it 'should have ssh agent forwarding enabled' do
542
+ it "should generate a new /etc/hosts file referencing each host" do
487
543
  ENV['DOCKER_HOST'] = nil
488
544
  docker.provision
489
-
490
- expect( hosts[0]['ip'] ).to be === '127.0.1.1'
491
- expect( hosts[0]['port'] ).to be === 8022
492
- expect( hosts[0]['ssh'][:password] ).to be === 'root'
493
- expect( hosts[0]['ssh'][:port] ).to be === 8022
494
- expect( hosts[0]['ssh'][:forward_agent] ).to be === true
495
- end
496
-
497
- it 'should connect to gateway ip' do
498
- FakeFS do
499
- File.open('/.dockerenv', 'w') { }
500
- docker.provision
501
-
502
- expect( hosts[0]['ip'] ).to be === '192.0.2.254'
503
- expect( hosts[0]['port'] ).to be === 8022
545
+ hosts.each do |host|
546
+ expect( docker ).to receive( :get_domain_name ).with( host ).and_return( 'labs.lan' )
547
+ expect( docker ).to receive( :set_etc_hosts ).with( host, "127.0.0.1\tlocalhost localhost.localdomain\n192.0.2.1\tvm1.labs.lan vm1\n192.0.2.1\tvm2.labs.lan vm2\n192.0.2.1\tvm3.labs.lan vm3\n" ).once
504
548
  end
549
+ docker.hack_etc_hosts( hosts, options )
505
550
  end
506
551
 
507
- end
552
+ it 'should record the image and container for later' do
553
+ docker.provision
508
554
 
509
- it "should generate a new /etc/hosts file referencing each host" do
510
- ENV['DOCKER_HOST'] = nil
511
- docker.provision
512
- hosts.each do |host|
513
- expect( docker ).to receive( :get_domain_name ).with( host ).and_return( 'labs.lan' )
514
- expect( docker ).to receive( :set_etc_hosts ).with( host, "127.0.0.1\tlocalhost localhost.localdomain\n192.0.2.1\tvm1.labs.lan vm1\n192.0.2.1\tvm2.labs.lan vm2\n192.0.2.1\tvm3.labs.lan vm3\n" ).once
555
+ expect( hosts[0]['docker_image_id'] ).to be === image.id
556
+ expect( hosts[0]['docker_container_id'] ).to be === container.id
515
557
  end
516
- docker.hack_etc_hosts( hosts, options )
517
- end
518
-
519
- it 'should record the image and container for later' do
520
- docker.provision
521
558
 
522
- expect( hosts[0]['docker_image_id'] ).to be === image.id
523
- expect( hosts[0]['docker_container_id'] ).to be === container.id
524
- end
559
+ context 'provision=false' do
560
+ let(:options) {{
561
+ :logger => logger,
562
+ :forward_ssh_agent => true,
563
+ :provision => false
564
+ }}
525
565
 
526
- context 'provision=false' do
527
- let(:options) {{
528
- :logger => logger,
529
- :forward_ssh_agent => true,
530
- :provision => false
531
- }}
532
566
 
567
+ it 'should fix ssh' do
568
+ hosts.each_with_index do |host, index|
569
+ container_name = "spec-container-#{index}"
570
+ host['docker_container_name'] = container_name
533
571
 
534
- it 'should fix ssh' do
535
- hosts.each_with_index do |host, index|
536
- container_name = "spec-container-#{index}"
537
- host['docker_container_name'] = container_name
538
-
539
- expect( ::Docker::Container ).to receive(:all).and_return([container])
540
- expect(docker).to receive(:fix_ssh).exactly(1).times
572
+ expect( ::Docker::Container ).to receive(:all).and_return([container])
573
+ expect(docker).to receive(:fix_ssh).exactly(1).times
574
+ end
575
+ docker.provision
541
576
  end
542
- docker.provision
543
- end
544
577
 
545
- it 'should not create a container if a named one already exists' do
546
- hosts.each_with_index do |host, index|
547
- container_name = "spec-container-#{index}"
548
- host['docker_container_name'] = container_name
578
+ it 'should not create a container if a named one already exists' do
579
+ hosts.each_with_index do |host, index|
580
+ container_name = "spec-container-#{index}"
581
+ host['docker_container_name'] = container_name
549
582
 
550
- expect( ::Docker::Container ).to receive(:all).and_return([container])
551
- expect( ::Docker::Container ).not_to receive(:create)
552
- end
583
+ expect( ::Docker::Container ).to receive(:all).and_return([container])
584
+ expect( ::Docker::Container ).not_to receive(:create)
585
+ end
553
586
 
554
- docker.provision
587
+ docker.provision
588
+ end
555
589
  end
556
590
  end
557
- end
558
591
 
559
- describe '#cleanup' do
560
- before :each do
561
- # get into a state where there's something to clean
562
- allow( ::Docker ).to receive(:validate_version!)
563
- allow( ::Docker::Container ).to receive(:all).and_return([container])
564
- allow( ::Docker::Image ).to receive(:remove).with(image.id)
565
- allow( docker ).to receive(:dockerfile_for)
566
- docker.provision
567
- end
568
-
569
- it 'should stop the containers' do
570
- allow( docker ).to receive( :sleep ).and_return(true)
571
- expect( container ).to receive(:kill)
572
- docker.cleanup
573
- end
592
+ describe '#cleanup' do
593
+ before :each do
594
+ # get into a state where there's something to clean
595
+ allow( ::Docker::Container ).to receive(:all).and_return([container])
596
+ allow( ::Docker::Image ).to receive(:remove).with(image.id)
597
+ allow( docker ).to receive(:dockerfile_for)
598
+ docker.provision
599
+ end
574
600
 
575
- it 'should delete the containers' do
576
- allow( docker ).to receive( :sleep ).and_return(true)
577
- expect( container ).to receive(:delete)
578
- docker.cleanup
579
- end
601
+ it 'should stop the containers' do
602
+ allow( docker ).to receive( :sleep ).and_return(true)
603
+ expect( container ).to receive(:kill)
604
+ docker.cleanup
605
+ end
580
606
 
581
- it 'should delete the images' do
582
- allow( docker ).to receive( :sleep ).and_return(true)
583
- expect( ::Docker::Image ).to receive(:remove).with(image.id)
584
- docker.cleanup
585
- end
607
+ it 'should delete the containers' do
608
+ allow( docker ).to receive( :sleep ).and_return(true)
609
+ expect( container ).to receive(:delete)
610
+ docker.cleanup
611
+ end
586
612
 
587
- it 'should not delete the image if docker_preserve_image is set to true' do
588
- allow( docker ).to receive( :sleep ).and_return(true)
589
- hosts.each do |host|
590
- host['docker_preserve_image']=true
613
+ it 'should delete the images' do
614
+ allow( docker ).to receive( :sleep ).and_return(true)
615
+ expect( ::Docker::Image ).to receive(:remove).with(image.id)
616
+ docker.cleanup
591
617
  end
592
- expect( ::Docker::Image ).to_not receive(:remove)
593
- docker.cleanup
594
- end
595
618
 
596
- it 'should delete the image if docker_preserve_image is set to false' do
597
- allow( docker ).to receive( :sleep ).and_return(true)
598
- hosts.each do |host|
599
- host['docker_preserve_image']=false
619
+ it 'should not delete the image if docker_preserve_image is set to true' do
620
+ allow( docker ).to receive( :sleep ).and_return(true)
621
+ hosts.each do |host|
622
+ host['docker_preserve_image']=true
623
+ end
624
+ expect( ::Docker::Image ).to_not receive(:remove)
625
+ docker.cleanup
600
626
  end
601
- expect( ::Docker::Image ).to receive(:remove).with(image.id)
602
- docker.cleanup
603
- end
604
627
 
605
- end
628
+ it 'should delete the image if docker_preserve_image is set to false' do
629
+ allow( docker ).to receive( :sleep ).and_return(true)
630
+ hosts.each do |host|
631
+ host['docker_preserve_image']=false
632
+ end
633
+ expect( ::Docker::Image ).to receive(:remove).with(image.id)
634
+ docker.cleanup
635
+ end
606
636
 
607
- describe '#dockerfile_for' do
608
- FakeFS.deactivate!
609
- before :each do
610
- allow( ::Docker ).to receive(:validate_version!)
611
- end
612
- it 'should raise on an unsupported platform' do
613
- expect { docker.send(:dockerfile_for, {'platform' => 'a_sidewalk', 'image' => 'foobar' }) }.to raise_error(/platform a_sidewalk not yet supported/)
614
637
  end
615
638
 
616
- it 'should set "ENV container docker"' do
639
+ describe '#dockerfile_for' do
617
640
  FakeFS.deactivate!
618
- platforms.each do |platform|
619
- dockerfile = docker.send(:dockerfile_for, {
620
- 'platform' => platform,
621
- 'image' => 'foobar',
622
- })
623
- expect( dockerfile ).to be =~ /ENV container docker/
641
+ it 'should raise on an unsupported platform' do
642
+ expect { docker.send(:dockerfile_for, {'platform' => 'a_sidewalk', 'image' => 'foobar' }) }.to raise_error(/platform a_sidewalk not yet supported/)
624
643
  end
625
- end
626
644
 
627
- it 'should add docker_image_commands as RUN statements' do
628
- FakeFS.deactivate!
629
- platforms.each do |platform|
630
- dockerfile = docker.send(:dockerfile_for, {
631
- 'platform' => platform,
632
- 'image' => 'foobar',
633
- 'docker_image_commands' => [
634
- 'special one',
635
- 'special two',
636
- 'special three',
637
- ]
638
- })
645
+ it 'should set "ENV container docker"' do
646
+ FakeFS.deactivate!
647
+ platforms.each do |platform|
648
+ dockerfile = docker.send(:dockerfile_for, {
649
+ 'platform' => platform,
650
+ 'image' => 'foobar',
651
+ })
652
+ expect( dockerfile ).to be =~ /ENV container docker/
653
+ end
654
+ end
639
655
 
640
- expect( dockerfile ).to be =~ /RUN special one\nRUN special two\nRUN special three/
656
+ it 'should add docker_image_commands as RUN statements' do
657
+ FakeFS.deactivate!
658
+ platforms.each do |platform|
659
+ dockerfile = docker.send(:dockerfile_for, {
660
+ 'platform' => platform,
661
+ 'image' => 'foobar',
662
+ 'docker_image_commands' => [
663
+ 'special one',
664
+ 'special two',
665
+ 'special three',
666
+ ]
667
+ })
668
+
669
+ expect( dockerfile ).to be =~ /RUN special one\nRUN special two\nRUN special three/
670
+ end
641
671
  end
642
- end
643
672
 
644
- it 'should add docker_image_entrypoint' do
645
- FakeFS.deactivate!
646
- platforms.each do |platform|
673
+ it 'should add docker_image_entrypoint' do
674
+ FakeFS.deactivate!
675
+ platforms.each do |platform|
676
+ dockerfile = docker.send(:dockerfile_for, {
677
+ 'platform' => platform,
678
+ 'image' => 'foobar',
679
+ 'docker_image_entrypoint' => '/bin/bash'
680
+ })
681
+
682
+ expect( dockerfile ).to be =~ %r{ENTRYPOINT /bin/bash}
683
+ end
684
+ end
685
+
686
+ it 'should use zypper on sles' do
687
+ FakeFS.deactivate!
647
688
  dockerfile = docker.send(:dockerfile_for, {
648
- 'platform' => platform,
689
+ 'platform' => 'sles-12-x86_64',
649
690
  'image' => 'foobar',
650
- 'docker_image_entrypoint' => '/bin/bash'
651
691
  })
652
692
 
653
- expect( dockerfile ).to be =~ %r{ENTRYPOINT /bin/bash}
693
+ expect( dockerfile ).to be =~ /RUN zypper -n in openssh/
654
694
  end
655
- end
656
695
 
657
- it 'should use zypper on sles' do
658
- FakeFS.deactivate!
659
- dockerfile = docker.send(:dockerfile_for, {
660
- 'platform' => 'sles-12-x86_64',
661
- 'image' => 'foobar',
662
- })
696
+ (22..29).to_a.each do | fedora_release |
697
+ it "should use dnf on fedora #{fedora_release}" do
698
+ FakeFS.deactivate!
699
+ dockerfile = docker.send(:dockerfile_for, {
700
+ 'platform' => "fedora-#{fedora_release}-x86_64",
701
+ 'image' => 'foobar',
702
+ })
663
703
 
664
- expect( dockerfile ).to be =~ /RUN zypper -n in openssh/
665
- end
704
+ expect( dockerfile ).to be =~ /RUN dnf install -y sudo/
705
+ end
706
+ end
666
707
 
667
- (22..29).to_a.each do | fedora_release |
668
- it "should use dnf on fedora #{fedora_release}" do
708
+ it 'should use pacman on archlinux' do
669
709
  FakeFS.deactivate!
670
710
  dockerfile = docker.send(:dockerfile_for, {
671
- 'platform' => "fedora-#{fedora_release}-x86_64",
711
+ 'platform' => 'archlinux-current-x86_64',
672
712
  'image' => 'foobar',
673
713
  })
674
714
 
675
- expect( dockerfile ).to be =~ /RUN dnf install -y sudo/
715
+ expect( dockerfile ).to be =~ /RUN pacman -S --noconfirm openssh/
676
716
  end
677
717
  end
678
-
679
- it 'should use pacman on archlinux' do
680
- FakeFS.deactivate!
681
- dockerfile = docker.send(:dockerfile_for, {
682
- 'platform' => 'archlinux-current-x86_64',
683
- 'image' => 'foobar',
684
- })
685
-
686
- expect( dockerfile ).to be =~ /RUN pacman -S --noconfirm openssh/
687
- end
688
718
  end
689
719
  end
690
720
  end