kitchen-docker 2.9.0 → 2.10.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/.gitignore +1 -0
- data/.kitchen.windows.yml +33 -0
- data/.kitchen.yml +8 -1
- data/.travis.yml +49 -15
- data/CHANGELOG.md +6 -0
- data/README.md +64 -12
- data/docker.ps1 +9 -0
- data/kitchen-docker.gemspec +4 -5
- data/lib/docker/version.rb +25 -0
- data/lib/kitchen/docker/container.rb +70 -0
- data/lib/kitchen/docker/container/linux.rb +211 -0
- data/lib/kitchen/docker/container/windows.rb +84 -0
- data/lib/kitchen/{driver → docker}/docker_version.rb +2 -5
- data/lib/kitchen/{driver/docker/erb.rb → docker/erb_context.rb} +2 -5
- data/lib/kitchen/docker/helpers/cli_helper.rb +147 -0
- data/lib/kitchen/docker/helpers/container_helper.rb +172 -0
- data/lib/kitchen/docker/helpers/file_helper.rb +40 -0
- data/lib/kitchen/docker/helpers/image_helper.rb +68 -0
- data/lib/kitchen/docker/helpers/inspec_helper.rb +40 -0
- data/lib/kitchen/driver/docker.rb +164 -457
- data/lib/kitchen/transport/docker.rb +111 -0
- data/lib/train/docker.rb +125 -0
- data/test/integration/default/serverspec/default_spec.rb +1 -1
- data/test/integration/default/serverspec/spec_helper.rb +21 -0
- data/test/integration/inspec/inspec_spec.rb +8 -2
- metadata +42 -7
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
require 'kitchen'
|
|
15
|
+
require 'kitchen/configurable'
|
|
16
|
+
require_relative 'cli_helper'
|
|
17
|
+
require_relative 'container_helper'
|
|
18
|
+
|
|
19
|
+
module Kitchen
|
|
20
|
+
module Docker
|
|
21
|
+
module Helpers
|
|
22
|
+
module ImageHelper
|
|
23
|
+
include Configurable
|
|
24
|
+
include Kitchen::Docker::Helpers::CliHelper
|
|
25
|
+
include Kitchen::Docker::Helpers::ContainerHelper
|
|
26
|
+
|
|
27
|
+
def parse_image_id(output)
|
|
28
|
+
output.each_line do |line|
|
|
29
|
+
if line =~ /image id|build successful|successfully built/i
|
|
30
|
+
return line.split(/\s+/).last
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
raise ActionFailed, 'Could not parse Docker build output for image ID'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def remove_image(state)
|
|
37
|
+
image_id = state[:image_id]
|
|
38
|
+
docker_command("rmi #{image_id}")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def build_image(state, dockerfile)
|
|
42
|
+
cmd = 'build'
|
|
43
|
+
cmd << ' --no-cache' unless config[:use_cache]
|
|
44
|
+
extra_build_options = config_to_options(config[:build_options])
|
|
45
|
+
cmd << " #{extra_build_options}" unless extra_build_options.empty?
|
|
46
|
+
dockerfile_contents = dockerfile
|
|
47
|
+
build_context = config[:build_context] ? '.' : '-'
|
|
48
|
+
file = Tempfile.new('Dockerfile-kitchen', Dir.pwd)
|
|
49
|
+
output = begin
|
|
50
|
+
file.write(dockerfile)
|
|
51
|
+
file.close
|
|
52
|
+
docker_command("#{cmd} -f #{Shellwords.escape(dockerfile_path(file))} #{build_context}",
|
|
53
|
+
input: dockerfile_contents)
|
|
54
|
+
ensure
|
|
55
|
+
file.close unless file.closed?
|
|
56
|
+
file.unlink
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
parse_image_id(output)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def image_exists?(state)
|
|
63
|
+
state[:image_id] && !!docker_command("inspect --type=image #{state[:image_id]}") rescue false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# You may obtain a copy of the License at
|
|
5
|
+
#
|
|
6
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
#
|
|
8
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
# See the License for the specific language governing permissions and
|
|
12
|
+
# limitations under the License.
|
|
13
|
+
|
|
14
|
+
# This helper should be removed when the kitchen-inspec gem has been updated to include these runner options
|
|
15
|
+
begin
|
|
16
|
+
require 'kitchen/verifier/inspec'
|
|
17
|
+
|
|
18
|
+
# Add runner options for Docker transport for kitchen-inspec gem
|
|
19
|
+
module Kitchen
|
|
20
|
+
module Docker
|
|
21
|
+
module Helpers
|
|
22
|
+
module InspecHelper
|
|
23
|
+
Kitchen::Verifier::Inspec.class_eval do
|
|
24
|
+
def runner_options_for_docker(config_data)
|
|
25
|
+
opts = {
|
|
26
|
+
'backend' => 'docker',
|
|
27
|
+
'logger' => logger,
|
|
28
|
+
'host' => config_data[:container_id],
|
|
29
|
+
}
|
|
30
|
+
logger.debug "Connect to Container: #{opts['host']}"
|
|
31
|
+
opts
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
rescue LoadError => e
|
|
39
|
+
logger.debug("[Docker] kitchen-inspec gem not found for InSpec verifier. #{e}")
|
|
40
|
+
end
|
|
@@ -1,457 +1,164 @@
|
|
|
1
|
-
#
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
require '
|
|
18
|
-
require '
|
|
19
|
-
require '
|
|
20
|
-
|
|
21
|
-
require '
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
include ShellOut
|
|
37
|
-
|
|
38
|
-
default_config :binary, 'docker'
|
|
39
|
-
default_config :
|
|
40
|
-
default_config :
|
|
41
|
-
default_config :
|
|
42
|
-
default_config :
|
|
43
|
-
default_config :
|
|
44
|
-
default_config :
|
|
45
|
-
default_config :
|
|
46
|
-
default_config :
|
|
47
|
-
|
|
48
|
-
default_config :
|
|
49
|
-
default_config :
|
|
50
|
-
default_config :
|
|
51
|
-
default_config :
|
|
52
|
-
default_config :
|
|
53
|
-
default_config :
|
|
54
|
-
default_config :
|
|
55
|
-
default_config :
|
|
56
|
-
default_config :
|
|
57
|
-
default_config :
|
|
58
|
-
default_config :
|
|
59
|
-
default_config :
|
|
60
|
-
default_config :
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
use_sudo: config[:use_sudo],
|
|
166
|
-
log_subject: Thor::Util.snake_case(self.class.to_s),
|
|
167
|
-
}))
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def generate_keys
|
|
171
|
-
MUTEX_FOR_SSH_KEYS.synchronize do
|
|
172
|
-
if !File.exist?(config[:public_key]) || !File.exist?(config[:private_key])
|
|
173
|
-
private_key = OpenSSL::PKey::RSA.new(2048)
|
|
174
|
-
blobbed_key = Base64.encode64(private_key.to_blob).gsub("\n", '')
|
|
175
|
-
public_key = "ssh-rsa #{blobbed_key} kitchen_docker_key"
|
|
176
|
-
File.open(config[:private_key], 'w') do |file|
|
|
177
|
-
file.write(private_key)
|
|
178
|
-
file.chmod(0600)
|
|
179
|
-
end
|
|
180
|
-
File.open(config[:public_key], 'w') do |file|
|
|
181
|
-
file.write(public_key)
|
|
182
|
-
file.chmod(0600)
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def build_dockerfile
|
|
189
|
-
from = "FROM #{config[:image]}"
|
|
190
|
-
|
|
191
|
-
env_variables = ''
|
|
192
|
-
if config[:http_proxy]
|
|
193
|
-
env_variables << "ENV http_proxy #{config[:http_proxy]}\n"
|
|
194
|
-
env_variables << "ENV HTTP_PROXY #{config[:http_proxy]}\n"
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
if config[:https_proxy]
|
|
198
|
-
env_variables << "ENV https_proxy #{config[:https_proxy]}\n"
|
|
199
|
-
env_variables << "ENV HTTPS_PROXY #{config[:https_proxy]}\n"
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
if config[:no_proxy]
|
|
203
|
-
env_variables << "ENV no_proxy #{config[:no_proxy]}\n"
|
|
204
|
-
env_variables << "ENV NO_PROXY #{config[:no_proxy]}\n"
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
platform = case config[:platform]
|
|
208
|
-
when 'debian', 'ubuntu'
|
|
209
|
-
disable_upstart = <<-eos
|
|
210
|
-
RUN [ ! -f "/sbin/initctl" ] || dpkg-divert --local --rename --add /sbin/initctl && ln -sf /bin/true /sbin/initctl
|
|
211
|
-
eos
|
|
212
|
-
packages = <<-eos
|
|
213
|
-
ENV DEBIAN_FRONTEND noninteractive
|
|
214
|
-
ENV container docker
|
|
215
|
-
RUN apt-get update
|
|
216
|
-
RUN apt-get install -y sudo openssh-server curl lsb-release
|
|
217
|
-
eos
|
|
218
|
-
config[:disable_upstart] ? disable_upstart + packages : packages
|
|
219
|
-
when 'rhel', 'centos', 'oraclelinux', 'amazonlinux'
|
|
220
|
-
<<-eos
|
|
221
|
-
ENV container docker
|
|
222
|
-
RUN yum clean all
|
|
223
|
-
RUN yum install -y sudo openssh-server openssh-clients which curl
|
|
224
|
-
RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ''
|
|
225
|
-
RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N ''
|
|
226
|
-
eos
|
|
227
|
-
when 'fedora'
|
|
228
|
-
<<-eos
|
|
229
|
-
ENV container docker
|
|
230
|
-
RUN dnf clean all
|
|
231
|
-
RUN dnf install -y sudo openssh-server openssh-clients which curl
|
|
232
|
-
RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ''
|
|
233
|
-
RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N ''
|
|
234
|
-
eos
|
|
235
|
-
when 'opensuse/tumbleweed', 'opensuse/leap', 'opensuse', 'sles'
|
|
236
|
-
<<-eos
|
|
237
|
-
ENV container docker
|
|
238
|
-
RUN zypper install -y sudo openssh which curl
|
|
239
|
-
RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ''
|
|
240
|
-
RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N ''
|
|
241
|
-
eos
|
|
242
|
-
when 'arch'
|
|
243
|
-
# See https://bugs.archlinux.org/task/47052 for why we
|
|
244
|
-
# blank out limits.conf.
|
|
245
|
-
<<-eos
|
|
246
|
-
RUN pacman --noconfirm -Sy archlinux-keyring
|
|
247
|
-
RUN pacman-db-upgrade
|
|
248
|
-
RUN pacman --noconfirm -Syu openssl openssh sudo curl
|
|
249
|
-
RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key
|
|
250
|
-
RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key
|
|
251
|
-
RUN echo >/etc/security/limits.conf
|
|
252
|
-
eos
|
|
253
|
-
when 'gentoo'
|
|
254
|
-
<<-eos
|
|
255
|
-
RUN emerge --sync
|
|
256
|
-
RUN emerge net-misc/openssh app-admin/sudo
|
|
257
|
-
RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key
|
|
258
|
-
RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key
|
|
259
|
-
eos
|
|
260
|
-
when 'gentoo-paludis'
|
|
261
|
-
<<-eos
|
|
262
|
-
RUN cave sync
|
|
263
|
-
RUN cave resolve -zx net-misc/openssh app-admin/sudo
|
|
264
|
-
RUN [ -f "/etc/ssh/ssh_host_rsa_key" ] || ssh-keygen -A -t rsa -f /etc/ssh/ssh_host_rsa_key
|
|
265
|
-
RUN [ -f "/etc/ssh/ssh_host_dsa_key" ] || ssh-keygen -A -t dsa -f /etc/ssh/ssh_host_dsa_key
|
|
266
|
-
eos
|
|
267
|
-
else
|
|
268
|
-
raise ActionFailed,
|
|
269
|
-
"Unknown platform '#{config[:platform]}'"
|
|
270
|
-
end
|
|
271
|
-
|
|
272
|
-
username = config[:username]
|
|
273
|
-
public_key = IO.read(config[:public_key]).strip
|
|
274
|
-
homedir = username == 'root' ? '/root' : "/home/#{username}"
|
|
275
|
-
|
|
276
|
-
base = <<-eos
|
|
277
|
-
RUN if ! getent passwd #{username}; then \
|
|
278
|
-
useradd -d #{homedir} -m -s /bin/bash -p '*' #{username}; \
|
|
279
|
-
fi
|
|
280
|
-
RUN echo "#{username} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
|
281
|
-
RUN echo "Defaults !requiretty" >> /etc/sudoers
|
|
282
|
-
RUN mkdir -p #{homedir}/.ssh
|
|
283
|
-
RUN chown -R #{username} #{homedir}/.ssh
|
|
284
|
-
RUN chmod 0700 #{homedir}/.ssh
|
|
285
|
-
RUN touch #{homedir}/.ssh/authorized_keys
|
|
286
|
-
RUN chown #{username} #{homedir}/.ssh/authorized_keys
|
|
287
|
-
RUN chmod 0600 #{homedir}/.ssh/authorized_keys
|
|
288
|
-
RUN mkdir -p /run/sshd
|
|
289
|
-
eos
|
|
290
|
-
custom = ''
|
|
291
|
-
Array(config[:provision_command]).each do |cmd|
|
|
292
|
-
custom << "RUN #{cmd}\n"
|
|
293
|
-
end
|
|
294
|
-
ssh_key = "RUN echo #{Shellwords.escape(public_key)} >> #{homedir}/.ssh/authorized_keys"
|
|
295
|
-
# Empty string to ensure the file ends with a newline.
|
|
296
|
-
[from, env_variables, platform, base, custom, ssh_key, ''].join("\n")
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
def dockerfile
|
|
300
|
-
if config[:dockerfile]
|
|
301
|
-
template = IO.read(File.expand_path(config[:dockerfile]))
|
|
302
|
-
context = DockerERBContext.new(config.to_hash)
|
|
303
|
-
ERB.new(template).result(context.get_binding)
|
|
304
|
-
else
|
|
305
|
-
build_dockerfile
|
|
306
|
-
end
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
def parse_image_id(output)
|
|
310
|
-
output.each_line do |line|
|
|
311
|
-
if line =~ /image id|build successful|successfully built/i
|
|
312
|
-
return line.split(/\s+/).last
|
|
313
|
-
end
|
|
314
|
-
end
|
|
315
|
-
raise ActionFailed,
|
|
316
|
-
'Could not parse Docker build output for image ID'
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
def build_image(state)
|
|
320
|
-
cmd = "build"
|
|
321
|
-
cmd << " --no-cache" unless config[:use_cache]
|
|
322
|
-
extra_build_options = config_to_options(config[:build_options])
|
|
323
|
-
cmd << " #{extra_build_options}" unless extra_build_options.empty?
|
|
324
|
-
dockerfile_contents = dockerfile
|
|
325
|
-
build_context = config[:build_context] ? '.' : '-'
|
|
326
|
-
file = Tempfile.new('Dockerfile-kitchen', Dir.pwd)
|
|
327
|
-
output = begin
|
|
328
|
-
file.write(dockerfile)
|
|
329
|
-
file.close
|
|
330
|
-
docker_command("#{cmd} -f #{Shellwords.escape(dockerfile_path(file))} #{build_context}", :input => dockerfile_contents)
|
|
331
|
-
ensure
|
|
332
|
-
file.close unless file.closed?
|
|
333
|
-
file.unlink
|
|
334
|
-
end
|
|
335
|
-
parse_image_id(output)
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
def parse_container_id(output)
|
|
339
|
-
container_id = output.chomp
|
|
340
|
-
unless [12, 64].include?(container_id.size)
|
|
341
|
-
raise ActionFailed,
|
|
342
|
-
'Could not parse Docker run output for container ID'
|
|
343
|
-
end
|
|
344
|
-
container_id
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
def build_run_command(image_id)
|
|
348
|
-
cmd = "run -d -p 22"
|
|
349
|
-
Array(config[:forward]).each {|port| cmd << " -p #{port}"}
|
|
350
|
-
Array(config[:dns]).each {|dns| cmd << " --dns #{dns}"}
|
|
351
|
-
Array(config[:add_host]).each {|host, ip| cmd << " --add-host=#{host}:#{ip}"}
|
|
352
|
-
Array(config[:volume]).each {|volume| cmd << " -v #{volume}"}
|
|
353
|
-
Array(config[:volumes_from]).each {|container| cmd << " --volumes-from #{container}"}
|
|
354
|
-
Array(config[:links]).each {|link| cmd << " --link #{link}"}
|
|
355
|
-
Array(config[:devices]).each {|device| cmd << " --device #{device}"}
|
|
356
|
-
cmd << " --name #{config[:instance_name]}" if config[:instance_name]
|
|
357
|
-
cmd << " -P" if config[:publish_all]
|
|
358
|
-
cmd << " -h #{config[:hostname]}" if config[:hostname]
|
|
359
|
-
cmd << " -m #{config[:memory]}" if config[:memory]
|
|
360
|
-
cmd << " -c #{config[:cpu]}" if config[:cpu]
|
|
361
|
-
cmd << " -e http_proxy=#{config[:http_proxy]}" if config[:http_proxy]
|
|
362
|
-
cmd << " -e https_proxy=#{config[:https_proxy]}" if config[:https_proxy]
|
|
363
|
-
cmd << " --privileged" if config[:privileged]
|
|
364
|
-
Array(config[:cap_add]).each {|cap| cmd << " --cap-add=#{cap}"} if config[:cap_add]
|
|
365
|
-
Array(config[:cap_drop]).each {|cap| cmd << " --cap-drop=#{cap}"} if config[:cap_drop]
|
|
366
|
-
Array(config[:security_opt]).each {|opt| cmd << " --security-opt=#{opt}"} if config[:security_opt]
|
|
367
|
-
extra_run_options = config_to_options(config[:run_options])
|
|
368
|
-
cmd << " #{extra_run_options}" unless extra_run_options.empty?
|
|
369
|
-
cmd << " #{image_id} #{config[:run_command]}"
|
|
370
|
-
cmd
|
|
371
|
-
end
|
|
372
|
-
|
|
373
|
-
def run_container(state)
|
|
374
|
-
cmd = build_run_command(state[:image_id])
|
|
375
|
-
output = docker_command(cmd)
|
|
376
|
-
parse_container_id(output)
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
def container_exists?(state)
|
|
380
|
-
state[:container_id] && !!docker_command("top #{state[:container_id]}") rescue false
|
|
381
|
-
end
|
|
382
|
-
|
|
383
|
-
def image_exists?(state)
|
|
384
|
-
state[:image_id] && !!docker_command("docker inspect --type=image #{state[:image_id]}") rescue false
|
|
385
|
-
end
|
|
386
|
-
|
|
387
|
-
def parse_container_ssh_port(output)
|
|
388
|
-
begin
|
|
389
|
-
_host, port = output.split(':')
|
|
390
|
-
port.to_i
|
|
391
|
-
rescue
|
|
392
|
-
raise ActionFailed,
|
|
393
|
-
'Could not parse Docker port output for container SSH port'
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
|
|
397
|
-
def container_ssh_port(state)
|
|
398
|
-
begin
|
|
399
|
-
if config[:use_internal_docker_network]
|
|
400
|
-
return 22
|
|
401
|
-
end
|
|
402
|
-
output = docker_command("port #{state[:container_id]} 22/tcp")
|
|
403
|
-
parse_container_ssh_port(output)
|
|
404
|
-
rescue
|
|
405
|
-
raise ActionFailed,
|
|
406
|
-
'Docker reports container has no ssh port mapped'
|
|
407
|
-
end
|
|
408
|
-
end
|
|
409
|
-
|
|
410
|
-
def container_ip(state)
|
|
411
|
-
begin
|
|
412
|
-
cmd = "inspect --format '{{ .NetworkSettings.IPAddress }}'"
|
|
413
|
-
cmd << " #{state[:container_id]}"
|
|
414
|
-
docker_command(cmd).strip
|
|
415
|
-
rescue
|
|
416
|
-
raise ActionFailed,
|
|
417
|
-
'Error getting internal IP of Docker container'
|
|
418
|
-
end
|
|
419
|
-
end
|
|
420
|
-
|
|
421
|
-
def rm_container(state)
|
|
422
|
-
container_id = state[:container_id]
|
|
423
|
-
docker_command("stop -t 0 #{container_id}")
|
|
424
|
-
docker_command("rm #{container_id}")
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
def rm_image(state)
|
|
428
|
-
image_id = state[:image_id]
|
|
429
|
-
docker_command("rmi #{image_id}")
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
# Convert the config input for `:build_options` or `:run_options` in to a
|
|
433
|
-
# command line string for use with Docker.
|
|
434
|
-
#
|
|
435
|
-
# @since 2.5.0
|
|
436
|
-
# @param config [nil, String, Array, Hash] Config data to convert.
|
|
437
|
-
# @return [String]
|
|
438
|
-
def config_to_options(config)
|
|
439
|
-
case config
|
|
440
|
-
when nil
|
|
441
|
-
''
|
|
442
|
-
when String
|
|
443
|
-
config
|
|
444
|
-
when Array
|
|
445
|
-
config.map {|c| config_to_options(c) }.join(' ')
|
|
446
|
-
when Hash
|
|
447
|
-
config.map {|k, v| Array(v).map {|c| "--#{k}=#{Shellwords.escape(c)}" }.join(' ') }.join(' ')
|
|
448
|
-
end
|
|
449
|
-
end
|
|
450
|
-
|
|
451
|
-
def dockerfile_path(file)
|
|
452
|
-
config[:build_context] ? Pathname.new(file.path).relative_path_from(Pathname.pwd).to_s : file.path
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
end
|
|
456
|
-
end
|
|
457
|
-
end
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2014, Sean Porter
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
require 'kitchen'
|
|
17
|
+
require 'json'
|
|
18
|
+
require 'securerandom'
|
|
19
|
+
require 'net/ssh'
|
|
20
|
+
|
|
21
|
+
require 'kitchen/driver/base'
|
|
22
|
+
|
|
23
|
+
require_relative '../docker/container/linux'
|
|
24
|
+
require_relative '../docker/container/windows'
|
|
25
|
+
require_relative '../docker/helpers/cli_helper'
|
|
26
|
+
require_relative '../docker/helpers/container_helper'
|
|
27
|
+
|
|
28
|
+
module Kitchen
|
|
29
|
+
module Driver
|
|
30
|
+
# Docker driver for Kitchen.
|
|
31
|
+
#
|
|
32
|
+
# @author Sean Porter <portertech@gmail.com>
|
|
33
|
+
class Docker < Kitchen::Driver::Base
|
|
34
|
+
include Kitchen::Docker::Helpers::CliHelper
|
|
35
|
+
include Kitchen::Docker::Helpers::ContainerHelper
|
|
36
|
+
include ShellOut
|
|
37
|
+
|
|
38
|
+
default_config :binary, 'docker'
|
|
39
|
+
default_config :build_options, nil
|
|
40
|
+
default_config :cap_add, nil
|
|
41
|
+
default_config :cap_drop, nil
|
|
42
|
+
default_config :disable_upstart, true
|
|
43
|
+
default_config :env_variables, nil
|
|
44
|
+
default_config :interactive, false
|
|
45
|
+
default_config :private_key, File.join(Dir.pwd, '.kitchen', 'docker_id_rsa')
|
|
46
|
+
default_config :privileged, false
|
|
47
|
+
default_config :public_key, File.join(Dir.pwd, '.kitchen', 'docker_id_rsa.pub')
|
|
48
|
+
default_config :publish_all, false
|
|
49
|
+
default_config :remove_images, false
|
|
50
|
+
default_config :run_options, nil
|
|
51
|
+
default_config :security_opt, nil
|
|
52
|
+
default_config :tls, false
|
|
53
|
+
default_config :tls_cacert, nil
|
|
54
|
+
default_config :tls_cert, nil
|
|
55
|
+
default_config :tls_key, nil
|
|
56
|
+
default_config :tls_verify, false
|
|
57
|
+
default_config :tty, false
|
|
58
|
+
default_config :use_cache, true
|
|
59
|
+
default_config :use_internal_docker_network, false
|
|
60
|
+
default_config :use_sudo, false
|
|
61
|
+
default_config :wait_for_transport, true
|
|
62
|
+
|
|
63
|
+
default_config :build_context do |driver|
|
|
64
|
+
!driver.remote_socket?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
default_config :image do |driver|
|
|
68
|
+
driver.default_image
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
default_config :instance_name do |driver|
|
|
72
|
+
# Borrowed from kitchen-rackspace
|
|
73
|
+
[
|
|
74
|
+
driver.instance.name.gsub(/\W/, ''),
|
|
75
|
+
(Etc.getlogin || 'nologin').gsub(/\W/, ''),
|
|
76
|
+
Socket.gethostname.gsub(/\W/, '')[0..20],
|
|
77
|
+
Array.new(8) { rand(36).to_s(36) }.join
|
|
78
|
+
].join('-')
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
default_config :platform do |driver|
|
|
82
|
+
driver.default_platform
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
default_config :run_command do |driver|
|
|
86
|
+
if driver.windows_os?
|
|
87
|
+
# Launch arbitrary process to keep the Windows container alive
|
|
88
|
+
# If running in interactive mode, launch powershell.exe instead
|
|
89
|
+
if driver[:interactive]
|
|
90
|
+
'powershell.exe'
|
|
91
|
+
else
|
|
92
|
+
'ping -t localhost'
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
'/usr/sbin/sshd -D -o UseDNS=no -o UsePAM=no -o PasswordAuthentication=yes '\
|
|
96
|
+
'-o UsePrivilegeSeparation=no -o PidFile=/tmp/sshd.pid'
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
default_config :socket do |driver|
|
|
101
|
+
socket = 'unix:///var/run/docker.sock'
|
|
102
|
+
socket = 'npipe:////./pipe/docker_engine' if driver.windows_os?
|
|
103
|
+
ENV['DOCKER_HOST'] || socket
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
default_config :username do |driver|
|
|
107
|
+
# Return nil to prevent username from being added to Docker
|
|
108
|
+
# command line args for Windows if a username was not specified
|
|
109
|
+
if driver.windows_os?
|
|
110
|
+
nil
|
|
111
|
+
else
|
|
112
|
+
'kitchen'
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def verify_dependencies
|
|
117
|
+
run_command("#{config[:binary]} >> #{dev_null} 2>&1", quiet: true, use_sudo: config[:use_sudo])
|
|
118
|
+
rescue
|
|
119
|
+
raise UserError, 'You must first install the Docker CLI tool https://www.docker.com/get-started'
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def create(state)
|
|
123
|
+
container.create(state)
|
|
124
|
+
|
|
125
|
+
wait_for_transport(state)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def destroy(state)
|
|
129
|
+
container.destroy(state)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def wait_for_transport(state)
|
|
133
|
+
if config[:wait_for_transport]
|
|
134
|
+
instance.transport.connection(state) do |conn|
|
|
135
|
+
conn.wait_until_ready
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def default_image
|
|
141
|
+
platform, release = instance.platform.name.split('-')
|
|
142
|
+
if platform == 'centos' && release
|
|
143
|
+
release = 'centos' + release.split('.').first
|
|
144
|
+
end
|
|
145
|
+
release ? [platform, release].join(':') : platform
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def default_platform
|
|
149
|
+
instance.platform.name.split('-').first
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
protected
|
|
153
|
+
|
|
154
|
+
def container
|
|
155
|
+
@container ||= if windows_os?
|
|
156
|
+
Kitchen::Docker::Container::Windows.new(config)
|
|
157
|
+
else
|
|
158
|
+
Kitchen::Docker::Container::Linux.new(config)
|
|
159
|
+
end
|
|
160
|
+
@container
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|