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 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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.tailor ADDED
@@ -0,0 +1,4 @@
1
+ Tailor.config do |config|
2
+ config.formatters "text"
3
+ config.file_set 'lib/**/*.rb'
4
+ end
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 1.9.3
6
+ - 1.9.2
7
+ - ruby-head
8
+
9
+ matrix:
10
+ allow_failures:
11
+ - rvm: ruby-head
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ## 0.1.0 / Unreleased
2
+
3
+ * Initial release
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'docker-api'
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,2 @@
1
+ nothing to see here move along
2
+ ===================================
@@ -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: