kitchen-dokken 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: