beaker-docker 0.7.1 → 0.8.0

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