chef-provisioning-docker 1.0.0.beta.1 → 1.0.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -7
- data/chef-provisioning-docker.gemspec +1 -1
- data/lib/chef/provisioning/docker_driver/chef_zero_http_proxy.rb +95 -0
- data/lib/chef/provisioning/docker_driver/docker_container_machine.rb +199 -13
- data/lib/chef/provisioning/docker_driver/docker_run_options.rb +591 -0
- data/lib/chef/provisioning/docker_driver/docker_transport.rb +41 -12
- data/lib/chef/provisioning/docker_driver/driver.rb +76 -167
- data/lib/chef/provisioning/docker_driver/version.rb +1 -1
- metadata +12 -5
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'chef/provisioning/transport'
|
2
2
|
require 'chef/provisioning/transport/ssh'
|
3
|
+
require 'chef/provisioning/docker_driver/chef_zero_http_proxy'
|
3
4
|
require 'docker'
|
4
5
|
require 'archive/tar/minitar'
|
5
6
|
require 'shellwords'
|
@@ -21,11 +22,12 @@ module DockerDriver
|
|
21
22
|
attr_reader :config
|
22
23
|
attr_accessor :container
|
23
24
|
|
24
|
-
def execute(command, options
|
25
|
+
def execute(command, timeout: nil, keep_stdin_open: nil, tty: nil, detached: nil, **options)
|
25
26
|
opts = {}
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
opts[:tty] = tty unless tty.nil?
|
28
|
+
opts[:detached] = detached unless detached.nil?
|
29
|
+
opts[:stdin] = keep_stdin_open unless keep_stdin_open.nil?
|
30
|
+
opts[:wait] = timeout unless timeout.nil?
|
29
31
|
|
30
32
|
command = Shellwords.split(command) if command.is_a?(String)
|
31
33
|
Chef::Log.debug("execute #{command.inspect} on container #{container.id} with options #{opts}'")
|
@@ -115,8 +117,34 @@ module DockerDriver
|
|
115
117
|
Chef::Log.debug("Session loop completed normally")
|
116
118
|
end
|
117
119
|
else
|
118
|
-
# We are the host.
|
119
|
-
# will
|
120
|
+
# We are the host. Find the docker machine's gateway (us) and talk to that;
|
121
|
+
# and set up a little proxy that will forward the container's requests to
|
122
|
+
# chef-zero
|
123
|
+
result = execute('ip route list', :read_only => true)
|
124
|
+
|
125
|
+
Chef::Log.debug("IP route: #{result.stdout}")
|
126
|
+
|
127
|
+
if result.stdout =~ /default via (\S+)/
|
128
|
+
|
129
|
+
old_uri = uri.dup
|
130
|
+
|
131
|
+
uri.host = $1
|
132
|
+
|
133
|
+
if !@proxy_thread
|
134
|
+
# Listen to docker instances only, and forward to localhost
|
135
|
+
@proxy_thread = Thread.new do
|
136
|
+
begin
|
137
|
+
Chef::Log.debug("Starting proxy thread: #{old_uri.host}:#{old_uri.port} <--> #{uri.host}:#{uri.port}")
|
138
|
+
ChefZeroHttpProxy.new(uri.host, uri.port, old_uri.host, old_uri.port).run
|
139
|
+
rescue
|
140
|
+
Chef::Log.error("Proxy thread unable to start: #{$!}")
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
else
|
145
|
+
raise "Cannot forward port: ip route ls did not show default in expected format.\nSTDOUT: #{result.stdout}"
|
146
|
+
end
|
147
|
+
|
120
148
|
end
|
121
149
|
else
|
122
150
|
old_uri = uri.dup
|
@@ -140,8 +168,6 @@ module DockerDriver
|
|
140
168
|
def available?
|
141
169
|
end
|
142
170
|
|
143
|
-
private
|
144
|
-
|
145
171
|
def is_local_machine(host)
|
146
172
|
local_addrs = Socket.ip_address_list
|
147
173
|
host_addrs = Addrinfo.getaddrinfo(host, nil)
|
@@ -152,7 +178,7 @@ module DockerDriver
|
|
152
178
|
end
|
153
179
|
end
|
154
180
|
|
155
|
-
def docker_toolkit_transport
|
181
|
+
def docker_toolkit_transport(connection_url=nil)
|
156
182
|
if !defined?(@docker_toolkit_transport)
|
157
183
|
# Figure out which docker-machine this container is in
|
158
184
|
begin
|
@@ -162,18 +188,21 @@ module DockerDriver
|
|
162
188
|
@docker_toolkit_transport = nil
|
163
189
|
return
|
164
190
|
end
|
191
|
+
|
192
|
+
connection_url ||= container.connection.url
|
193
|
+
|
165
194
|
Chef::Log.debug("Found docker machines:")
|
166
195
|
docker_machine = nil
|
167
196
|
docker_machines.lines.each do |line|
|
168
197
|
machine_name, machine_url = line.chomp.split(',', 2)
|
169
198
|
Chef::Log.debug("- #{machine_name} at URL #{machine_url.inspect}")
|
170
|
-
if machine_url ==
|
171
|
-
Chef::Log.debug("Docker machine #{machine_name} at URL #{machine_url} matches the container's URL #{
|
199
|
+
if machine_url == connection_url
|
200
|
+
Chef::Log.debug("Docker machine #{machine_name} at URL #{machine_url} matches the container's URL #{connection_url}! Will use it for port forwarding.")
|
172
201
|
docker_machine = machine_name
|
173
202
|
end
|
174
203
|
end
|
175
204
|
if !docker_machine
|
176
|
-
Chef::Log.debug("Docker Toolkit is installed, but no Docker machine's URL matches #{
|
205
|
+
Chef::Log.debug("Docker Toolkit is installed, but no Docker machine's URL matches #{connection_url.inspect}. Assuming docker must be installed as well ...")
|
177
206
|
@docker_toolkit_transport = nil
|
178
207
|
return
|
179
208
|
end
|
@@ -28,6 +28,10 @@ module DockerDriver
|
|
28
28
|
Driver.new(driver_url, config)
|
29
29
|
end
|
30
30
|
|
31
|
+
def driver_url
|
32
|
+
"docker:#{Docker.url}"
|
33
|
+
end
|
34
|
+
|
31
35
|
def initialize(driver_url, config)
|
32
36
|
super
|
33
37
|
url = Driver.connection_url(driver_url)
|
@@ -37,10 +41,14 @@ module DockerDriver
|
|
37
41
|
# to be set for command-line utilities
|
38
42
|
ENV['DOCKER_HOST'] = url
|
39
43
|
Chef::Log.debug("Setting Docker URL to #{url}")
|
40
|
-
Docker.url = url
|
41
44
|
end
|
42
45
|
|
43
|
-
|
46
|
+
ENV['DOCKER_HOST'] ||= url if url
|
47
|
+
Docker.logger = Chef::Log
|
48
|
+
options = Docker.options.dup || {}
|
49
|
+
options.merge!(read_timeout: 600)
|
50
|
+
options.merge!(config.hash_dup) if config
|
51
|
+
@connection = Docker::Connection.new(url || Docker.url, options)
|
44
52
|
end
|
45
53
|
|
46
54
|
def self.canonicalize_url(driver_url, config)
|
@@ -75,6 +83,8 @@ module DockerDriver
|
|
75
83
|
action_handler,
|
76
84
|
machine_spec
|
77
85
|
)
|
86
|
+
|
87
|
+
# Grab options from existing machine (TODO seems wrong) and set the machine_spec to that
|
78
88
|
docker_options = machine_options[:docker_options]
|
79
89
|
container_id = nil
|
80
90
|
image_id = machine_options[:image_id]
|
@@ -84,7 +94,6 @@ module DockerDriver
|
|
84
94
|
image_id ||= machine_spec.reference['image_id']
|
85
95
|
docker_options ||= machine_spec.reference['docker_options']
|
86
96
|
end
|
87
|
-
|
88
97
|
container_name ||= machine_spec.name
|
89
98
|
machine_spec.reference = {
|
90
99
|
'driver_url' => driver_url,
|
@@ -96,125 +105,66 @@ module DockerDriver
|
|
96
105
|
'docker_options' => stringize_keys(docker_options),
|
97
106
|
'container_id' => container_id
|
98
107
|
}
|
99
|
-
build_container(machine_spec, docker_options)
|
100
108
|
end
|
101
109
|
|
102
110
|
def ready_machine(action_handler, machine_spec, machine_options)
|
103
|
-
start_machine(action_handler, machine_spec, machine_options)
|
104
111
|
machine_for(machine_spec, machine_options)
|
105
112
|
end
|
106
113
|
|
107
|
-
def
|
114
|
+
def start_machine(action_handler, machine_spec, machine_options)
|
108
115
|
container = container_for(machine_spec)
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
build_image(machine_spec, docker_options)
|
113
|
-
|
114
|
-
args = [
|
115
|
-
'docker',
|
116
|
-
'run',
|
117
|
-
'--name',
|
118
|
-
machine_spec.reference['container_name'],
|
119
|
-
'--detach'
|
120
|
-
]
|
121
|
-
|
122
|
-
if docker_options[:keep_stdin_open]
|
123
|
-
args << '-i'
|
124
|
-
end
|
125
|
-
|
126
|
-
# We create the initial container with --net host so it can access things
|
127
|
-
# while it converges. When the final container starts, it will have its
|
128
|
-
# normal network.
|
129
|
-
args << '--net'
|
130
|
-
args << 'host'
|
131
|
-
|
132
|
-
if docker_options[:env]
|
133
|
-
docker_options[:env].each do |key, value|
|
134
|
-
args << '-e'
|
135
|
-
args << "#{key}=#{value}"
|
116
|
+
if container && !container.info['State']['Running']
|
117
|
+
action_handler.perform_action "start container #{machine_spec.name}" do
|
118
|
+
container.start!
|
136
119
|
end
|
137
120
|
end
|
121
|
+
end
|
138
122
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
end
|
123
|
+
# Connect to machine without acquiring it
|
124
|
+
def connect_to_machine(machine_spec, machine_options)
|
125
|
+
Chef::Log.debug('Connect to machine')
|
126
|
+
machine_for(machine_spec, machine_options)
|
127
|
+
end
|
145
128
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
129
|
+
def destroy_machine(action_handler, machine_spec, machine_options)
|
130
|
+
container = container_for(machine_spec)
|
131
|
+
if container
|
132
|
+
image_id = container.info['Image']
|
133
|
+
action_handler.perform_action "stop and destroy container #{machine_spec.name}" do
|
134
|
+
container.stop
|
135
|
+
container.delete
|
150
136
|
end
|
151
137
|
end
|
138
|
+
end
|
152
139
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
140
|
+
def stop_machine(action_handler, machine_spec, machine_options)
|
141
|
+
container = container_for(machine_spec)
|
142
|
+
if container.info['State']['Running']
|
143
|
+
action_handler.perform_action "stop container #{machine_spec.name}" do
|
144
|
+
container.stop!
|
157
145
|
end
|
158
146
|
end
|
159
|
-
|
160
|
-
if docker_options[:dns_search]
|
161
|
-
args << '--dns-search'
|
162
|
-
args << "#{docker_options[:dns_search]}"
|
163
|
-
end
|
164
|
-
|
165
|
-
args << image.id
|
166
|
-
args += Shellwords.split("/bin/sh -c 'while true;do sleep 1000; done'")
|
167
|
-
|
168
|
-
cmdstr = Shellwords.join(args)
|
169
|
-
Chef::Log.debug("Executing #{cmdstr}")
|
170
|
-
|
171
|
-
cmd = Mixlib::ShellOut.new(cmdstr)
|
172
|
-
cmd.run_command
|
173
|
-
|
174
|
-
container = Docker::Container.get(machine_spec.reference['container_name'])
|
175
|
-
|
176
|
-
Chef::Log.debug("Container id: #{container.id}")
|
177
|
-
machine_spec.reference['container_id'] = container.id
|
178
|
-
container
|
179
147
|
end
|
180
148
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
source_repository = base_image[:repository]
|
185
|
-
source_tag = base_image[:tag]
|
186
|
-
|
187
|
-
target_tag = machine_spec.reference['container_name']
|
188
|
-
|
189
|
-
image = Docker::Image.create(
|
190
|
-
'fromImage' => source_name,
|
191
|
-
'repo' => source_repository,
|
192
|
-
'tag' => source_tag
|
193
|
-
)
|
194
|
-
|
195
|
-
Chef::Log.debug("Allocated #{image}")
|
196
|
-
image.tag('repo' => 'chef', 'tag' => target_tag)
|
197
|
-
Chef::Log.debug("Tagged image #{image}")
|
198
|
-
|
199
|
-
machine_spec.reference['image_id'] = image.id
|
200
|
-
image
|
201
|
-
end
|
149
|
+
#
|
150
|
+
# Images
|
151
|
+
#
|
202
152
|
|
203
153
|
def allocate_image(action_handler, image_spec, image_options, machine_spec, machine_options)
|
154
|
+
tag_container_image(action_handler, machine_spec, image_spec)
|
155
|
+
|
204
156
|
# Set machine options on the image to match our newly created image
|
205
157
|
image_spec.reference = {
|
206
158
|
'driver_url' => driver_url,
|
207
159
|
'driver_version' => Chef::Provisioning::DockerDriver::VERSION,
|
208
160
|
'allocated_at' => Time.now.to_i,
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
:tag => image_spec.name
|
214
|
-
},
|
215
|
-
:from_image => true
|
161
|
+
'docker_options' => {
|
162
|
+
'base_image' => {
|
163
|
+
'name' => image_spec.name
|
164
|
+
}
|
216
165
|
}
|
217
166
|
}
|
167
|
+
|
218
168
|
# Workaround for chef/chef-provisioning-docker#37
|
219
169
|
machine_spec.attrs[:keep_image] = true
|
220
170
|
end
|
@@ -225,66 +175,33 @@ module DockerDriver
|
|
225
175
|
|
226
176
|
# workaround for https://github.com/chef/chef-provisioning/issues/358.
|
227
177
|
def destroy_image(action_handler, image_spec, image_options, machine_options={})
|
228
|
-
image =
|
178
|
+
image = image_for(image_spec)
|
229
179
|
image.delete unless image.nil?
|
230
180
|
end
|
231
181
|
|
232
|
-
|
233
|
-
def connect_to_machine(machine_spec, machine_options)
|
234
|
-
Chef::Log.debug('Connect to machine')
|
235
|
-
machine_for(machine_spec, machine_options)
|
236
|
-
end
|
182
|
+
private
|
237
183
|
|
238
|
-
def
|
184
|
+
def tag_container_image(action_handler, machine_spec, image_spec)
|
239
185
|
container = container_for(machine_spec)
|
240
|
-
|
241
|
-
|
242
|
-
container.
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
image = find_image(machine_spec)
|
247
|
-
Chef::Log.debug("Destroying image: chef:#{image.id}")
|
248
|
-
image.delete
|
186
|
+
existing_image = image_for(image_spec)
|
187
|
+
unless existing_image && existing_image.id == container.info['Image']
|
188
|
+
image = Docker::Image.get(container.info['Image'], {}, @connection)
|
189
|
+
action_handler.perform_action "tag image #{container.info['Image']} as chef-images/#{image_spec.name}" do
|
190
|
+
image.tag('repo' => image_spec.name, 'force' => true)
|
191
|
+
end
|
249
192
|
end
|
250
193
|
end
|
251
194
|
|
252
|
-
def
|
253
|
-
|
254
|
-
return if container.nil?
|
255
|
-
|
256
|
-
container.stop if container.info['State']['Running']
|
195
|
+
def to_camel_case(name)
|
196
|
+
name.split('_').map { |x| x.capitalize }.join("")
|
257
197
|
end
|
258
198
|
|
259
|
-
def
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
rescue Docker::Error::NotFoundError
|
266
|
-
end
|
267
|
-
end
|
268
|
-
|
269
|
-
if image.nil?
|
270
|
-
image_name = "chef:#{machine_spec.reference['container_name']}"
|
271
|
-
if machine_spec.from_image
|
272
|
-
base_image = base_image_for(machine_spec)
|
273
|
-
image_name = "#{base_image[:repository]}:#{base_image[:tag]}"
|
274
|
-
end
|
275
|
-
|
276
|
-
image = Docker::Image.all.select {
|
277
|
-
|i| i.info['RepoTags'].include? image_name
|
278
|
-
}.first
|
279
|
-
|
280
|
-
if machine_spec.from_image && image.nil?
|
281
|
-
raise "Unable to locate machine_image for #{image_name}"
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
machine_spec.reference['image_id'] = image.id if image
|
286
|
-
|
287
|
-
image
|
199
|
+
def to_snake_case(name)
|
200
|
+
# ExposedPorts -> _exposed_ports
|
201
|
+
name = name.gsub(/[A-Z]/) { |x| "_#{x.downcase}" }
|
202
|
+
# _exposed_ports -> exposed_ports
|
203
|
+
name = name[1..-1] if name.start_with?('_')
|
204
|
+
name
|
288
205
|
end
|
289
206
|
|
290
207
|
def from_image_from_action_handler(action_handler, machine_spec)
|
@@ -298,22 +215,11 @@ module DockerDriver
|
|
298
215
|
end
|
299
216
|
end
|
300
217
|
|
301
|
-
def driver_url
|
302
|
-
"docker:#{Docker.url}"
|
303
|
-
end
|
304
|
-
|
305
|
-
def start_machine(action_handler, machine_spec, machine_options)
|
306
|
-
container = container_for(machine_spec)
|
307
|
-
if container && !container.info['State']['Running']
|
308
|
-
container.start
|
309
|
-
end
|
310
|
-
end
|
311
|
-
|
312
218
|
def machine_for(machine_spec, machine_options)
|
313
219
|
Chef::Log.debug('machine_for...')
|
314
|
-
docker_options = machine_options[:docker_options] || Mash.from_hash(machine_spec.reference['docker_options'])
|
220
|
+
docker_options = machine_options[:docker_options] || Mash.from_hash(machine_spec.reference['docker_options'] || {})
|
315
221
|
|
316
|
-
container =
|
222
|
+
container = container_for(machine_spec)
|
317
223
|
|
318
224
|
if machine_spec.from_image
|
319
225
|
convergence_strategy = Chef::Provisioning::ConvergenceStrategy::NoConverge.new({}, config)
|
@@ -328,28 +234,31 @@ module DockerDriver
|
|
328
234
|
machine_spec,
|
329
235
|
transport,
|
330
236
|
convergence_strategy,
|
237
|
+
@connection,
|
331
238
|
docker_options[:command]
|
332
239
|
)
|
333
240
|
end
|
334
241
|
|
335
242
|
def container_for(machine_spec)
|
336
|
-
container_id = machine_spec.reference['container_id']
|
337
243
|
begin
|
338
|
-
|
244
|
+
Docker::Container.get(machine_spec.name, {}, @connection)
|
339
245
|
rescue Docker::Error::NotFoundError
|
340
246
|
end
|
341
247
|
end
|
342
248
|
|
343
|
-
def
|
344
|
-
|
345
|
-
|
346
|
-
|
249
|
+
def image_for(image_spec)
|
250
|
+
begin
|
251
|
+
Docker::Image.get(image_spec.name, {}, @connection)
|
252
|
+
rescue Docker::Error::NotFoundError
|
253
|
+
end
|
347
254
|
end
|
348
255
|
|
349
256
|
def stringize_keys(hash)
|
350
|
-
|
351
|
-
|
352
|
-
|
257
|
+
if hash
|
258
|
+
hash.each_with_object({}) do |(k,v),hash|
|
259
|
+
v = stringize_keys(v) if v.is_a?(Hash)
|
260
|
+
hash[k.to_s] = v
|
261
|
+
end
|
353
262
|
end
|
354
263
|
end
|
355
264
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chef-provisioning-docker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0.beta.
|
4
|
+
version: 1.0.0.beta.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Duffield
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef
|
@@ -44,14 +44,20 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.26'
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 1.26.2
|
48
51
|
type: :runtime
|
49
52
|
prerelease: false
|
50
53
|
version_requirements: !ruby/object:Gem::Requirement
|
51
54
|
requirements:
|
52
55
|
- - "~>"
|
53
56
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
57
|
+
version: '1.26'
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 1.26.2
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
62
|
name: minitar
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,7 +142,9 @@ files:
|
|
136
142
|
- Rakefile
|
137
143
|
- chef-provisioning-docker.gemspec
|
138
144
|
- lib/chef/provisioning/docker_driver.rb
|
145
|
+
- lib/chef/provisioning/docker_driver/chef_zero_http_proxy.rb
|
139
146
|
- lib/chef/provisioning/docker_driver/docker_container_machine.rb
|
147
|
+
- lib/chef/provisioning/docker_driver/docker_run_options.rb
|
140
148
|
- lib/chef/provisioning/docker_driver/docker_transport.rb
|
141
149
|
- lib/chef/provisioning/docker_driver/driver.rb
|
142
150
|
- lib/chef/provisioning/docker_driver/version.rb
|
@@ -168,4 +176,3 @@ signing_key:
|
|
168
176
|
specification_version: 4
|
169
177
|
summary: Provisioner for creating Docker containers in Chef Provisioning.
|
170
178
|
test_files: []
|
171
|
-
has_rdoc:
|