beaker-docker 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +25 -0
- data/.simplecov +9 -0
- data/Gemfile +25 -0
- data/LICENSE +202 -0
- data/README.md +36 -0
- data/Rakefile +161 -0
- data/acceptance/config/nodes/hosts.yaml +25 -0
- data/beaker-docker.gemspec +38 -0
- data/bin/beaker-docker +32 -0
- data/docker.md +148 -0
- data/lib/beaker-docker/version.rb +3 -0
- data/lib/beaker/hypervisor/docker.rb +336 -0
- data/spec/beaker/hypervisor/docker_spec.rb +491 -0
- data/spec/spec_helper.rb +17 -0
- metadata +213 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
---
|
2
|
+
HOSTS:
|
3
|
+
ubuntu1604-64-1:
|
4
|
+
platform: ubuntu-1604-x86_64
|
5
|
+
hypervisor: docker
|
6
|
+
image: ubuntu:16.04
|
7
|
+
roles:
|
8
|
+
- master
|
9
|
+
- agent
|
10
|
+
- dashboard
|
11
|
+
- database
|
12
|
+
- classifier
|
13
|
+
- default
|
14
|
+
docker_cmd: '["/sbin/init"]'
|
15
|
+
ubuntu1604-64-2:
|
16
|
+
platform: ubuntu-1604-x86_64
|
17
|
+
hypervisor: docker
|
18
|
+
image: ubuntu:16.04
|
19
|
+
roles:
|
20
|
+
- agent
|
21
|
+
docker_cmd: '["/sbin/init"]'
|
22
|
+
CONFIG:
|
23
|
+
nfs_server: none
|
24
|
+
consoleport: 443
|
25
|
+
log_level: verbose
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
require 'beaker-docker/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "beaker-docker"
|
7
|
+
s.version = BeakerDocker::VERSION
|
8
|
+
s.authors = ["Rishi Javia, Kevin Imber, Tony Vu"]
|
9
|
+
s.email = ["rishi.javia@puppet.com, kevin.imber@puppet.com, tony.vu@puppet.com"]
|
10
|
+
s.homepage = "https://github.com/puppetlabs/beaker-docker"
|
11
|
+
s.summary = %q{Beaker DSL Extension Helpers!}
|
12
|
+
s.description = %q{For use for the Beaker acceptance testing tool}
|
13
|
+
s.license = 'Apache2'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
# Testing dependencies
|
21
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
22
|
+
s.add_development_dependency 'rspec-its'
|
23
|
+
s.add_development_dependency 'fakefs', '~> 0.6'
|
24
|
+
s.add_development_dependency 'rake', '~> 10.1'
|
25
|
+
s.add_development_dependency 'simplecov'
|
26
|
+
s.add_development_dependency 'pry', '~> 0.10'
|
27
|
+
|
28
|
+
# Documentation dependencies
|
29
|
+
s.add_development_dependency 'yard'
|
30
|
+
s.add_development_dependency 'markdown'
|
31
|
+
s.add_development_dependency 'thin'
|
32
|
+
|
33
|
+
# Run time dependencies
|
34
|
+
s.add_runtime_dependency 'stringify-hash', '~> 0.0.0'
|
35
|
+
s.add_runtime_dependency 'docker-api'
|
36
|
+
|
37
|
+
end
|
38
|
+
|
data/bin/beaker-docker
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems' unless defined?(Gem)
|
4
|
+
require 'beaker-docker'
|
5
|
+
|
6
|
+
VERSION_STRING =
|
7
|
+
"
|
8
|
+
_ .--.
|
9
|
+
( ` )
|
10
|
+
beaker-docker .-' `--,
|
11
|
+
_..----.. ( )`-.
|
12
|
+
.'_|` _|` _|( .__, )
|
13
|
+
/_| _| _| _( (_, .-'
|
14
|
+
;| _| _| _| '-'__,--'`--'
|
15
|
+
| _| _| _| _| |
|
16
|
+
_ || _| _| _| _| %s
|
17
|
+
_( `--.\\_| _| _| _|/
|
18
|
+
.-' )--,| _| _|.`
|
19
|
+
(__, (_ ) )_| _| /
|
20
|
+
`-.__.\\ _,--'\\|__|__/
|
21
|
+
;____;
|
22
|
+
\\YT/
|
23
|
+
||
|
24
|
+
|\"\"|
|
25
|
+
'=='
|
26
|
+
"
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
puts BeakerDocker::VERSION
|
31
|
+
|
32
|
+
exit 0
|
data/docker.md
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
This option allows for testing against Docker containers.
|
2
|
+
|
3
|
+
|
4
|
+
### Why?
|
5
|
+
|
6
|
+
Using docker as a hypervisor significantly speeds up the provisioning process, as you don't have to spin up an entire VM to run the tests, which has significant overhead.
|
7
|
+
|
8
|
+
### How?
|
9
|
+
|
10
|
+
So first of all, install Docker using the instructions [here](https://docs.docker.com/installation/#installation).
|
11
|
+
|
12
|
+
In the real world, it's generally seen as [bad practice to have sshd running in a Docker container](http://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/). However, for the purpose of a disposable test instance, we're not going to worry about that!
|
13
|
+
|
14
|
+
### Basic docker hosts file ###
|
15
|
+
The base image to use for the container is named by the image key.
|
16
|
+
|
17
|
+
HOSTS:
|
18
|
+
ubuntu-12-10:
|
19
|
+
platform: ubuntu-12.10-x64
|
20
|
+
image: ubuntu:12.10
|
21
|
+
hypervisor: docker
|
22
|
+
CONFIG:
|
23
|
+
type: foss
|
24
|
+
|
25
|
+
### Docker hosts file, with image modification ###
|
26
|
+
You can specify extra commands to be executed in order to modify the image with the `docker_image_commands` key.
|
27
|
+
|
28
|
+
HOSTS:
|
29
|
+
ubuntu-12-10:
|
30
|
+
platform: ubuntu-12.10-x64
|
31
|
+
image: ubuntu:12.10
|
32
|
+
hypervisor: docker
|
33
|
+
docker_image_commands:
|
34
|
+
- 'apt-get install -y myapp'
|
35
|
+
- 'myapp --setup'
|
36
|
+
CONFIG:
|
37
|
+
type: foss
|
38
|
+
|
39
|
+
### Docker hosts files, with modified start commands ###
|
40
|
+
By default the docker container just runs an sshd which is adequate for 'puppet apply' style testing. You can specify a different command to start with the `docker_cmd` key. This gives you scope to run something with more service supervision baked into it, but it is is important that this command starts an sshd listening on port 22 so that beaker can drive the container.
|
41
|
+
|
42
|
+
HOSTS:
|
43
|
+
ubuntu-12-10:
|
44
|
+
platform: ubuntu-12.10-x64
|
45
|
+
image: ubuntu:12.10
|
46
|
+
hypervisor: docker
|
47
|
+
docker_cmd: '["/sbin/init"]'
|
48
|
+
CONFIG:
|
49
|
+
type: foss
|
50
|
+
|
51
|
+
### Preserve Docker Image ###
|
52
|
+
Unless the image configuration changes you might want to keep the Docker image for multiple spec runs. Use `docker_preserve_image` option for a host.
|
53
|
+
|
54
|
+
HOSTS:
|
55
|
+
ubuntu-12-10:
|
56
|
+
platform: ubuntu-12.10-x64
|
57
|
+
image: ubuntu:12.10
|
58
|
+
hypervisor: docker
|
59
|
+
docker_preserve_image: true
|
60
|
+
CONFIG:
|
61
|
+
type: foss
|
62
|
+
|
63
|
+
### Reuse Docker Image ###
|
64
|
+
In case you want to rerun the puppet again on the docker container, you can pass BEAKER_provision=no on the command line to set the env. Add this line in you default.ml file
|
65
|
+
|
66
|
+
```
|
67
|
+
HOSTS:
|
68
|
+
centos6-64:
|
69
|
+
roles:
|
70
|
+
- agent
|
71
|
+
platform: el-6-x86_64
|
72
|
+
image: centos:6.6
|
73
|
+
hypervisor: docker
|
74
|
+
CONFIG:
|
75
|
+
type: foss
|
76
|
+
log_level: verbose
|
77
|
+
ssh:
|
78
|
+
password: root
|
79
|
+
auth_methods: ["password"]
|
80
|
+
```
|
81
|
+
|
82
|
+
### Mounting volumes into your docker container ###
|
83
|
+
You can mount folders into a docker container:
|
84
|
+
|
85
|
+
HOSTS:
|
86
|
+
ubuntu-12-10:
|
87
|
+
platform: ubuntu-12.10-x64
|
88
|
+
image: ubuntu:12.10
|
89
|
+
hypervisor: docker
|
90
|
+
mount_folders:
|
91
|
+
name1:
|
92
|
+
host_path: host_path1
|
93
|
+
container_path: container_path1
|
94
|
+
name2:
|
95
|
+
host_path: host_path2
|
96
|
+
container_path: container_path2
|
97
|
+
opts: rw
|
98
|
+
CONFIG:
|
99
|
+
type: foss
|
100
|
+
|
101
|
+
### Example Output
|
102
|
+
|
103
|
+
For this example made a new docker nodeset file in the [puppetlabs-inifile](https://github.com/puppetlabs/puppetlabs-inifile) repo and ran the ini_setting_spec.rb spec:
|
104
|
+
|
105
|
+
```bash
|
106
|
+
$ bundle exec rspec spec/acceptance/ini_setting_spec.rb
|
107
|
+
Hypervisor for debian-7 is docker
|
108
|
+
Beaker::Hypervisor, found some docker boxes to create
|
109
|
+
Provisioning docker
|
110
|
+
provisioning debian-7
|
111
|
+
Creating image
|
112
|
+
Dockerfile is FROM debian:7.4
|
113
|
+
RUN apt-get update
|
114
|
+
RUN apt-get install -y openssh-server openssh-client curl ntpdate lsb-release
|
115
|
+
RUN mkdir -p /var/run/sshd
|
116
|
+
RUN echo root:root | chpasswd
|
117
|
+
RUN apt-get install -yq lsb-release wget net-tools ruby rubygems ruby1.8-dev libaugeas-dev libaugeas-ruby ntpdate locales-all
|
118
|
+
RUN REALLY_GEM_UPDATE_SYSTEM=1 gem update --system --no-ri --no-rdoc
|
119
|
+
EXPOSE 22
|
120
|
+
CMD ["/sbin/init"]
|
121
|
+
```
|
122
|
+
|
123
|
+
This step may take a while, as Docker will have to download the image. The subsequent runs will be a lot faster (as long as `docker_preserve_image: true` has been enabled).
|
124
|
+
|
125
|
+
For example, running this took 5 minutes to download and setup the `debian:7.4` image, but runs instantly the second time.
|
126
|
+
|
127
|
+
You should then see something like:
|
128
|
+
|
129
|
+
```
|
130
|
+
Creating container from image 3a86e5aba94d
|
131
|
+
post
|
132
|
+
/v1.15/containers/create
|
133
|
+
{}
|
134
|
+
{"Image":"3a86e5aba94d","Hostname":"debian-7"}
|
135
|
+
Starting container b8b31702b34b4aedd137c8a6a72fe730560bb00533e68764ba6263405f9244e4
|
136
|
+
post
|
137
|
+
/v1.15/containers/b8b31702b34b4aedd137c8a6a72fe730560bb00533e68764ba6263405f9244e4/start
|
138
|
+
{}
|
139
|
+
{"PublishAllPorts":true,"Privileged":true}
|
140
|
+
Using docker server at 192.168.59.103
|
141
|
+
get
|
142
|
+
/v1.15/containers/b8b31702b34b4aedd137c8a6a72fe730560bb00533e68764ba6263405f9244e4/json
|
143
|
+
{}
|
144
|
+
|
145
|
+
node available as ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@192.168.59.103 -p 49155
|
146
|
+
```
|
147
|
+
|
148
|
+
The tests should then run as normal from there.
|
@@ -0,0 +1,336 @@
|
|
1
|
+
module Beaker
|
2
|
+
class Docker < Beaker::Hypervisor
|
3
|
+
|
4
|
+
# Docker hypvervisor initializtion
|
5
|
+
# Env variables supported:
|
6
|
+
# DOCKER_REGISTRY: Docker registry URL
|
7
|
+
# DOCKER_HOST: Remote docker host
|
8
|
+
# DOCKER_BUILDARGS: Docker buildargs map
|
9
|
+
# @param [Host, Array<Host>, String, Symbol] hosts One or more hosts to act upon,
|
10
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
11
|
+
# @param [Hash{Symbol=>String}] options Options to pass on to the hypervisor
|
12
|
+
def initialize(hosts, options)
|
13
|
+
require 'docker'
|
14
|
+
@options = options
|
15
|
+
@logger = options[:logger]
|
16
|
+
@hosts = hosts
|
17
|
+
|
18
|
+
# increase the http timeouts as provisioning images can be slow
|
19
|
+
default_docker_options = { :write_timeout => 300, :read_timeout => 300 }.merge(::Docker.options || {})
|
20
|
+
# Merge docker options from the entry in hosts file
|
21
|
+
::Docker.options = default_docker_options.merge(@options[:docker_options] || {})
|
22
|
+
# assert that the docker-api gem can talk to your docker
|
23
|
+
# enpoint. Will raise if there is a version mismatch
|
24
|
+
begin
|
25
|
+
::Docker.validate_version!
|
26
|
+
rescue Excon::Errors::SocketError => e
|
27
|
+
raise "Docker instance not connectable.\nError was: #{e}\nCheck your DOCKER_HOST variable has been set\nIf you are on OSX or Windows, you might not have Docker Machine setup correctly: https://docs.docker.com/machine/\n"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Pass on all the logging from docker-api to the beaker logger instance
|
31
|
+
::Docker.logger = @logger
|
32
|
+
|
33
|
+
# Find out what kind of remote instance we are talking against
|
34
|
+
if ::Docker.version['Version'] =~ /swarm/
|
35
|
+
@docker_type = 'swarm'
|
36
|
+
unless ENV['DOCKER_REGISTRY']
|
37
|
+
raise "Using Swarm with beaker requires a private registry. Please setup the private registry and set the 'DOCKER_REGISTRY' env var"
|
38
|
+
else
|
39
|
+
@registry = ENV['DOCKER_REGISTRY']
|
40
|
+
end
|
41
|
+
else
|
42
|
+
@docker_type = 'docker'
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def provision
|
48
|
+
@logger.notify "Provisioning docker"
|
49
|
+
|
50
|
+
@hosts.each do |host|
|
51
|
+
@logger.notify "provisioning #{host.name}"
|
52
|
+
|
53
|
+
@logger.debug("Creating image")
|
54
|
+
image = ::Docker::Image.build(dockerfile_for(host), {
|
55
|
+
:rm => true, :buildargs => buildargs_for(host)
|
56
|
+
})
|
57
|
+
|
58
|
+
if @docker_type == 'swarm'
|
59
|
+
image_name = "#{@registry}/beaker/#{image.id}"
|
60
|
+
ret = ::Docker::Image.search(:term => image_name)
|
61
|
+
if ret.first.nil?
|
62
|
+
@logger.debug("Image does not exist on registry. Pushing.")
|
63
|
+
image.tag({:repo => image_name, :force => true})
|
64
|
+
image.push
|
65
|
+
end
|
66
|
+
else
|
67
|
+
image_name = image.id
|
68
|
+
end
|
69
|
+
|
70
|
+
container_opts = {
|
71
|
+
'Image' => image_name,
|
72
|
+
'Hostname' => host.name,
|
73
|
+
}
|
74
|
+
container = find_container(host)
|
75
|
+
|
76
|
+
# If the specified container exists, then use it rather creating a new one
|
77
|
+
if container.nil?
|
78
|
+
unless host['mount_folders'].nil?
|
79
|
+
container_opts['HostConfig'] ||= {}
|
80
|
+
container_opts['HostConfig']['Binds'] = host['mount_folders'].values.map do |mount|
|
81
|
+
a = [ File.expand_path(mount['host_path']), mount['container_path'] ]
|
82
|
+
a << mount['opts'] if mount.has_key?('opts')
|
83
|
+
a.join(':')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
if @options[:provision]
|
88
|
+
if host['docker_container_name']
|
89
|
+
container_opts['name'] = host['docker_container_name']
|
90
|
+
end
|
91
|
+
|
92
|
+
@logger.debug("Creating container from image #{image_name}")
|
93
|
+
container = ::Docker::Container.create(container_opts)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
if container.nil?
|
98
|
+
raise RuntimeError, 'Cannot continue because no existing container ' +
|
99
|
+
'could be found and provisioning is disabled.'
|
100
|
+
end
|
101
|
+
|
102
|
+
fix_ssh(container) if @options[:provision] == false
|
103
|
+
|
104
|
+
@logger.debug("Starting container #{container.id}")
|
105
|
+
container.start({"PublishAllPorts" => true, "Privileged" => true})
|
106
|
+
|
107
|
+
# Find out where the ssh port is from the container
|
108
|
+
# When running on swarm DOCKER_HOST points to the swarm manager so we have to get the
|
109
|
+
# IP of the swarm slave via the container data
|
110
|
+
# When we are talking to a normal docker instance DOCKER_HOST can point to a remote docker instance.
|
111
|
+
|
112
|
+
# Talking against a remote docker host which is a normal docker host
|
113
|
+
if @docker_type == 'docker' && ENV['DOCKER_HOST']
|
114
|
+
ip = URI.parse(ENV['DOCKER_HOST']).host
|
115
|
+
else
|
116
|
+
# Swarm or local docker host
|
117
|
+
ip = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostIp"]
|
118
|
+
end
|
119
|
+
|
120
|
+
@logger.info("Using docker server at #{ip}")
|
121
|
+
port = container.json["NetworkSettings"]["Ports"]["22/tcp"][0]["HostPort"]
|
122
|
+
|
123
|
+
forward_ssh_agent = @options[:forward_ssh_agent] || false
|
124
|
+
|
125
|
+
# Update host metadata
|
126
|
+
host['ip'] = ip
|
127
|
+
host['port'] = port
|
128
|
+
host['ssh'] = {
|
129
|
+
:password => root_password,
|
130
|
+
:port => port,
|
131
|
+
:forward_agent => forward_ssh_agent,
|
132
|
+
}
|
133
|
+
|
134
|
+
@logger.debug("node available as ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@#{ip} -p #{port}")
|
135
|
+
host['docker_container'] = container
|
136
|
+
host['docker_image'] = image
|
137
|
+
host['vm_ip'] = container.json["NetworkSettings"]["IPAddress"].to_s
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
hack_etc_hosts @hosts, @options
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
def cleanup
|
146
|
+
@logger.notify "Cleaning up docker"
|
147
|
+
@hosts.each do |host|
|
148
|
+
if container = host['docker_container']
|
149
|
+
@logger.debug("stop container #{container.id}")
|
150
|
+
begin
|
151
|
+
container.kill
|
152
|
+
sleep 2 # avoid a race condition where the root FS can't unmount
|
153
|
+
rescue Excon::Errors::ClientError => e
|
154
|
+
@logger.warn("stop of container #{container.id} failed: #{e.response.body}")
|
155
|
+
end
|
156
|
+
@logger.debug("delete container #{container.id}")
|
157
|
+
begin
|
158
|
+
container.delete
|
159
|
+
rescue Excon::Errors::ClientError => e
|
160
|
+
@logger.warn("deletion of container #{container.id} failed: #{e.response.body}")
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Do not remove the image if docker_reserve_image is set to true, otherwise remove it
|
165
|
+
if image = (host['docker_preserve_image'] ? nil : host['docker_image'])
|
166
|
+
@logger.debug("delete image #{image.id}")
|
167
|
+
begin
|
168
|
+
image.delete
|
169
|
+
rescue Excon::Errors::ClientError => e
|
170
|
+
@logger.warn("deletion of image #{image.id} failed: #{e.response.body}")
|
171
|
+
rescue ::Docker::Error::DockerError => e
|
172
|
+
@logger.warn("deletion of image #{image.id} caused internal Docker error: #{e.message}")
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
|
180
|
+
def root_password
|
181
|
+
'root'
|
182
|
+
end
|
183
|
+
|
184
|
+
def buildargs_for(host)
|
185
|
+
docker_buildargs = {}
|
186
|
+
docker_buildargs_env = ENV['DOCKER_BUILDARGS']
|
187
|
+
if docker_buildargs_env != nil
|
188
|
+
docker_buildargs_env.split(/ +|\t+/).each do |arg|
|
189
|
+
key,value=arg.split(/=/)
|
190
|
+
if key
|
191
|
+
docker_buildargs[key]=value
|
192
|
+
else
|
193
|
+
@logger.warn("DOCKER_BUILDARGS environment variable appears invalid, no key found for value #{value}" )
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
if docker_buildargs.empty?
|
198
|
+
buildargs = host['docker_buildargs'] || {}
|
199
|
+
else
|
200
|
+
buildargs = docker_buildargs
|
201
|
+
end
|
202
|
+
@logger.debug("Docker build buildargs: #{buildargs}")
|
203
|
+
JSON.generate(buildargs)
|
204
|
+
end
|
205
|
+
|
206
|
+
def dockerfile_for(host)
|
207
|
+
if host['dockerfile'] then
|
208
|
+
@logger.debug("attempting to load user Dockerfile from #{host['dockerfile']}")
|
209
|
+
if File.exist?(host['dockerfile']) then
|
210
|
+
dockerfile = File.read(host['dockerfile'])
|
211
|
+
else
|
212
|
+
raise "requested Dockerfile #{host['dockerfile']} does not exist"
|
213
|
+
end
|
214
|
+
else
|
215
|
+
raise("Docker image undefined!") if (host['image']||= nil).to_s.empty?
|
216
|
+
|
217
|
+
# specify base image
|
218
|
+
dockerfile = <<-EOF
|
219
|
+
FROM #{host['image']}
|
220
|
+
ENV container docker
|
221
|
+
EOF
|
222
|
+
|
223
|
+
# additional options to specify to the sshd
|
224
|
+
# may vary by platform
|
225
|
+
sshd_options = ''
|
226
|
+
|
227
|
+
# add platform-specific actions
|
228
|
+
service_name = "sshd"
|
229
|
+
case host['platform']
|
230
|
+
when /ubuntu/, /debian/
|
231
|
+
service_name = "ssh"
|
232
|
+
dockerfile += <<-EOF
|
233
|
+
RUN apt-get update
|
234
|
+
RUN apt-get install -y openssh-server openssh-client #{Beaker::HostPrebuiltSteps::DEBIAN_PACKAGES.join(' ')}
|
235
|
+
EOF
|
236
|
+
when /cumulus/
|
237
|
+
dockerfile += <<-EOF
|
238
|
+
RUN apt-get update
|
239
|
+
RUN apt-get install -y openssh-server openssh-client #{Beaker::HostPrebuiltSteps::CUMULUS_PACKAGES.join(' ')}
|
240
|
+
EOF
|
241
|
+
when /fedora-(2[2-9])/
|
242
|
+
dockerfile += <<-EOF
|
243
|
+
RUN dnf clean all
|
244
|
+
RUN dnf install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::UNIX_PACKAGES.join(' ')}
|
245
|
+
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
246
|
+
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
|
247
|
+
EOF
|
248
|
+
when /^el-/, /centos/, /fedora/, /redhat/, /eos/
|
249
|
+
dockerfile += <<-EOF
|
250
|
+
RUN yum clean all
|
251
|
+
RUN yum install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::UNIX_PACKAGES.join(' ')}
|
252
|
+
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
253
|
+
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
|
254
|
+
EOF
|
255
|
+
when /opensuse/, /sles/
|
256
|
+
dockerfile += <<-EOF
|
257
|
+
RUN zypper -n in openssh #{Beaker::HostPrebuiltSteps::SLES_PACKAGES.join(' ')}
|
258
|
+
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
|
259
|
+
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
|
260
|
+
RUN sed -ri 's/^#?UsePAM .*/UsePAM no/' /etc/ssh/sshd_config
|
261
|
+
EOF
|
262
|
+
else
|
263
|
+
# TODO add more platform steps here
|
264
|
+
raise "platform #{host['platform']} not yet supported on docker"
|
265
|
+
end
|
266
|
+
|
267
|
+
# Make sshd directory, set root password
|
268
|
+
dockerfile += <<-EOF
|
269
|
+
RUN mkdir -p /var/run/sshd
|
270
|
+
RUN echo root:#{root_password} | chpasswd
|
271
|
+
EOF
|
272
|
+
|
273
|
+
# Configure sshd service to allowroot login using password
|
274
|
+
# Also, disable reverse DNS lookups to prevent every. single. ssh
|
275
|
+
# operation taking 30 seconds while the lookup times out.
|
276
|
+
dockerfile += <<-EOF
|
277
|
+
RUN sed -ri 's/^#?PermitRootLogin .*/PermitRootLogin yes/' /etc/ssh/sshd_config
|
278
|
+
RUN sed -ri 's/^#?PasswordAuthentication .*/PasswordAuthentication yes/' /etc/ssh/sshd_config
|
279
|
+
RUN sed -ri 's/^#?UseDNS .*/UseDNS no/' /etc/ssh/sshd_config
|
280
|
+
EOF
|
281
|
+
|
282
|
+
|
283
|
+
# Any extra commands specified for the host
|
284
|
+
dockerfile += (host['docker_image_commands'] || []).map { |command|
|
285
|
+
"RUN #{command}\n"
|
286
|
+
}.join('')
|
287
|
+
|
288
|
+
# Override image entrypoint
|
289
|
+
if host['docker_image_entrypoint']
|
290
|
+
dockerfile += "ENTRYPOINT #{host['docker_image_entrypoint']}\n"
|
291
|
+
end
|
292
|
+
|
293
|
+
# How to start a sshd on port 22. May be an init for more supervision
|
294
|
+
# Ensure that the ssh server can be restarted (done from set_env) and container keeps running
|
295
|
+
cmd = host['docker_cmd'] || ["sh","-c","service #{service_name} start ; tail -f /dev/null"]
|
296
|
+
dockerfile += <<-EOF
|
297
|
+
EXPOSE 22
|
298
|
+
CMD #{cmd}
|
299
|
+
EOF
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
@logger.debug("Dockerfile is #{dockerfile}")
|
304
|
+
return dockerfile
|
305
|
+
end
|
306
|
+
|
307
|
+
# a puppet run may have changed the ssh config which would
|
308
|
+
# keep us out of the container. This is a best effort to fix it.
|
309
|
+
def fix_ssh(container)
|
310
|
+
@logger.debug("Fixing ssh on container #{container.id}")
|
311
|
+
container.exec(['sed','-ri',
|
312
|
+
's/^#?PermitRootLogin .*/PermitRootLogin yes/',
|
313
|
+
'/etc/ssh/sshd_config'])
|
314
|
+
container.exec(['sed','-ri',
|
315
|
+
's/^#?PasswordAuthentication .*/PasswordAuthentication yes/',
|
316
|
+
'/etc/ssh/sshd_config'])
|
317
|
+
container.exec(['sed','-ri',
|
318
|
+
's/^#?UseDNS .*/UseDNS no/',
|
319
|
+
'/etc/ssh/sshd_config'])
|
320
|
+
container.exec(%w(service ssh restart))
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
# return the existing container if we're not provisioning
|
325
|
+
# and docker_container_name is set
|
326
|
+
def find_container(host)
|
327
|
+
return nil if host['docker_container_name'].nil? || @options[:provision]
|
328
|
+
@logger.debug("Looking for an existing container called #{host['docker_container_name']}")
|
329
|
+
|
330
|
+
::Docker::Container.all.select do |c|
|
331
|
+
c.info['Names'].include? "/#{host['docker_container_name']}"
|
332
|
+
end.first
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
end
|