chef-provisioning-docker 1.0.0.beta.1 → 1.0.0.beta.2

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.
@@ -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
- if options[:keep_stdin_open]
27
- opts[:stdin] = true
28
- end
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. The docker machine was run with --net=host, so it
119
- # will be able to talk to us automatically.
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 == container.connection.url
171
- Chef::Log.debug("Docker machine #{machine_name} at URL #{machine_url} matches the container's URL #{container.connection.url}! Will use it for port forwarding.")
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 #{container.connection.url.inspect}. Assuming docker must be installed as well ...")
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
- @connection = Docker.connection
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 build_container(machine_spec, docker_options)
114
+ def start_machine(action_handler, machine_spec, machine_options)
108
115
  container = container_for(machine_spec)
109
- return container unless container.nil?
110
-
111
- image = find_image(machine_spec) ||
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
- if docker_options[:ports]
140
- docker_options[:ports].each do |portnum|
141
- args << '-p'
142
- args << "#{portnum}"
143
- end
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
- if docker_options[:volumes]
147
- docker_options[:volumes].each do |volume|
148
- args << '-v'
149
- args << "#{volume}"
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
- if docker_options[:dns]
154
- docker_options[:dns].each do |entry|
155
- args << '--dns'
156
- args << "#{entry}"
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
- def build_image(machine_spec, docker_options)
182
- base_image = docker_options[:base_image] || base_image_for(machine_spec)
183
- source_name = base_image[:name]
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
- :docker_options => {
210
- :base_image => {
211
- :name => "chef_#{image_spec.name}",
212
- :repository => 'chef',
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 = Docker::Image.get("chef:#{image_spec.name}")
178
+ image = image_for(image_spec)
229
179
  image.delete unless image.nil?
230
180
  end
231
181
 
232
- # Connect to machine without acquiring it
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 destroy_machine(action_handler, machine_spec, machine_options)
184
+ def tag_container_image(action_handler, machine_spec, image_spec)
239
185
  container = container_for(machine_spec)
240
- if container
241
- Chef::Log.debug("Destroying container: #{container.id}")
242
- container.delete(:force => true)
243
- end
244
-
245
- if !machine_spec.attrs[:keep_image] && !machine_options[:keep_image]
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 stop_machine(action_handler, machine_spec, machine_options)
253
- container = container_for(machine_spec)
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 find_image(machine_spec)
260
- image = nil
261
-
262
- if machine_spec.reference['image_id']
263
- begin
264
- image = Docker::Image.get(machine_spec.reference['image_id'])
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 = Docker::Container.get(machine_spec.reference['container_id'], @connection)
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
- container = Docker::Container.get(container_id, @connection) if container_id
244
+ Docker::Container.get(machine_spec.name, {}, @connection)
339
245
  rescue Docker::Error::NotFoundError
340
246
  end
341
247
  end
342
248
 
343
- def base_image_for(machine_spec)
344
- Chef::Log.debug("Looking for image #{machine_spec.from_image}")
345
- image_spec = machine_spec.managed_entry_store.get!(:machine_image, machine_spec.from_image)
346
- Mash.new(image_spec.reference)[:docker_options][:base_image]
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
- hash.each_with_object({}) do |(k,v),hash|
351
- v = stringize_keys(v) if v.is_a?(Hash)
352
- hash[k.to_s] = v
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
@@ -1,7 +1,7 @@
1
1
  class Chef
2
2
  module Provisioning
3
3
  module DockerDriver
4
- VERSION = '1.0.0.beta.1'
4
+ VERSION = '1.0.0.beta.2'
5
5
  end
6
6
  end
7
7
  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.1
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 00:00:00.000000000 Z
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.25'
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.25'
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: