kitchen-dokken 0.0.1
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 +7 -0
- data/.cane +0 -0
- data/.gitignore +17 -0
- data/.tailor +4 -0
- data/.travis.yml +11 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +5 -0
- data/LICENSE +15 -0
- data/README.md +2 -0
- data/kitchen-dokken.gemspec +22 -0
- data/lib/kitchen/driver/dokken/helpers.rb +62 -0
- data/lib/kitchen/driver/dokken.rb +298 -0
- data/lib/kitchen/driver/dokken_version.rb +24 -0
- data/lib/kitchen/provisioner/dokken.rb +63 -0
- data/lib/kitchen/transport/dokken/helpers.rb +22 -0
- data/lib/kitchen/transport/dokken.rb +175 -0
- metadata +75 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: e002503da77ac2cfef2c4b438bbae22456c2c243
|
|
4
|
+
data.tar.gz: 195ee44c88ff70005443628b7680482ce4644575
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2e4ef34b0fb97a894b8d205b8b985668b71fb1908e43fc878fbad012ac5def9ad87670fc34b668556178f3fddaaf27abce1ae5bb3f3289a82dc31edcfcbb2654
|
|
7
|
+
data.tar.gz: 1689cc3dcc2246c9abd779e768d5287dff5096e7821eda24d86d62115471faa29fb3d2ac908edce865f95a49ffef7abd2c1d4ab4f304b73b90efd2d9ee8e5b54
|
data/.cane
ADDED
|
File without changes
|
data/.gitignore
ADDED
data/.tailor
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Author:: Sean OMeara (<sean@chef.io>)
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2015, Sean OMeara
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'kitchen/driver/dokken_version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'kitchen-dokken'
|
|
8
|
+
spec.version = Kitchen::Driver::DOKKEN_VERSION
|
|
9
|
+
spec.authors = ['Sean OMeara']
|
|
10
|
+
spec.email = ['sean@chef.io']
|
|
11
|
+
spec.description = 'A Test Kitchen Driver for Dokken'
|
|
12
|
+
spec.summary = 'A Test Kitchen Driver that talks to the Docker Remote API and uses Volumes to produce sparse container images'
|
|
13
|
+
spec.homepage = 'https://github.com/someara/kitchen-dokken'
|
|
14
|
+
spec.license = 'Apache 2.0'
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
|
17
|
+
spec.executables = []
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
spec.add_dependency 'test-kitchen', '~> 1.4'
|
|
22
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Dokken
|
|
2
|
+
module Driver
|
|
3
|
+
module Helpers
|
|
4
|
+
def insecure_ssh_public_key
|
|
5
|
+
<<-EOF
|
|
6
|
+
ssh-dss AAAAB3NzaC1kc3MAAACBANmw8lqXnnGoQ0LusVNr/716mQhEgxb8RYQbg+HP0w//XXVZki2iSC7/LhQEdYgUZaBYJKpBNQ3FSIvyfM5RksicEF10jv/QQ+gsQKHf/jyWTLSiiaSJiwhDrkNW94V/T2pczXlK2j5UiyGKA6UDmSeiS6Ve969nqLJLb77xWOlXAAAAFQCsaq9PvaFa+SXUfWYV9JrDskPtywAAAIBmTuJyTAqdy+xPiI7AFI+BCuWpjrczBs/aw3R5ArNaRf3/PBUumpAUCePJ6UPcw5vU74AloCYvcUnwU8IbZ/Oj6A5NGTo6HvIajP2Y8E17cjsMTXzTPbuT1SqkrlVcsQtJpHU/+WBGoUJeWg66/CjUp/Nx2YK+6QJzoALBLyJW+AAAAIEAyi7XX3Ev12AXgpwRPPbfVltJ9H5Hpll34gc2ORhmCSL6RE42BpAXuzI7lbGun2dXFsCdDm0DQz3h4JHtTHePd6xXqyPpUda4ktLVtEWMm0XIQNE8P5zP0gcfqVe4prOYeBLwrvAkyeNY5wosgzGHrQ+/hFwW3s8liEjZaFDhCWY= test-kitchen
|
|
7
|
+
EOF
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def insecure_ssh_private_key
|
|
11
|
+
<<-EOF
|
|
12
|
+
-----BEGIN DSA PRIVATE KEY-----
|
|
13
|
+
MIIBuwIBAAKBgQDZsPJal55xqENC7rFTa/+9epkIRIMW/EWEG4Phz9MP/111WZIt
|
|
14
|
+
okgu/y4UBHWIFGWgWCSqQTUNxUiL8nzOUZLInBBddI7/0EPoLECh3/48lky0oomk
|
|
15
|
+
iYsIQ65DVveFf09qXM15Sto+VIshigOlA5knokulXvevZ6iyS2++8VjpVwIVAKxq
|
|
16
|
+
r0+9oVr5JdR9ZhX0msOyQ+3LAoGAZk7ickwKncvsT4iOwBSPgQrlqY63MwbP2sN0
|
|
17
|
+
eQKzWkX9/zwVLpqQFAnjyelD3MOb1O+AJaAmL3FJ8FPCG2fzo+gOTRk6Oh7yGoz9
|
|
18
|
+
mPBNe3I7DE180z27k9UqpK5VXLELSaR1P/lgRqFCXloOuvwo1KfzcdmCvukCc6AC
|
|
19
|
+
wS8iVvgCgYEAyi7XX3Ev12AXgpwRPPbfVltJ9H5Hpll34gc2ORhmCSL6RE42BpAX
|
|
20
|
+
uzI7lbGun2dXFsCdDm0DQz3h4JHtTHePd6xXqyPpUda4ktLVtEWMm0XIQNE8P5zP
|
|
21
|
+
0gcfqVe4prOYeBLwrvAkyeNY5wosgzGHrQ+/hFwW3s8liEjZaFDhCWYCFASgG6eP
|
|
22
|
+
vVnsIrCx2rI5/KEQZ+oG
|
|
23
|
+
-----END DSA PRIVATE KEY-----
|
|
24
|
+
EOF
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def data_dockerfile
|
|
28
|
+
<<-EOF
|
|
29
|
+
FROM centos:7
|
|
30
|
+
MAINTAINER Sean OMeara \"sean@chef.io\"
|
|
31
|
+
|
|
32
|
+
ENV LANG en_US.UTF-8
|
|
33
|
+
|
|
34
|
+
RUN yum -y install tar rsync openssh-server passwd git
|
|
35
|
+
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -N ''
|
|
36
|
+
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -N ''
|
|
37
|
+
|
|
38
|
+
RUN mkdir -p /root/.ssh/
|
|
39
|
+
COPY authorized_keys /root/.ssh/authorized_keys
|
|
40
|
+
|
|
41
|
+
EXPOSE 22
|
|
42
|
+
CMD [ "/usr/sbin/sshd", "-D", "-p", "22", "-o", "UseDNS=no", "-o", "UsePrivilegeSeparation=no" ]
|
|
43
|
+
|
|
44
|
+
VOLUME /opt/kitchen
|
|
45
|
+
VOLUME /opt/verifier
|
|
46
|
+
EOF
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create_data_image
|
|
50
|
+
return if Docker::Image.exist?(data_image)
|
|
51
|
+
|
|
52
|
+
tmpdir = Dir.tmpdir
|
|
53
|
+
FileUtils.mkdir_p "#{tmpdir}/dokken"
|
|
54
|
+
File.write("#{tmpdir}/dokken/Dockerfile", data_dockerfile)
|
|
55
|
+
File.write("#{tmpdir}/dokken/authorized_keys", insecure_ssh_public_key)
|
|
56
|
+
|
|
57
|
+
i = Docker::Image.build_from_dir("#{tmpdir}/dokken", { 'nocache' => true, 'rm' => true })
|
|
58
|
+
i.tag('repo' => repo(data_image), 'tag' => tag(data_image), 'force' => true)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: Sean OMeara (<sean@chef.io>)
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2015, Sean OMeara
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
require 'kitchen'
|
|
19
|
+
require 'tmpdir'
|
|
20
|
+
require 'docker'
|
|
21
|
+
require_relative 'dokken/helpers'
|
|
22
|
+
|
|
23
|
+
include Dokken::Driver::Helpers
|
|
24
|
+
|
|
25
|
+
# FIXME: - make true
|
|
26
|
+
Excon.defaults[:ssl_verify_peer] = false
|
|
27
|
+
|
|
28
|
+
module Kitchen
|
|
29
|
+
module Driver
|
|
30
|
+
# Dokken driver for Kitchen.
|
|
31
|
+
#
|
|
32
|
+
# @author Sean OMeara <sean@chef.io>
|
|
33
|
+
class Dokken < Kitchen::Driver::Base
|
|
34
|
+
default_config :pid_one_command, 'sh -c "trap exit 0 SIGTERM; while :; do sleep 1; done"'
|
|
35
|
+
default_config :privileged, false
|
|
36
|
+
|
|
37
|
+
# (see Base#create)
|
|
38
|
+
def create(state)
|
|
39
|
+
# image to config
|
|
40
|
+
pull_platform_image
|
|
41
|
+
|
|
42
|
+
# chef
|
|
43
|
+
pull_chef_image
|
|
44
|
+
start_chef_container state
|
|
45
|
+
|
|
46
|
+
# data
|
|
47
|
+
make_data_image
|
|
48
|
+
start_data_container state
|
|
49
|
+
|
|
50
|
+
# work image
|
|
51
|
+
build_work_image state
|
|
52
|
+
|
|
53
|
+
# runner
|
|
54
|
+
start_runner_container state
|
|
55
|
+
|
|
56
|
+
# misc
|
|
57
|
+
save_misc_state state
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def destroy(_state)
|
|
61
|
+
delete_data
|
|
62
|
+
delete_runner
|
|
63
|
+
delete_work_image
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def connection
|
|
69
|
+
@connection ||= begin
|
|
70
|
+
opts = Docker.options
|
|
71
|
+
opts[:read_timeout] = 3600
|
|
72
|
+
opts[:write_timeout] = 3600
|
|
73
|
+
Docker::Connection.new(Docker.url, opts)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def delete_work_image
|
|
78
|
+
return unless Docker::Image.exist?(work_image, connection)
|
|
79
|
+
i = Docker::Image.get(work_image, connection)
|
|
80
|
+
i.remove(force: true)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def build_work_image(state)
|
|
84
|
+
return if Docker::Image.exist?(work_image, connection)
|
|
85
|
+
|
|
86
|
+
FileUtils.mkdir_p context_root
|
|
87
|
+
File.write("#{context_root}/Dockerfile", work_image_dockerfile)
|
|
88
|
+
|
|
89
|
+
i = Docker::Image.build_from_dir(context_root, { 'nocache' => true, 'rm' => true }, connection)
|
|
90
|
+
i.tag('repo' => repo(work_image), 'tag' => tag(work_image), 'force' => true)
|
|
91
|
+
state[:work_image] = work_image
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def context_root
|
|
95
|
+
tmpdir = Dir.tmpdir
|
|
96
|
+
"#{tmpdir}/dokken/#{instance_name}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def work_image_dockerfile
|
|
100
|
+
from = "FROM #{platform_image}"
|
|
101
|
+
custom = []
|
|
102
|
+
Array(config[:intermediate_instructions]).each { |c| custom << c }
|
|
103
|
+
[from, custom].join("\n")
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def save_misc_state(state)
|
|
107
|
+
state[:platform_image] = platform_image
|
|
108
|
+
state[:instance_name] = instance_name
|
|
109
|
+
state[:instance_platform_name] = instance_platform_name
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def instance_name
|
|
113
|
+
instance.name
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def instance_platform_name
|
|
117
|
+
instance.platform.name
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def work_image
|
|
121
|
+
return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
|
|
122
|
+
instance_name
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def image_prefix
|
|
126
|
+
'someara'
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def delete_runner
|
|
130
|
+
debug "driver - deleting container #{runner_container_name}"
|
|
131
|
+
delete_container runner_container_name
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def delete_chef_container
|
|
135
|
+
debug "driver - deleting container #{chef_container_name}"
|
|
136
|
+
delete_container chef_container_name
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def delete_data
|
|
140
|
+
debug "driver - deleting container #{data_container_name}"
|
|
141
|
+
delete_container data_container_name
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def start_runner_container(state)
|
|
145
|
+
debug "driver - starting #{runner_container_name}"
|
|
146
|
+
runner_container = run_container(
|
|
147
|
+
'name' => runner_container_name,
|
|
148
|
+
'Cmd' => Shellwords.shellwords(config[:pid_one_command]),
|
|
149
|
+
'Image' => "#{repo(work_image)}:#{tag(work_image)}",
|
|
150
|
+
'HostConfig' => {
|
|
151
|
+
'Privileged' => config[:privileged],
|
|
152
|
+
'VolumesFrom' => [chef_container_name, data_container_name]
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
state[:runner_container] = runner_container.json
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def start_data_container(state)
|
|
159
|
+
debug "driver - creating #{data_container_name}"
|
|
160
|
+
data_container = run_container(
|
|
161
|
+
'name' => data_container_name,
|
|
162
|
+
'Image' => "#{repo(data_image)}:#{tag(data_image)}",
|
|
163
|
+
'PortBindings' => {
|
|
164
|
+
'22/tcp' => [
|
|
165
|
+
{ 'HostPort' => '' }
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
'PublishAllPorts' => true
|
|
169
|
+
)
|
|
170
|
+
state[:data_container] = data_container.json
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def make_data_image
|
|
174
|
+
debug "driver - pulling #{data_image}"
|
|
175
|
+
pull_if_missing data_image
|
|
176
|
+
# -- or --
|
|
177
|
+
# debug 'driver - calling create_data_image'
|
|
178
|
+
# create_data_image
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def start_chef_container(state)
|
|
182
|
+
c = Docker::Container.get(chef_container_name)
|
|
183
|
+
rescue Docker::Error::NotFoundError
|
|
184
|
+
begin
|
|
185
|
+
debug "driver - creating volume container #{chef_container_name} from #{chef_image}"
|
|
186
|
+
chef_container = create_container(
|
|
187
|
+
'name' => chef_container_name,
|
|
188
|
+
'Cmd' => 'true',
|
|
189
|
+
'Image' => "#{repo(chef_image)}:#{tag(chef_image)}"
|
|
190
|
+
)
|
|
191
|
+
state[:chef_container] = chef_container.json
|
|
192
|
+
rescue
|
|
193
|
+
debug "driver - #{chef_container_name} alreay exists"
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def pull_platform_image
|
|
198
|
+
debug "driver - pulling #{chef_image} #{repo(platform_image)} #{tag(platform_image)}"
|
|
199
|
+
pull_if_missing platform_image
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def pull_chef_image
|
|
203
|
+
debug "driver - pulling #{chef_image} #{repo(chef_image)} #{tag(chef_image)}"
|
|
204
|
+
pull_if_missing chef_image
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def delete_image(name)
|
|
208
|
+
i = Docker::Image.get(name, connection)
|
|
209
|
+
i.remove(force: true)
|
|
210
|
+
rescue Docker::Error => e
|
|
211
|
+
puts "Image #{name} not found. Nothing to delete."
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def run_container(args)
|
|
215
|
+
c = create_container(args)
|
|
216
|
+
tries ||= 3
|
|
217
|
+
begin
|
|
218
|
+
c.start
|
|
219
|
+
return c
|
|
220
|
+
rescue Docker::Error => e
|
|
221
|
+
retry unless (tries -= 1).zero?
|
|
222
|
+
raise e.message
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def delete_container(name)
|
|
227
|
+
c = Docker::Container.get(name, connection)
|
|
228
|
+
puts "Destroying container #{name}."
|
|
229
|
+
c.stop
|
|
230
|
+
c.delete(force: true, v: true)
|
|
231
|
+
rescue
|
|
232
|
+
puts "Container #{name} not found. Nothing to delete."
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def container_exist?(name)
|
|
236
|
+
return true if Docker::Container.get(name)
|
|
237
|
+
rescue
|
|
238
|
+
false
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def create_container(args)
|
|
242
|
+
c = Docker::Container.create(args.clone, connection)
|
|
243
|
+
rescue Docker::Error::ConflictError
|
|
244
|
+
c = Docker::Container.get(args['name'])
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def repo(image)
|
|
248
|
+
image.split(':')[0]
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def tag(image)
|
|
252
|
+
image.split(':')[1] || 'latest'
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def pull_image(image)
|
|
256
|
+
retries ||= 3
|
|
257
|
+
Docker::Image.create({ 'fromImage' => repo(image), 'tag' => tag(image) }, connection)
|
|
258
|
+
rescue Docker::Error => e
|
|
259
|
+
retry unless (tries -= 1).zero?
|
|
260
|
+
raise e.message
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def pull_if_missing(image)
|
|
264
|
+
return if Docker::Image.exist?("#{repo(image)}:#{tag(image)}", connection)
|
|
265
|
+
pull_image image
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
def platform_image
|
|
269
|
+
config[:image]
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def chef_version
|
|
273
|
+
config[:chef_version]
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def chef_image
|
|
277
|
+
"someara/chef:#{chef_version}"
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def chef_container_name
|
|
281
|
+
"chef-#{chef_version}"
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
def data_image
|
|
285
|
+
'someara/kitchen-cache:latest'
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def data_container_name
|
|
289
|
+
# "#{instance.suite.name}-data"
|
|
290
|
+
"#{instance.name}-data"
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def runner_container_name
|
|
294
|
+
"#{instance.name}"
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Author:: Sean OMeara (<sean@chef.io>)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (C) 2015, Sean OMeara
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
module Kitchen
|
|
20
|
+
module Driver
|
|
21
|
+
# Version string for Dokken Kitchen driver
|
|
22
|
+
DOKKEN_VERSION = '0.0.1'
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
#
|
|
3
|
+
# Author:: Sean OMeara (<sean@chef.io>)
|
|
4
|
+
#
|
|
5
|
+
# Copyright (C) 2015, Sean OMeara
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
|
|
19
|
+
require 'kitchen'
|
|
20
|
+
require 'kitchen/provisioner/chef_zero'
|
|
21
|
+
|
|
22
|
+
module Kitchen
|
|
23
|
+
module Provisioner
|
|
24
|
+
# @author Sean OMeara <sean@chef.io>
|
|
25
|
+
class Dokken < Kitchen::Provisioner::ChefZero
|
|
26
|
+
kitchen_provisioner_api_version 2
|
|
27
|
+
|
|
28
|
+
plugin_version Kitchen::VERSION
|
|
29
|
+
|
|
30
|
+
default_config :root_path, '/opt/kitchen'
|
|
31
|
+
|
|
32
|
+
# (see Base#call)
|
|
33
|
+
def call(state)
|
|
34
|
+
create_sandbox
|
|
35
|
+
sandbox_dirs = Dir.glob(File.join(sandbox_path, '*'))
|
|
36
|
+
|
|
37
|
+
instance.transport.connection(state) do |conn|
|
|
38
|
+
info("Transferring files to #{instance.to_str}")
|
|
39
|
+
conn.upload(sandbox_dirs, config[:root_path])
|
|
40
|
+
debug('Transfer complete')
|
|
41
|
+
conn.execute(run_command)
|
|
42
|
+
end
|
|
43
|
+
rescue Kitchen::Transport::TransportFailed => ex
|
|
44
|
+
raise ActionFailed, ex.message
|
|
45
|
+
ensure
|
|
46
|
+
cleanup_sandbox
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# magic method name because we're subclassing ChefZero
|
|
52
|
+
def run_command
|
|
53
|
+
cmd = '/opt/chef/embedded/bin/chef-client -z'
|
|
54
|
+
cmd << ' -c /opt/kitchen/client.rb'
|
|
55
|
+
cmd << ' -j /opt/kitchen/dna.json'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def runner_container_name
|
|
59
|
+
"#{instance.name}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Dokken
|
|
2
|
+
module Transport
|
|
3
|
+
module Helpers
|
|
4
|
+
def insecure_ssh_private_key
|
|
5
|
+
<<-EOF
|
|
6
|
+
-----BEGIN DSA PRIVATE KEY-----
|
|
7
|
+
MIIBuwIBAAKBgQDZsPJal55xqENC7rFTa/+9epkIRIMW/EWEG4Phz9MP/111WZIt
|
|
8
|
+
okgu/y4UBHWIFGWgWCSqQTUNxUiL8nzOUZLInBBddI7/0EPoLECh3/48lky0oomk
|
|
9
|
+
iYsIQ65DVveFf09qXM15Sto+VIshigOlA5knokulXvevZ6iyS2++8VjpVwIVAKxq
|
|
10
|
+
r0+9oVr5JdR9ZhX0msOyQ+3LAoGAZk7ickwKncvsT4iOwBSPgQrlqY63MwbP2sN0
|
|
11
|
+
eQKzWkX9/zwVLpqQFAnjyelD3MOb1O+AJaAmL3FJ8FPCG2fzo+gOTRk6Oh7yGoz9
|
|
12
|
+
mPBNe3I7DE180z27k9UqpK5VXLELSaR1P/lgRqFCXloOuvwo1KfzcdmCvukCc6AC
|
|
13
|
+
wS8iVvgCgYEAyi7XX3Ev12AXgpwRPPbfVltJ9H5Hpll34gc2ORhmCSL6RE42BpAX
|
|
14
|
+
uzI7lbGun2dXFsCdDm0DQz3h4JHtTHePd6xXqyPpUda4ktLVtEWMm0XIQNE8P5zP
|
|
15
|
+
0gcfqVe4prOYeBLwrvAkyeNY5wosgzGHrQ+/hFwW3s8liEjZaFDhCWYCFASgG6eP
|
|
16
|
+
vVnsIrCx2rI5/KEQZ+oG
|
|
17
|
+
-----END DSA PRIVATE KEY-----
|
|
18
|
+
EOF
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Author:: Sean OMeara (<sean@chef.io>)
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2015, Sean OMeara
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
|
|
18
|
+
require 'kitchen'
|
|
19
|
+
require 'tmpdir'
|
|
20
|
+
require 'digest/sha1'
|
|
21
|
+
require_relative 'dokken/helpers'
|
|
22
|
+
|
|
23
|
+
include Dokken::Transport::Helpers
|
|
24
|
+
|
|
25
|
+
module Kitchen
|
|
26
|
+
module Transport
|
|
27
|
+
# Wrapped exception for any internally raised errors.
|
|
28
|
+
#
|
|
29
|
+
# @author Sean OMeara <sean@chef.io>
|
|
30
|
+
class DockerExecFailed < TransportFailed; end
|
|
31
|
+
|
|
32
|
+
# A Transport which uses Docker tricks to execute commands and
|
|
33
|
+
# transfer files.
|
|
34
|
+
#
|
|
35
|
+
# @author Sean OMeara <sean@chef.io>
|
|
36
|
+
class Dokken < Kitchen::Transport::Base
|
|
37
|
+
kitchen_transport_api_version 1
|
|
38
|
+
|
|
39
|
+
plugin_version Kitchen::VERSION
|
|
40
|
+
|
|
41
|
+
default_config :read_timeout, 3600
|
|
42
|
+
default_config :write_timeout, 3600
|
|
43
|
+
|
|
44
|
+
# (see Base#connection)
|
|
45
|
+
def connection(state, &block)
|
|
46
|
+
options = connection_options(config.to_hash.merge(state))
|
|
47
|
+
|
|
48
|
+
if @connection && @connection_options == options
|
|
49
|
+
reuse_connection(&block)
|
|
50
|
+
else
|
|
51
|
+
create_new_connection(options, &block)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# @author Sean OMeara <sean@chef.io>
|
|
56
|
+
class Connection < Kitchen::Transport::Dokken::Connection
|
|
57
|
+
def docker_connection
|
|
58
|
+
@docker_connection ||= Docker::Connection.new(Docker.url, options[:docker_host])
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def execute(command)
|
|
62
|
+
return if command.nil?
|
|
63
|
+
|
|
64
|
+
runner = Docker::Container.get(instance_name, {}, docker_connection)
|
|
65
|
+
o = runner.exec(Shellwords.shellwords(command)) { |stream, chunk| puts "#{stream}: #{chunk}" }
|
|
66
|
+
exit_code = o[2]
|
|
67
|
+
|
|
68
|
+
if exit_code != 0
|
|
69
|
+
fail Transport::DockerExecFailed,
|
|
70
|
+
"Docker Exec (#{exit_code}) for command: [#{command}]"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
old_image = Docker::Image.get(work_image, {}, docker_connection)
|
|
75
|
+
old_image.remove
|
|
76
|
+
rescue
|
|
77
|
+
debug "#{work_image} not present. nothing to remove."
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
new_image = runner.commit
|
|
81
|
+
new_image.tag('repo' => work_image, 'tag' => 'latest', 'force' => 'true')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def upload(locals, remote)
|
|
85
|
+
ip = ENV['DOCKER_HOST'].split('tcp://')[1].split(':')[0]
|
|
86
|
+
# require 'pry' ; binding.pry
|
|
87
|
+
port = options[:data_container][:NetworkSettings][:Ports][:"22/tcp"][0][:HostPort]
|
|
88
|
+
|
|
89
|
+
tmpdir = Dir.tmpdir
|
|
90
|
+
FileUtils.mkdir_p "#{tmpdir}/dokken"
|
|
91
|
+
File.write("#{tmpdir}/dokken/id_rsa", insecure_ssh_private_key)
|
|
92
|
+
FileUtils.chmod(0600, "#{tmpdir}/dokken/id_rsa")
|
|
93
|
+
|
|
94
|
+
rsync_cmd = '/usr/bin/rsync -a -e'
|
|
95
|
+
rsync_cmd << ' \''
|
|
96
|
+
rsync_cmd << 'ssh -2'
|
|
97
|
+
rsync_cmd << " -i #{tmpdir}/dokken/id_rsa"
|
|
98
|
+
rsync_cmd << ' -o CheckHostIP=no'
|
|
99
|
+
rsync_cmd << ' -o Compression=no'
|
|
100
|
+
rsync_cmd << ' -o PasswordAuthentication=no'
|
|
101
|
+
rsync_cmd << ' -o StrictHostKeyChecking=no'
|
|
102
|
+
rsync_cmd << ' -o UserKnownHostsFile=/dev/null'
|
|
103
|
+
rsync_cmd << ' -o LogLevel=ERROR'
|
|
104
|
+
rsync_cmd << " -p #{port}"
|
|
105
|
+
rsync_cmd << '\''
|
|
106
|
+
rsync_cmd << " #{locals.join(' ')} root@#{ip}:#{remote}"
|
|
107
|
+
system(rsync_cmd)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def login_command
|
|
111
|
+
runner = "#{options[:instance_name]}"
|
|
112
|
+
args = ['exec', '-it', runner, '/bin/bash', '-login', '-i']
|
|
113
|
+
LoginCommand.new('docker', args)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def instance_name
|
|
119
|
+
options[:instance_name]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def work_image
|
|
123
|
+
return "#{image_prefix}/#{instance_name}" unless image_prefix.nil?
|
|
124
|
+
instance_name
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def image_prefix
|
|
128
|
+
'someara'
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
# Builds the hash of options needed by the Connection object on
|
|
135
|
+
# construction.
|
|
136
|
+
#
|
|
137
|
+
# @param data [Hash] merged configuration and mutable state data
|
|
138
|
+
# @return [Hash] hash of connection options
|
|
139
|
+
# @api private
|
|
140
|
+
def connection_options(data) # rubocop:disable Metrics/MethodLength
|
|
141
|
+
opts = {}
|
|
142
|
+
opts[:docker_host] = Docker.options
|
|
143
|
+
opts[:data_container] = data[:data_container]
|
|
144
|
+
opts[:instance_name] = data[:instance_name]
|
|
145
|
+
opts
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Creates a new Dokken Connection instance and save it for potential future
|
|
149
|
+
# reuse.
|
|
150
|
+
#
|
|
151
|
+
# @param options [Hash] conneciton options
|
|
152
|
+
# @return [Ssh::Connection] an SSH Connection instance
|
|
153
|
+
# @api private
|
|
154
|
+
def create_new_connection(options, &block)
|
|
155
|
+
if @connection
|
|
156
|
+
logger.debug("[Dokken] shutting previous connection #{@connection}")
|
|
157
|
+
@connection.close
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
@connection_options = options
|
|
161
|
+
@connection = Kitchen::Transport::Dokken::Connection.new(options, &block)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Return the last saved Dokken connection instance.
|
|
165
|
+
#
|
|
166
|
+
# @return [Dokken::Connection] an Dokken Connection instance
|
|
167
|
+
# @api private
|
|
168
|
+
def reuse_connection
|
|
169
|
+
logger.debug("[Dokken] reusing existing connection #{@connection}")
|
|
170
|
+
yield @connection if block_given?
|
|
171
|
+
@connection
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: kitchen-dokken
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Sean OMeara
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-11-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: test-kitchen
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.4'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.4'
|
|
27
|
+
description: A Test Kitchen Driver for Dokken
|
|
28
|
+
email:
|
|
29
|
+
- sean@chef.io
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- ".cane"
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- ".tailor"
|
|
37
|
+
- ".travis.yml"
|
|
38
|
+
- CHANGELOG.md
|
|
39
|
+
- Gemfile
|
|
40
|
+
- LICENSE
|
|
41
|
+
- README.md
|
|
42
|
+
- kitchen-dokken.gemspec
|
|
43
|
+
- lib/kitchen/driver/dokken.rb
|
|
44
|
+
- lib/kitchen/driver/dokken/helpers.rb
|
|
45
|
+
- lib/kitchen/driver/dokken_version.rb
|
|
46
|
+
- lib/kitchen/provisioner/dokken.rb
|
|
47
|
+
- lib/kitchen/transport/dokken.rb
|
|
48
|
+
- lib/kitchen/transport/dokken/helpers.rb
|
|
49
|
+
homepage: https://github.com/someara/kitchen-dokken
|
|
50
|
+
licenses:
|
|
51
|
+
- Apache 2.0
|
|
52
|
+
metadata: {}
|
|
53
|
+
post_install_message:
|
|
54
|
+
rdoc_options: []
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - ">="
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '0'
|
|
67
|
+
requirements: []
|
|
68
|
+
rubyforge_project:
|
|
69
|
+
rubygems_version: 2.4.8
|
|
70
|
+
signing_key:
|
|
71
|
+
specification_version: 4
|
|
72
|
+
summary: A Test Kitchen Driver that talks to the Docker Remote API and uses Volumes
|
|
73
|
+
to produce sparse container images
|
|
74
|
+
test_files: []
|
|
75
|
+
has_rdoc:
|