beaker-docker 1.5.0 → 2.0.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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +0 -3
- data/.rubocop.yml +2 -40
- data/.rubocop_todo.yml +9 -536
- data/CHANGELOG.md +21 -0
- data/Gemfile +0 -4
- data/beaker-docker.gemspec +3 -4
- data/lib/beaker/hypervisor/docker.rb +187 -207
- data/lib/beaker-docker/version.rb +1 -1
- data/spec/beaker/hypervisor/docker_spec.rb +371 -389
- metadata +9 -9
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Beaker
|
4
|
+
# Docker hypervisor for Beaker acceptance testing framework
|
2
5
|
class Docker < Beaker::Hypervisor
|
3
|
-
|
4
6
|
# Docker hypvervisor initializtion
|
5
7
|
# Env variables supported:
|
6
8
|
# DOCKER_REGISTRY: Docker registry URL
|
@@ -10,13 +12,14 @@ module Beaker
|
|
10
12
|
# or a role (String or Symbol) that identifies one or more hosts.
|
11
13
|
# @param [Hash{Symbol=>String}] options Options to pass on to the hypervisor
|
12
14
|
def initialize(hosts, options)
|
15
|
+
super
|
13
16
|
require 'docker'
|
14
17
|
@options = options
|
15
18
|
@logger = options[:logger] || Beaker::Logger.new
|
16
19
|
@hosts = hosts
|
17
20
|
|
18
21
|
# increase the http timeouts as provisioning images can be slow
|
19
|
-
default_docker_options = { :
|
22
|
+
default_docker_options = { write_timeout: 300, read_timeout: 300 }.merge(::Docker.options || {})
|
20
23
|
# Merge docker options from the entry in hosts file
|
21
24
|
::Docker.options = default_docker_options.merge(@options[:docker_options] || {})
|
22
25
|
|
@@ -37,13 +40,12 @@ module Beaker
|
|
37
40
|
::Docker.logger = @logger
|
38
41
|
|
39
42
|
# Find out what kind of remote instance we are talking against
|
40
|
-
if
|
43
|
+
if @docker_version['Version'].include?('swarm')
|
41
44
|
@docker_type = 'swarm'
|
42
|
-
unless ENV['DOCKER_REGISTRY']
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
end
|
45
|
+
raise "Using Swarm with beaker requires a private registry. Please setup the private registry and set the 'DOCKER_REGISTRY' env var" unless ENV['DOCKER_REGISTRY']
|
46
|
+
|
47
|
+
@registry = ENV.fetch('DOCKER_REGISTRY', nil)
|
48
|
+
|
47
49
|
elsif ::Docker.respond_to?(:podman?) && ::Docker.podman?
|
48
50
|
@docker_type = 'podman'
|
49
51
|
else
|
@@ -52,15 +54,15 @@ module Beaker
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def install_and_run_ssh(host)
|
55
|
-
def host.enable_root_login(host,
|
57
|
+
def host.enable_root_login(host, _opts)
|
56
58
|
logger.debug("Root login already enabled for #{host}")
|
57
59
|
end
|
58
60
|
|
59
61
|
# If the container is running ssh as its init process then this method
|
60
62
|
# will cause issues.
|
61
|
-
if
|
63
|
+
if Array(host[:docker_cmd]).first&.include?('sshd')
|
62
64
|
def host.ssh_service_restart
|
63
|
-
self[:docker_container].exec(%w
|
65
|
+
self[:docker_container].exec(%w[kill -1 1])
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
@@ -69,49 +71,45 @@ module Beaker
|
|
69
71
|
|
70
72
|
def get_container_opts(host, image_name)
|
71
73
|
container_opts = {}
|
72
|
-
if host['dockerfile']
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
}
|
88
|
-
} )
|
74
|
+
container_opts['ExposedPorts'] = { '22/tcp' => {} } if host['dockerfile']
|
75
|
+
|
76
|
+
container_opts.merge!({
|
77
|
+
'Image' => image_name,
|
78
|
+
'Hostname' => host.name,
|
79
|
+
'HostConfig' => {
|
80
|
+
'PortBindings' => {
|
81
|
+
'22/tcp' => [{ 'HostPort' => rand(1025..9999).to_s, 'HostIp' => '0.0.0.0' }],
|
82
|
+
},
|
83
|
+
'PublishAllPorts' => true,
|
84
|
+
'RestartPolicy' => {
|
85
|
+
'Name' => 'always',
|
86
|
+
},
|
87
|
+
},
|
88
|
+
})
|
89
89
|
end
|
90
90
|
|
91
91
|
def get_container_image(host)
|
92
|
-
@logger.debug(
|
92
|
+
@logger.debug('Creating image')
|
93
93
|
|
94
|
-
if host['use_image_as_is']
|
95
|
-
return ::Docker::Image.create('fromImage' => host['image'])
|
96
|
-
end
|
94
|
+
return ::Docker::Image.create('fromImage' => host['image']) if host['use_image_as_is']
|
97
95
|
|
98
96
|
dockerfile = host['dockerfile']
|
99
97
|
if dockerfile
|
100
98
|
# assume that the dockerfile is in the repo and tests are running
|
101
99
|
# from the root of the repo; maybe add support for external Dockerfiles
|
102
100
|
# with external build dependencies later.
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
101
|
+
raise "Unable to find dockerfile at #{dockerfile}" unless File.exist?(dockerfile)
|
102
|
+
|
103
|
+
dir = File.expand_path(dockerfile).chomp(dockerfile)
|
104
|
+
return ::Docker::Image.build_from_dir(
|
105
|
+
dir,
|
106
|
+
{
|
107
|
+
'dockerfile' => dockerfile,
|
108
|
+
:rm => true,
|
109
|
+
:buildargs => buildargs_for(host),
|
110
|
+
},
|
111
|
+
)
|
112
|
+
|
115
113
|
elsif host['use_image_entry_point']
|
116
114
|
df = <<-DF
|
117
115
|
FROM #{host['image']}
|
@@ -123,13 +121,12 @@ module Beaker
|
|
123
121
|
return ::Docker::Image.build(df, { rm: true, buildargs: buildargs_for(host) })
|
124
122
|
end
|
125
123
|
|
126
|
-
|
127
|
-
{ rm: true, buildargs: buildargs_for(host) })
|
124
|
+
::Docker::Image.build(dockerfile_for(host), { rm: true, buildargs: buildargs_for(host) })
|
128
125
|
end
|
129
126
|
|
130
127
|
# Nested Docker scenarios
|
131
128
|
def nested_docker?
|
132
|
-
ENV['DOCKER_IN_DOCKER'] || ENV
|
129
|
+
ENV['DOCKER_IN_DOCKER'] || ENV.fetch('WSLENV', nil)
|
133
130
|
end
|
134
131
|
|
135
132
|
# Find out where the ssh port is from the container
|
@@ -139,7 +136,7 @@ module Beaker
|
|
139
136
|
def get_ssh_connection_info(container)
|
140
137
|
ssh_connection_info = {
|
141
138
|
ip: nil,
|
142
|
-
port: nil
|
139
|
+
port: nil,
|
143
140
|
}
|
144
141
|
|
145
142
|
container_json = container.json
|
@@ -149,82 +146,76 @@ module Beaker
|
|
149
146
|
ip = nil
|
150
147
|
port = nil
|
151
148
|
# Talking against a remote docker host which is a normal docker host
|
152
|
-
if @docker_type == 'docker' && ENV
|
153
|
-
ip = URI.parse(ENV
|
154
|
-
|
149
|
+
if @docker_type == 'docker' && ENV.fetch('DOCKER_HOST', nil) && !ENV.fetch('DOCKER_HOST', '').include?(':///') && !nested_docker?
|
150
|
+
ip = URI.parse(ENV.fetch('DOCKER_HOST', nil)).host
|
151
|
+
elsif in_container? && !nested_docker?
|
155
152
|
# Swarm or local docker host
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
# The many faces of container networking
|
161
|
-
|
162
|
-
# Host to Container
|
163
|
-
port22 = network_settings.dig('PortBindings','22/tcp')
|
164
|
-
if port22.nil? && network_settings.key?('Ports') && !nested_docker?
|
165
|
-
port22 = network_settings.dig('Ports','22/tcp')
|
166
|
-
end
|
153
|
+
gw = network_settings['Gateway']
|
154
|
+
ip = gw unless gw.nil? || gw.empty?
|
155
|
+
else
|
156
|
+
# The many faces of container networking
|
167
157
|
|
168
|
-
|
169
|
-
|
158
|
+
# Host to Container
|
159
|
+
port22 = network_settings.dig('PortBindings', '22/tcp')
|
160
|
+
port22 = network_settings.dig('Ports', '22/tcp') if port22.nil? && network_settings.key?('Ports') && !nested_docker?
|
170
161
|
|
171
|
-
|
172
|
-
|
173
|
-
ip = network_settings['IPAddress']
|
174
|
-
port = ip && !ip.empty? ? 22 : nil
|
175
|
-
end
|
162
|
+
ip = port22[0]['HostIp'] if port22
|
163
|
+
port = port22[0]['HostPort'] if port22
|
176
164
|
|
177
|
-
|
178
|
-
|
179
|
-
|
165
|
+
# Container to container
|
166
|
+
unless ip && port
|
167
|
+
ip = network_settings['IPAddress']
|
168
|
+
port = ip && !ip.empty? ? 22 : nil
|
169
|
+
end
|
180
170
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
else
|
185
|
-
port = nil
|
186
|
-
end
|
187
|
-
end
|
171
|
+
# Container through gateway
|
172
|
+
unless ip && port
|
173
|
+
ip = network_settings['Gateway']
|
188
174
|
|
189
|
-
|
190
|
-
|
191
|
-
port22 = network_settings.dig('Ports','22/tcp')
|
192
|
-
ip = port22[0]["HostIp"] if port22
|
175
|
+
if ip && !ip.empty?
|
176
|
+
port22 = network_settings.dig('PortBindings', '22/tcp')
|
193
177
|
port = port22[0]['HostPort'] if port22
|
178
|
+
else
|
179
|
+
port = nil
|
194
180
|
end
|
195
181
|
end
|
182
|
+
|
183
|
+
# Legacy fallback
|
184
|
+
unless ip && port
|
185
|
+
port22 = network_settings.dig('Ports', '22/tcp')
|
186
|
+
ip = port22[0]['HostIp'] if port22
|
187
|
+
port = port22[0]['HostPort'] if port22
|
188
|
+
end
|
196
189
|
end
|
197
190
|
|
198
191
|
if host_config['NetworkMode'] != 'slirp4netns' && network_settings['IPAddress'] && !network_settings['IPAddress'].empty?
|
199
192
|
ip = network_settings['IPAddress'] if ip.nil?
|
200
193
|
else
|
201
|
-
port22 = network_settings.dig('Ports','22/tcp')
|
194
|
+
port22 = network_settings.dig('Ports', '22/tcp')
|
202
195
|
port = port22[0]['HostPort'] if port22
|
203
196
|
end
|
204
197
|
|
205
|
-
ssh_connection_info[:ip] =
|
198
|
+
ssh_connection_info[:ip] = ip == '0.0.0.0' ? '127.0.0.1' : ip
|
206
199
|
ssh_connection_info[:port] = port || '22'
|
207
200
|
ssh_connection_info
|
208
201
|
end
|
209
202
|
|
210
203
|
def provision
|
211
|
-
@logger.notify
|
204
|
+
@logger.notify 'Provisioning docker'
|
212
205
|
|
213
206
|
@hosts.each do |host|
|
214
207
|
@logger.notify "provisioning #{host.name}"
|
215
208
|
|
216
209
|
image = get_container_image(host)
|
217
210
|
|
218
|
-
if host['tag']
|
219
|
-
image.tag({:repo => host['tag']})
|
220
|
-
end
|
211
|
+
image.tag({ repo: host['tag'] }) if host['tag']
|
221
212
|
|
222
213
|
if @docker_type == 'swarm'
|
223
214
|
image_name = "#{@registry}/beaker/#{image.id}"
|
224
|
-
ret = ::Docker::Image.search(:
|
215
|
+
ret = ::Docker::Image.search(term: image_name)
|
225
216
|
if ret.first.nil?
|
226
|
-
@logger.debug(
|
227
|
-
image.tag({:
|
217
|
+
@logger.debug('Image does not exist on registry. Pushing.')
|
218
|
+
image.tag({ repo: image_name, force: true })
|
228
219
|
image.push
|
229
220
|
end
|
230
221
|
else
|
@@ -235,8 +226,8 @@ module Beaker
|
|
235
226
|
|
236
227
|
container_opts = get_container_opts(host, image_name)
|
237
228
|
if host['dockeropts'] || @options[:dockeropts]
|
238
|
-
dockeropts = host['dockeropts']
|
239
|
-
dockeropts.each do |k,v|
|
229
|
+
dockeropts = host['dockeropts'] || @options[:dockeropts]
|
230
|
+
dockeropts.each do |k, v|
|
240
231
|
container_opts[k] = v
|
241
232
|
end
|
242
233
|
end
|
@@ -251,12 +242,12 @@ module Beaker
|
|
251
242
|
container_opts['HostConfig']['Binds'] = host['mount_folders'].values.map do |mount|
|
252
243
|
host_path = File.expand_path(mount['host_path'])
|
253
244
|
# When using docker_toolbox and getting a "(Driveletter):/" path, convert windows path to VM mount
|
254
|
-
if ENV['DOCKER_TOOLBOX_INSTALL_PATH'] && host_path =~
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
if mount.
|
259
|
-
a << mount['opts'] if mount.
|
245
|
+
host_path = "/#{host_path.gsub(/^.:/, host_path[/^(.)/].downcase)}" if ENV['DOCKER_TOOLBOX_INSTALL_PATH'] && host_path =~ %r{^.:/}
|
246
|
+
a = [host_path, mount['container_path']]
|
247
|
+
|
248
|
+
# TODO: rewrite this part
|
249
|
+
if mount.key?('opts')
|
250
|
+
a << mount['opts'] if mount.key?('opts')
|
260
251
|
else
|
261
252
|
a << mount['opts'] = 'z'
|
262
253
|
end
|
@@ -265,9 +256,7 @@ module Beaker
|
|
265
256
|
end
|
266
257
|
end
|
267
258
|
|
268
|
-
if host['docker_env']
|
269
|
-
container_opts['Env'] = host['docker_env']
|
270
|
-
end
|
259
|
+
container_opts['Env'] = host['docker_env'] if host['docker_env']
|
271
260
|
|
272
261
|
# Fixup privileges
|
273
262
|
#
|
@@ -282,11 +271,7 @@ module Beaker
|
|
282
271
|
container_opts['HostConfig']['Privileged'] = container_opts['HostConfig']['Privileged'].nil? ? true : container_opts['HostConfig']['Privileged']
|
283
272
|
end
|
284
273
|
|
285
|
-
|
286
|
-
container_opts['name'] = host['docker_container_name']
|
287
|
-
else
|
288
|
-
container_opts['name'] = ['beaker', host.name, SecureRandom.uuid.split('-').last].join('-')
|
289
|
-
end
|
274
|
+
container_opts['name'] = (host['docker_container_name'] || ['beaker', host.name, SecureRandom.uuid.split('-').last].join('-'))
|
290
275
|
|
291
276
|
if host['docker_port_bindings']
|
292
277
|
container_opts['ExposedPorts'] = {} if container_opts['ExposedPorts'].nil?
|
@@ -300,9 +285,9 @@ module Beaker
|
|
300
285
|
|
301
286
|
@logger.debug("Creating container from image #{image_name}")
|
302
287
|
|
303
|
-
ok=false
|
304
|
-
retries=0
|
305
|
-
while
|
288
|
+
ok = false
|
289
|
+
retries = 0
|
290
|
+
while !ok && (retries < 5)
|
306
291
|
container = ::Docker::Container.create(container_opts)
|
307
292
|
|
308
293
|
ssh_info = get_ssh_connection_info(container)
|
@@ -312,19 +297,19 @@ module Beaker
|
|
312
297
|
container.delete(force: true)
|
313
298
|
container = nil
|
314
299
|
|
315
|
-
retries+=1
|
300
|
+
retries += 1
|
316
301
|
next
|
317
302
|
end
|
318
303
|
|
319
|
-
ok=true
|
304
|
+
ok = true
|
320
305
|
end
|
321
306
|
else
|
322
307
|
host['use_existing_container'] = true
|
323
308
|
end
|
324
309
|
|
325
310
|
if container.nil?
|
326
|
-
raise
|
327
|
-
|
311
|
+
raise 'Cannot continue because no existing container ' \
|
312
|
+
'could be found and provisioning is disabled.'
|
328
313
|
end
|
329
314
|
|
330
315
|
fix_ssh(container) if @options[:provision] == false
|
@@ -364,19 +349,19 @@ module Beaker
|
|
364
349
|
host['ip'] = ip
|
365
350
|
host['port'] = port
|
366
351
|
host['ssh'] = {
|
367
|
-
:
|
368
|
-
:
|
369
|
-
:
|
370
|
-
:
|
352
|
+
password: root_password,
|
353
|
+
port: port,
|
354
|
+
forward_agent: forward_ssh_agent,
|
355
|
+
auth_methods: %w[password publickey hostbased keyboard-interactive],
|
371
356
|
}
|
372
357
|
|
373
358
|
@logger.debug("node available as ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
|
374
359
|
host['docker_container_id'] = container.id
|
375
360
|
host['docker_image_id'] = image.id
|
376
|
-
host['vm_ip'] = container.json[
|
361
|
+
host['vm_ip'] = container.json['NetworkSettings']['IPAddress'].to_s
|
377
362
|
|
378
363
|
def host.reboot
|
379
|
-
@logger.warn(
|
364
|
+
@logger.warn('Rebooting containers is ineffective...ignoring')
|
380
365
|
end
|
381
366
|
end
|
382
367
|
|
@@ -386,92 +371,88 @@ module Beaker
|
|
386
371
|
# This sideloads sshd after a container starts
|
387
372
|
def install_ssh_components(container, host)
|
388
373
|
case host['platform']
|
389
|
-
when /ubuntu/, /debian/
|
390
|
-
container.exec(%w
|
391
|
-
container.exec(%w
|
392
|
-
container.exec(%w
|
393
|
-
when /cumulus/
|
394
|
-
container.exec(%w(apt-get update))
|
395
|
-
container.exec(%w(apt-get install -y openssh-server openssh-client))
|
396
|
-
container.exec(%w(sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*))
|
374
|
+
when /ubuntu/, /debian/, /cumulus/
|
375
|
+
container.exec(%w[apt-get update])
|
376
|
+
container.exec(%w[apt-get install -y openssh-server openssh-client])
|
377
|
+
container.exec(%w[sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*])
|
397
378
|
when /el-[89]/, /fedora-(2[2-9]|3[0-9])/
|
398
|
-
container.exec(%w
|
399
|
-
container.exec(%w
|
400
|
-
container.exec(%w
|
401
|
-
container.exec(%w
|
379
|
+
container.exec(%w[dnf clean all])
|
380
|
+
container.exec(%w[dnf install -y sudo openssh-server openssh-clients])
|
381
|
+
container.exec(%w[ssh-keygen -A])
|
382
|
+
container.exec(%w[sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*])
|
402
383
|
when /^el-/, /centos/, /fedora/, /redhat/, /eos/
|
403
|
-
container.exec(%w
|
404
|
-
container.exec(%w
|
405
|
-
container.exec(%w
|
406
|
-
container.exec(%w
|
407
|
-
container.exec(%w
|
384
|
+
container.exec(%w[yum clean all])
|
385
|
+
container.exec(%w[yum install -y sudo openssh-server openssh-clients])
|
386
|
+
container.exec(%w[ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key])
|
387
|
+
container.exec(%w[ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key])
|
388
|
+
container.exec(%w[sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*])
|
408
389
|
when /opensuse/, /sles/
|
409
|
-
container.exec(%w
|
410
|
-
container.exec(%w
|
411
|
-
container.exec(%w
|
412
|
-
container.exec(%w
|
390
|
+
container.exec(%w[zypper -n in openssh])
|
391
|
+
container.exec(%w[ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key])
|
392
|
+
container.exec(%w[ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key])
|
393
|
+
container.exec(%w[sed -ri 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config])
|
413
394
|
when /archlinux/
|
414
|
-
container.exec(%w
|
415
|
-
container.exec(%w
|
416
|
-
container.exec(%w
|
417
|
-
container.exec(%w
|
418
|
-
container.exec(%w
|
419
|
-
container.exec(%w
|
395
|
+
container.exec(%w[pacman --noconfirm -Sy archlinux-keyring])
|
396
|
+
container.exec(%w[pacman --noconfirm -Syu])
|
397
|
+
container.exec(%w[pacman -S --noconfirm openssh])
|
398
|
+
container.exec(%w[ssh-keygen -A])
|
399
|
+
container.exec(%w[sed -ri 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config])
|
400
|
+
container.exec(%w[systemctl enable sshd])
|
420
401
|
when /alpine/
|
421
|
-
container.exec(%w
|
422
|
-
container.exec(%w
|
402
|
+
container.exec(%w[apk add --update openssh])
|
403
|
+
container.exec(%w[ssh-keygen -A])
|
423
404
|
else
|
424
|
-
# TODO add more platform steps here
|
405
|
+
# TODO: add more platform steps here
|
425
406
|
raise "platform #{host['platform']} not yet supported on docker"
|
426
407
|
end
|
427
408
|
|
428
409
|
# Make sshd directory, set root password
|
429
|
-
container.exec(%w
|
410
|
+
container.exec(%w[mkdir -p /var/run/sshd])
|
430
411
|
container.exec(['/bin/sh', '-c', "echo root:#{root_password} | chpasswd"])
|
431
412
|
end
|
432
413
|
|
433
414
|
def cleanup
|
434
|
-
@logger.notify
|
415
|
+
@logger.notify 'Cleaning up docker'
|
435
416
|
@hosts.each do |host|
|
436
417
|
# leave the container running if docker_preserve_container is set
|
437
418
|
# setting docker_preserve_container also implies docker_preserve_image
|
438
419
|
# is set, since you can't delete an image that's the base of a running
|
439
420
|
# container
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
421
|
+
next if host['docker_preserve_container']
|
422
|
+
|
423
|
+
container = find_container(host)
|
424
|
+
if container
|
425
|
+
@logger.debug("stop container #{container.id}")
|
426
|
+
begin
|
427
|
+
container.kill
|
428
|
+
sleep 2 # avoid a race condition where the root FS can't unmount
|
429
|
+
rescue Excon::Errors::ClientError => e
|
430
|
+
@logger.warn("stop of container #{container.id} failed: #{e.response.body}")
|
431
|
+
end
|
432
|
+
@logger.debug("delete container #{container.id}")
|
433
|
+
begin
|
434
|
+
container.delete(force: true)
|
435
|
+
rescue Excon::Errors::ClientError => e
|
436
|
+
@logger.warn("deletion of container #{container.id} failed: #{e.response.body}")
|
456
437
|
end
|
438
|
+
end
|
457
439
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
else
|
472
|
-
@logger.warn("Intended to delete the host's docker image, but host['docker_image_id'] was not set")
|
473
|
-
end
|
440
|
+
# Do not remove the image if docker_preserve_image is set to true, otherwise remove it
|
441
|
+
next if host['docker_preserve_image']
|
442
|
+
|
443
|
+
image_id = host['docker_image_id']
|
444
|
+
|
445
|
+
if image_id
|
446
|
+
@logger.debug("deleting image #{image_id}")
|
447
|
+
begin
|
448
|
+
::Docker::Image.remove(image_id)
|
449
|
+
rescue Excon::Errors::ClientError => e
|
450
|
+
@logger.warn("deletion of image #{image_id} failed: #{e.response.body}")
|
451
|
+
rescue ::Docker::Error::DockerError => e
|
452
|
+
@logger.warn("deletion of image #{image_id} caused internal Docker error: #{e.message}")
|
474
453
|
end
|
454
|
+
else
|
455
|
+
@logger.warn("Intended to delete the host's docker image, but host['docker_image_id'] was not set")
|
475
456
|
end
|
476
457
|
end
|
477
458
|
end
|
@@ -484,22 +465,20 @@ module Beaker
|
|
484
465
|
|
485
466
|
def buildargs_for(host)
|
486
467
|
docker_buildargs = {}
|
487
|
-
docker_buildargs_env = ENV
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
@logger.warn("DOCKER_BUILDARGS environment variable appears invalid, no key found for value #{value}" )
|
495
|
-
end
|
468
|
+
docker_buildargs_env = ENV.fetch('DOCKER_BUILDARGS', nil)
|
469
|
+
docker_buildargs_env&.split(/ +|\t+/)&.each do |arg|
|
470
|
+
key, value = arg.split('=')
|
471
|
+
if key
|
472
|
+
docker_buildargs[key] = value
|
473
|
+
else
|
474
|
+
@logger.warn("DOCKER_BUILDARGS environment variable appears invalid, no key found for value #{value}")
|
496
475
|
end
|
497
476
|
end
|
498
|
-
if docker_buildargs.empty?
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
477
|
+
buildargs = if docker_buildargs.empty?
|
478
|
+
host['docker_buildargs'] || {}
|
479
|
+
else
|
480
|
+
docker_buildargs
|
481
|
+
end
|
503
482
|
@logger.debug("Docker build buildargs: #{buildargs}")
|
504
483
|
JSON.generate(buildargs)
|
505
484
|
end
|
@@ -512,7 +491,7 @@ module Beaker
|
|
512
491
|
DF
|
513
492
|
|
514
493
|
# Commands before any other commands. Can be used for eg. proxy configuration
|
515
|
-
dockerfile += (host['docker_image_first_commands'] || []).map { |cmd| "RUN #{cmd}\n" }.join
|
494
|
+
dockerfile += (host['docker_image_first_commands'] || []).map { |cmd| "RUN #{cmd}\n" }.join
|
516
495
|
|
517
496
|
# add platform-specific actions
|
518
497
|
service_name = 'sshd'
|
@@ -585,7 +564,7 @@ module Beaker
|
|
585
564
|
DF
|
586
565
|
|
587
566
|
# Any extra commands specified for the host
|
588
|
-
dockerfile += (host['docker_image_commands'] || []).map { |cmd| "RUN #{cmd}\n" }.join
|
567
|
+
dockerfile += (host['docker_image_commands'] || []).map { |cmd| "RUN #{cmd}\n" }.join
|
589
568
|
|
590
569
|
# Override image entrypoint
|
591
570
|
dockerfile += "ENTRYPOINT #{host['docker_image_entrypoint']}\n" if host['docker_image_entrypoint']
|
@@ -640,24 +619,25 @@ module Beaker
|
|
640
619
|
|
641
620
|
if id
|
642
621
|
@logger.debug("Looking for an existing container with ID #{id}")
|
643
|
-
container = containers.
|
622
|
+
container = containers.find { |c| c.id == id }
|
644
623
|
end
|
645
624
|
|
646
625
|
if name && container.nil?
|
647
626
|
@logger.debug("Looking for an existing container with name #{name}")
|
648
|
-
container = containers.
|
627
|
+
container = containers.find do |c|
|
649
628
|
c.info['Names'].include? "/#{name}"
|
650
|
-
end
|
629
|
+
end
|
651
630
|
end
|
652
631
|
|
653
632
|
return container unless container.nil?
|
654
|
-
|
655
|
-
|
633
|
+
|
634
|
+
@logger.debug('Existing container not found')
|
635
|
+
nil
|
656
636
|
end
|
657
637
|
|
658
638
|
# return true if we are inside a docker container
|
659
639
|
def in_container?
|
660
|
-
|
640
|
+
File.file?('/.dockerenv')
|
661
641
|
end
|
662
642
|
end
|
663
643
|
end
|