beaker-docker 1.4.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/.editorconfig +10 -0
- data/.github/workflows/test.yml +30 -60
- data/.gitignore +1 -0
- data/.rubocop.yml +5 -25
- data/.rubocop_todo.yml +9 -706
- data/.simplecov +2 -0
- data/CHANGELOG.md +51 -1
- data/Gemfile +3 -5
- data/Rakefile +37 -137
- data/acceptance/tests/00_default_spec.rb +5 -4
- data/beaker-docker.gemspec +21 -20
- data/bin/beaker-docker +8 -10
- data/lib/beaker/hypervisor/docker.rb +190 -217
- data/lib/beaker-docker/version.rb +3 -1
- data/lib/beaker-docker.rb +1 -0
- data/spec/beaker/hypervisor/docker_spec.rb +371 -389
- data/spec/spec_helper.rb +6 -5
- metadata +34 -38
- data/Gemfile.local +0 -3
@@ -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 @docker_version['Version']
|
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 host[:docker_cmd]
|
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,86 +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
|
153
|
+
gw = network_settings['Gateway']
|
154
|
+
ip = gw unless gw.nil? || gw.empty?
|
155
|
+
else
|
156
|
+
# The many faces of container networking
|
161
157
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
port22 = network_settings.dig('Ports','22/tcp')
|
166
|
-
end
|
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?
|
167
161
|
|
168
|
-
|
169
|
-
|
162
|
+
ip = port22[0]['HostIp'] if port22
|
163
|
+
port = port22[0]['HostPort'] if port22
|
170
164
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
165
|
+
# Container to container
|
166
|
+
unless ip && port
|
167
|
+
ip = network_settings['IPAddress']
|
168
|
+
port = ip && !ip.empty? ? 22 : nil
|
169
|
+
end
|
175
170
|
|
176
|
-
|
177
|
-
|
178
|
-
|
171
|
+
# Container through gateway
|
172
|
+
unless ip && port
|
173
|
+
ip = network_settings['Gateway']
|
179
174
|
|
180
|
-
|
181
|
-
|
182
|
-
|
175
|
+
if ip && !ip.empty?
|
176
|
+
port22 = network_settings.dig('PortBindings', '22/tcp')
|
177
|
+
port = port22[0]['HostPort'] if port22
|
178
|
+
else
|
183
179
|
port = nil
|
184
|
-
|
185
|
-
ip = network_settings['Gateway']
|
186
|
-
|
187
|
-
if ip && !ip.empty?
|
188
|
-
port22 = network_settings.dig('PortBindings','22/tcp')
|
189
|
-
port = port22[0]['HostPort'] if port22
|
190
|
-
end
|
191
180
|
end
|
181
|
+
end
|
192
182
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
end
|
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
|
199
188
|
end
|
200
189
|
end
|
201
190
|
|
202
191
|
if host_config['NetworkMode'] != 'slirp4netns' && network_settings['IPAddress'] && !network_settings['IPAddress'].empty?
|
203
192
|
ip = network_settings['IPAddress'] if ip.nil?
|
204
193
|
else
|
205
|
-
port22 = network_settings.dig('Ports','22/tcp')
|
194
|
+
port22 = network_settings.dig('Ports', '22/tcp')
|
206
195
|
port = port22[0]['HostPort'] if port22
|
207
196
|
end
|
208
197
|
|
209
|
-
ssh_connection_info[:ip] =
|
198
|
+
ssh_connection_info[:ip] = ip == '0.0.0.0' ? '127.0.0.1' : ip
|
210
199
|
ssh_connection_info[:port] = port || '22'
|
211
200
|
ssh_connection_info
|
212
201
|
end
|
213
202
|
|
214
203
|
def provision
|
215
|
-
@logger.notify
|
204
|
+
@logger.notify 'Provisioning docker'
|
216
205
|
|
217
206
|
@hosts.each do |host|
|
218
207
|
@logger.notify "provisioning #{host.name}"
|
219
208
|
|
220
209
|
image = get_container_image(host)
|
221
210
|
|
222
|
-
if host['tag']
|
223
|
-
image.tag({:repo => host['tag']})
|
224
|
-
end
|
211
|
+
image.tag({ repo: host['tag'] }) if host['tag']
|
225
212
|
|
226
213
|
if @docker_type == 'swarm'
|
227
214
|
image_name = "#{@registry}/beaker/#{image.id}"
|
228
|
-
ret = ::Docker::Image.search(:
|
215
|
+
ret = ::Docker::Image.search(term: image_name)
|
229
216
|
if ret.first.nil?
|
230
|
-
@logger.debug(
|
231
|
-
image.tag({:
|
217
|
+
@logger.debug('Image does not exist on registry. Pushing.')
|
218
|
+
image.tag({ repo: image_name, force: true })
|
232
219
|
image.push
|
233
220
|
end
|
234
221
|
else
|
@@ -239,8 +226,8 @@ module Beaker
|
|
239
226
|
|
240
227
|
container_opts = get_container_opts(host, image_name)
|
241
228
|
if host['dockeropts'] || @options[:dockeropts]
|
242
|
-
dockeropts = host['dockeropts']
|
243
|
-
dockeropts.each do |k,v|
|
229
|
+
dockeropts = host['dockeropts'] || @options[:dockeropts]
|
230
|
+
dockeropts.each do |k, v|
|
244
231
|
container_opts[k] = v
|
245
232
|
end
|
246
233
|
end
|
@@ -255,12 +242,12 @@ module Beaker
|
|
255
242
|
container_opts['HostConfig']['Binds'] = host['mount_folders'].values.map do |mount|
|
256
243
|
host_path = File.expand_path(mount['host_path'])
|
257
244
|
# When using docker_toolbox and getting a "(Driveletter):/" path, convert windows path to VM mount
|
258
|
-
if ENV['DOCKER_TOOLBOX_INSTALL_PATH'] && host_path =~
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
if mount.
|
263
|
-
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')
|
264
251
|
else
|
265
252
|
a << mount['opts'] = 'z'
|
266
253
|
end
|
@@ -269,9 +256,7 @@ module Beaker
|
|
269
256
|
end
|
270
257
|
end
|
271
258
|
|
272
|
-
if host['docker_env']
|
273
|
-
container_opts['Env'] = host['docker_env']
|
274
|
-
end
|
259
|
+
container_opts['Env'] = host['docker_env'] if host['docker_env']
|
275
260
|
|
276
261
|
# Fixup privileges
|
277
262
|
#
|
@@ -286,11 +271,7 @@ module Beaker
|
|
286
271
|
container_opts['HostConfig']['Privileged'] = container_opts['HostConfig']['Privileged'].nil? ? true : container_opts['HostConfig']['Privileged']
|
287
272
|
end
|
288
273
|
|
289
|
-
|
290
|
-
container_opts['name'] = host['docker_container_name']
|
291
|
-
else
|
292
|
-
container_opts['name'] = ['beaker', host.name, SecureRandom.uuid.split('-').last].join('-')
|
293
|
-
end
|
274
|
+
container_opts['name'] = (host['docker_container_name'] || ['beaker', host.name, SecureRandom.uuid.split('-').last].join('-'))
|
294
275
|
|
295
276
|
if host['docker_port_bindings']
|
296
277
|
container_opts['ExposedPorts'] = {} if container_opts['ExposedPorts'].nil?
|
@@ -304,9 +285,9 @@ module Beaker
|
|
304
285
|
|
305
286
|
@logger.debug("Creating container from image #{image_name}")
|
306
287
|
|
307
|
-
ok=false
|
308
|
-
retries=0
|
309
|
-
while
|
288
|
+
ok = false
|
289
|
+
retries = 0
|
290
|
+
while !ok && (retries < 5)
|
310
291
|
container = ::Docker::Container.create(container_opts)
|
311
292
|
|
312
293
|
ssh_info = get_ssh_connection_info(container)
|
@@ -316,19 +297,19 @@ module Beaker
|
|
316
297
|
container.delete(force: true)
|
317
298
|
container = nil
|
318
299
|
|
319
|
-
retries+=1
|
300
|
+
retries += 1
|
320
301
|
next
|
321
302
|
end
|
322
303
|
|
323
|
-
ok=true
|
304
|
+
ok = true
|
324
305
|
end
|
325
306
|
else
|
326
307
|
host['use_existing_container'] = true
|
327
308
|
end
|
328
309
|
|
329
310
|
if container.nil?
|
330
|
-
raise
|
331
|
-
|
311
|
+
raise 'Cannot continue because no existing container ' \
|
312
|
+
'could be found and provisioning is disabled.'
|
332
313
|
end
|
333
314
|
|
334
315
|
fix_ssh(container) if @options[:provision] == false
|
@@ -368,19 +349,19 @@ module Beaker
|
|
368
349
|
host['ip'] = ip
|
369
350
|
host['port'] = port
|
370
351
|
host['ssh'] = {
|
371
|
-
:
|
372
|
-
:
|
373
|
-
:
|
374
|
-
:
|
352
|
+
password: root_password,
|
353
|
+
port: port,
|
354
|
+
forward_agent: forward_ssh_agent,
|
355
|
+
auth_methods: %w[password publickey hostbased keyboard-interactive],
|
375
356
|
}
|
376
357
|
|
377
358
|
@logger.debug("node available as ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
|
378
359
|
host['docker_container_id'] = container.id
|
379
360
|
host['docker_image_id'] = image.id
|
380
|
-
host['vm_ip'] = container.json[
|
361
|
+
host['vm_ip'] = container.json['NetworkSettings']['IPAddress'].to_s
|
381
362
|
|
382
363
|
def host.reboot
|
383
|
-
@logger.warn(
|
364
|
+
@logger.warn('Rebooting containers is ineffective...ignoring')
|
384
365
|
end
|
385
366
|
end
|
386
367
|
|
@@ -390,92 +371,88 @@ module Beaker
|
|
390
371
|
# This sideloads sshd after a container starts
|
391
372
|
def install_ssh_components(container, host)
|
392
373
|
case host['platform']
|
393
|
-
when /ubuntu/, /debian/
|
394
|
-
container.exec(%w
|
395
|
-
container.exec(%w
|
396
|
-
container.exec(%w
|
397
|
-
when /cumulus/
|
398
|
-
container.exec(%w(apt-get update))
|
399
|
-
container.exec(%w(apt-get install -y openssh-server openssh-client))
|
400
|
-
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/*])
|
401
378
|
when /el-[89]/, /fedora-(2[2-9]|3[0-9])/
|
402
|
-
container.exec(%w
|
403
|
-
container.exec(%w
|
404
|
-
container.exec(%w
|
405
|
-
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/*])
|
406
383
|
when /^el-/, /centos/, /fedora/, /redhat/, /eos/
|
407
|
-
container.exec(%w
|
408
|
-
container.exec(%w
|
409
|
-
container.exec(%w
|
410
|
-
container.exec(%w
|
411
|
-
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/*])
|
412
389
|
when /opensuse/, /sles/
|
413
|
-
container.exec(%w
|
414
|
-
container.exec(%w
|
415
|
-
container.exec(%w
|
416
|
-
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])
|
417
394
|
when /archlinux/
|
418
|
-
container.exec(%w
|
419
|
-
container.exec(%w
|
420
|
-
container.exec(%w
|
421
|
-
container.exec(%w
|
422
|
-
container.exec(%w
|
423
|
-
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])
|
424
401
|
when /alpine/
|
425
|
-
container.exec(%w
|
426
|
-
container.exec(%w
|
402
|
+
container.exec(%w[apk add --update openssh])
|
403
|
+
container.exec(%w[ssh-keygen -A])
|
427
404
|
else
|
428
|
-
# TODO add more platform steps here
|
405
|
+
# TODO: add more platform steps here
|
429
406
|
raise "platform #{host['platform']} not yet supported on docker"
|
430
407
|
end
|
431
408
|
|
432
409
|
# Make sshd directory, set root password
|
433
|
-
container.exec(%w
|
410
|
+
container.exec(%w[mkdir -p /var/run/sshd])
|
434
411
|
container.exec(['/bin/sh', '-c', "echo root:#{root_password} | chpasswd"])
|
435
412
|
end
|
436
413
|
|
437
414
|
def cleanup
|
438
|
-
@logger.notify
|
415
|
+
@logger.notify 'Cleaning up docker'
|
439
416
|
@hosts.each do |host|
|
440
417
|
# leave the container running if docker_preserve_container is set
|
441
418
|
# setting docker_preserve_container also implies docker_preserve_image
|
442
419
|
# is set, since you can't delete an image that's the base of a running
|
443
420
|
# container
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
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}")
|
460
437
|
end
|
438
|
+
end
|
461
439
|
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
else
|
476
|
-
@logger.warn("Intended to delete the host's docker image, but host['docker_image_id'] was not set")
|
477
|
-
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}")
|
478
453
|
end
|
454
|
+
else
|
455
|
+
@logger.warn("Intended to delete the host's docker image, but host['docker_image_id'] was not set")
|
479
456
|
end
|
480
457
|
end
|
481
458
|
end
|
@@ -488,22 +465,20 @@ module Beaker
|
|
488
465
|
|
489
466
|
def buildargs_for(host)
|
490
467
|
docker_buildargs = {}
|
491
|
-
docker_buildargs_env = ENV
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
@logger.warn("DOCKER_BUILDARGS environment variable appears invalid, no key found for value #{value}" )
|
499
|
-
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}")
|
500
475
|
end
|
501
476
|
end
|
502
|
-
if docker_buildargs.empty?
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
477
|
+
buildargs = if docker_buildargs.empty?
|
478
|
+
host['docker_buildargs'] || {}
|
479
|
+
else
|
480
|
+
docker_buildargs
|
481
|
+
end
|
507
482
|
@logger.debug("Docker build buildargs: #{buildargs}")
|
508
483
|
JSON.generate(buildargs)
|
509
484
|
end
|
@@ -516,7 +491,7 @@ module Beaker
|
|
516
491
|
DF
|
517
492
|
|
518
493
|
# Commands before any other commands. Can be used for eg. proxy configuration
|
519
|
-
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
|
520
495
|
|
521
496
|
# add platform-specific actions
|
522
497
|
service_name = 'sshd'
|
@@ -538,23 +513,20 @@ module Beaker
|
|
538
513
|
dockerfile += <<~DF
|
539
514
|
RUN dnf clean all \
|
540
515
|
&& dnf install -y sudo openssh-server openssh-clients #{additional_packages.join(' ')} \
|
541
|
-
&& ssh-keygen -
|
542
|
-
&& ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key \
|
516
|
+
&& ssh-keygen -A \
|
543
517
|
&& sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*
|
544
518
|
DF
|
545
519
|
when /^el-/, /centos/, /fedora/, /redhat/, /eos/
|
546
520
|
dockerfile += <<~DF
|
547
521
|
RUN yum clean all \
|
548
522
|
&& yum install -y sudo openssh-server openssh-clients #{additional_packages.join(' ')} \
|
549
|
-
&& ssh-keygen -
|
550
|
-
&& ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key \
|
523
|
+
&& ssh-keygen -A \
|
551
524
|
&& sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*
|
552
525
|
DF
|
553
526
|
when /opensuse/, /sles/
|
554
527
|
dockerfile += <<~DF
|
555
528
|
RUN zypper -n in openssh #{additional_packages.join(' ')} \
|
556
|
-
&& ssh-keygen -
|
557
|
-
&& ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key \
|
529
|
+
&& ssh-keygen -A \
|
558
530
|
&& sed -ri 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config \
|
559
531
|
&& sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/*
|
560
532
|
DF
|
@@ -592,7 +564,7 @@ module Beaker
|
|
592
564
|
DF
|
593
565
|
|
594
566
|
# Any extra commands specified for the host
|
595
|
-
dockerfile += (host['docker_image_commands'] || []).map { |cmd| "RUN #{cmd}\n" }.join
|
567
|
+
dockerfile += (host['docker_image_commands'] || []).map { |cmd| "RUN #{cmd}\n" }.join
|
596
568
|
|
597
569
|
# Override image entrypoint
|
598
570
|
dockerfile += "ENTRYPOINT #{host['docker_image_entrypoint']}\n" if host['docker_image_entrypoint']
|
@@ -622,7 +594,7 @@ module Beaker
|
|
622
594
|
'-e', 's/^#?UseDNS .*/UseDNS no/',
|
623
595
|
# Unbreak users with a bunch of SSH keys loaded in their keyring.
|
624
596
|
'-e', 's/^#?MaxAuthTries.*/MaxAuthTries 1000/',
|
625
|
-
'/etc/ssh/sshd_config'])
|
597
|
+
'/etc/ssh/sshd_config',])
|
626
598
|
|
627
599
|
return unless host
|
628
600
|
|
@@ -647,24 +619,25 @@ module Beaker
|
|
647
619
|
|
648
620
|
if id
|
649
621
|
@logger.debug("Looking for an existing container with ID #{id}")
|
650
|
-
container = containers.
|
622
|
+
container = containers.find { |c| c.id == id }
|
651
623
|
end
|
652
624
|
|
653
625
|
if name && container.nil?
|
654
626
|
@logger.debug("Looking for an existing container with name #{name}")
|
655
|
-
container = containers.
|
627
|
+
container = containers.find do |c|
|
656
628
|
c.info['Names'].include? "/#{name}"
|
657
|
-
end
|
629
|
+
end
|
658
630
|
end
|
659
631
|
|
660
632
|
return container unless container.nil?
|
661
|
-
|
662
|
-
|
633
|
+
|
634
|
+
@logger.debug('Existing container not found')
|
635
|
+
nil
|
663
636
|
end
|
664
637
|
|
665
638
|
# return true if we are inside a docker container
|
666
639
|
def in_container?
|
667
|
-
|
640
|
+
File.file?('/.dockerenv')
|
668
641
|
end
|
669
642
|
end
|
670
643
|
end
|