beaker-docker 0.6.0 → 0.8.2

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,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