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

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: a042e9a753dc5de10f2238f555c255fa725816f52432b826c51aeb356ef0c929
4
- data.tar.gz: 0bee673f49e227e01f2a74b2cca37c9842779e65409bcd132864b1ff2392ea2d
2
+ SHA1:
3
+ metadata.gz: a24a00feb49b30d7a76c5f0d2c6441114b961264
4
+ data.tar.gz: 4c24b6bd38a87d0afd01127e9266610e5efe1624
5
5
  SHA512:
6
- metadata.gz: c303b979b6558672db6bf8d7056ecc80751f30d0fd1e61e3e590a10bd7c10eb4f02073343409a3501b94aca50a3f66c20fd51830526deb6edb97b6b636c23b99
7
- data.tar.gz: 6c9ec2a45a4ec5399d91e3cd378af31ba9753ab079756eae4f684f41d4d431dda94d4831a2f811956169b58bdff0dd1b6716845c9a79b7dc7e4cebeb67ee9c94
6
+ metadata.gz: c86add037f7fcb734e8884ab2116d2b13cc5eb9cbb2cd0c976e58f7e37313bde3ba82979aa950c6313c84d3ca5b048201d0b1ab7a067a378508d897773e7eeb2
7
+ data.tar.gz: ea5067b1773a9ec1eb218989de89d5023723a5fdfb3bdca40aa07d417f31e36f3ebe283847ca26d39f790c96eaac3b32b4b250633fe6bfe74f39562bb1cf172b
data/Gemfile CHANGED
@@ -1,2 +1,5 @@
1
1
  source "https://rubygems.org"
2
+ gemfile
2
3
  gemspec
4
+
5
+ gem 'chef-provisioning', :git => 'https://github.com/opscode/chef-provisioning.git', :branch => 'master'
data/README.md CHANGED
@@ -1,10 +1,8 @@
1
1
  # chef-provisioning-docker
2
2
 
3
- [![Build Status](https://travis-ci.org/chef/chef-provisioning-docker.svg?branch=master)](https://travis-ci.org/chef/chef-provisioning-docker) [![Gem Version](https://badge.fury.io/rb/chef-provisioning-docker.svg)](http://badge.fury.io/rb/chef-provisioning-docker)
4
-
5
3
  How to use:
6
4
 
7
- First you need to ensure that Docker is running. This can be done on a Linux host using Docker's installers or on OSX using boot2docker. Once you have that, you can install the dependencies with Bundler and then use the Docker like the following:
5
+ First you need to ensure that Docker is running. This can be done on a Linux host using Docker's installers or on OSX using boot2docker. Once you have that, you can install the dependencies with Bundler and then use the Docker like the following:
8
6
 
9
7
  ```
10
8
  CHEF_DRIVER=docker bundle exec chef-client -z docker_ubuntu_image.rb
@@ -18,66 +16,58 @@ Using this , you can then define a machine similar to the following example:
18
16
 
19
17
  ```ruby
20
18
  require 'chef/provisioning/docker_driver'
21
- with_driver 'docker'
22
19
 
23
20
  machine 'wario' do
24
- recipe 'openssh::default'
25
-
26
- machine_options(
27
- docker_options: {
28
- base_image: {
29
- name: 'ubuntu',
30
- repository: 'ubuntu',
31
- tag: '14.04'
32
- },
33
- :command => '/usr/sbin/sshd -p 8022 -D',
34
-
35
- #ENV (Environment Variables)
36
- #Set any env var in the container by using one or more -e flags, even overriding those already defined by the developer with a Dockerfile ENV
37
- :env => {
38
- "deep" => 'purple',
39
- "led" => 'zeppelin'
40
- },
41
-
42
- # Ports can be one of two forms:
43
- # src_port (string or integer) is a pass-through, i.e 8022 or "9933"
44
- # src:dst (string) is a map from src to dst, i.e "8022:8023" maps 8022 externally to 8023 in the container
45
-
46
- # Example (multiple):
47
- :ports => [8022, "8023:9000", "9500"],
48
-
49
- # Examples (single):
50
- :ports => 1234,
51
- :ports => "2345:6789",
52
-
53
- # Volumes can be one of three forms:
54
- # src_volume (string) is volume to add to container, i.e. creates new volume inside container at "/tmp"
55
- # src:dst (string) mounts host's directory src to container's dst, i.e "/tmp:/tmp1" mounts host's directory /tmp to container's /tmp1
56
- # src:dst:mode (string) mounts host's directory src to container's dst with the specified mount option, i.e "/:/rootfs:ro" mounts read-only host's root (/) folder to container's /rootfs
57
- # See more details on Docker volumes at https://github.com/docker/docker/blob/master/docs/sources/userguide/dockervolumes.md .
58
-
59
- # Example (single):
60
- :volumes => "/tmp",
61
-
62
- # Example (multiple):
63
- :volumes => ["/tmp:/tmp", "/:/rootfs:ro"],
64
-
65
- # if you need to keep stdin open (i.e docker run -i)
66
- # :keep_stdin_open => true
21
+ recipe 'openssh::default'
22
+
23
+ machine_options :docker_options => {
24
+ :base_image => {
25
+ :name => 'ubuntu',
26
+ :repository => 'ubuntu',
27
+ :tag => '14.04'
28
+ },
29
+ :command => '/usr/sbin/sshd -p 8022 -D',
67
30
 
31
+ #ENV (Environment Variables)
32
+ #Set any env var in the container by using one or more -e flags, even overriding those already defined by the developer with a Dockerfile ENV
33
+ :env => {
34
+ "deep" => 'purple',
35
+ "led" => 'zeppelin'
68
36
  },
69
- # optional, default timeout is 600
70
- docker_connection: {
71
- :read_timeout => 1000,
72
- }
73
- )
74
37
 
38
+ # Ports can be one of two forms:
39
+ # src_port (string or integer) is a pass-through, i.e 8022 or "9933"
40
+ # src:dst (string) is a map from src to dst, i.e "8022:8023" maps 8022 externally to 8023 in the container
41
+
42
+ # Example (multiple):
43
+ :ports => [8022, "8023:9000", "9500"],
44
+
45
+ # Examples (single):
46
+ :ports => 1234,
47
+ :ports => "2345:6789",
48
+
49
+ # Volumes can be one of three forms:
50
+ # src_volume (string) is volume to add to container, i.e. creates new volume inside container at "/tmp"
51
+ # src:dst (string) mounts host's directory src to container's dst, i.e "/tmp:/tmp1" mounts host's directory /tmp to container's /tmp1
52
+ # src:dst:mode (string) mounts host's directory src to container's dst with the specified mount option, i.e "/:/rootfs:ro" mounts read-only host's root (/) folder to container's /rootfs
53
+ # See more details on Docker volumes at https://github.com/docker/docker/blob/master/docs/sources/userguide/dockervolumes.md .
54
+
55
+ # Example (single):
56
+ :volumes => "/tmp",
57
+
58
+ # Example (multiple):
59
+ :volumes => ["/tmp:/tmp", "/:/rootfs:ro"],
60
+
61
+ # if you need to keep stdin open (i.e docker run -i)
62
+ # :keep_stdin_open => true
63
+
64
+ }
75
65
  end
76
66
  ```
77
67
 
78
68
  ## Machine images
79
69
 
80
- This supports the new machine image paradigm; with Docker you can build a base image, save that and use it to create a new container. Here is an example of this:
70
+ This supports the new machine image paradigm; with Docker you can build a base image, save that and use it to create a new container. Here is an example of this:
81
71
 
82
72
  ```ruby
83
73
  require 'chef/provisioning/docker_driver'
@@ -85,27 +75,26 @@ require 'chef/provisioning/docker_driver'
85
75
  machine_image 'ssh_server' do
86
76
  recipe 'openssh'
87
77
 
88
- machine_options(
89
- :docker_options => {
78
+ machine_options :docker_options => {
90
79
  :base_image => {
91
80
  :name => 'ubuntu',
92
81
  :repository => 'ubuntu',
93
82
  :tag => '14.04'
94
83
  }
95
- }
96
- )
84
+ }
97
85
  end
98
86
 
99
87
  machine 'ssh00' do
100
88
  from_image 'ssh_server'
101
89
 
102
- machine_options(
103
- :docker_options => {
90
+ machine_options :docker_options => {
104
91
  :command => '/usr/sbin/sshd -D -o UsePAM=no -o UsePrivilegeSeparation=no -o PidFile=/tmp/sshd.pid',
105
92
  :ports => [22]
106
- }
107
- )
93
+ }
108
94
  end
109
95
  ```
110
96
 
111
- This will create a docker container based on Ubuntu 14.04 and then execute the openssh recipe and run the /usr/sbin/sshd command as the container's run command.
97
+ This will create a docker container based on Ubuntu 14.04 and
98
+ then execute the Apache recipe and run the /usr/sbin/httpd command
99
+ as the container's run command.
100
+
@@ -9,12 +9,12 @@ Gem::Specification.new do |s|
9
9
  s.summary = 'Provisioner for creating Docker containers in Chef Provisioning.'
10
10
  s.description = s.summary
11
11
  s.author = 'Tom Duffield'
12
- s.email = 'tom@chef.io'
13
- s.homepage = 'https://github.com/chef/chef-provisioning-docker'
12
+ s.email = 'tom@getchef.com'
13
+ s.homepage = 'https://github.com/opscode/chef-provisioning-docker'
14
14
 
15
15
  s.add_dependency 'chef'
16
- s.add_dependency 'chef-provisioning', '>= 2.0', '< 3.0'
17
- s.add_dependency 'docker-api', '~> 1.26', '>= 1.26.2'
16
+ s.add_dependency 'chef-provisioning', '~> 1.0'
17
+ s.add_dependency 'docker-api', '~> 1.25'
18
18
  s.add_dependency 'minitar'
19
19
  s.add_dependency 'sys-proctable'
20
20
 
@@ -1,5 +1,4 @@
1
1
  require 'chef/provisioning/machine/unix_machine'
2
- require 'chef/provisioning/docker_driver/docker_run_options'
3
2
 
4
3
  class Chef
5
4
  module Provisioning
@@ -10,214 +9,29 @@ module DockerDriver
10
9
  # Options is expected to contain the optional keys
11
10
  # :command => the final command to execute
12
11
  # :ports => a list of port numbers to listen on
13
- def initialize(machine_spec, transport, convergence_strategy, connection, command = nil)
12
+ def initialize(machine_spec, transport, convergence_strategy, command = nil)
14
13
  super(machine_spec, transport, convergence_strategy)
15
14
  @command = command
16
15
  @transport = transport
17
- @connection = connection
18
- end
19
-
20
- def setup_convergence(action_handler)
21
- # Build a converge container to converge in
22
- transport.container = build_converge_container(action_handler)
23
- unless transport.container.info['State']['Running']
24
- action_handler.perform_action "start converge container chef-converge.#{machine_spec.name}" do
25
- transport.container.start!
26
- end
27
- end
28
- super(action_handler)
29
- # Commit after convergence setup (such as the install of Chef)
30
- # to break up the cost of the commit and avoid read timeouts
31
- transport.container.commit
32
16
  end
33
17
 
34
18
  def converge(action_handler)
35
- # First, grab and start the converge container if it's there ...
36
- transport.container = converge_container_for(machine_spec)
37
- if !transport.container
38
- raise "No converge container found! Did you run `:converge` without first running `:setup`?"
39
- end
40
- unless transport.container.info['State']['Running']
41
- action_handler.perform_action "start converge container chef-converge.#{machine_spec.name}" do
42
- transport.container.start!
43
- end
44
- end
45
-
46
- # Then, converge ...
47
- super(action_handler)
48
-
49
- # Save the converged image ...
50
- converged_image = commit_converged_image(action_handler, machine_spec, transport.container)
51
-
52
- # Build the new container
53
- transport.container = create_container(action_handler, machine_spec, converged_image)
54
-
55
- # Finally, start it!
56
- action_handler.perform_action "start container #{machine_spec.name}" do
57
- transport.container.start!
58
- end
59
- end
60
-
61
- private
62
-
63
- def container_config(action_handler, machine_spec)
64
- docker_options = machine_spec.reference['docker_options'] || {}
65
-
66
- # We're going to delete things to make it easier on ourselves, back it up
67
- docker_options = docker_options.dup
68
-
69
- # Bring in from_image
70
- if machine_spec.from_image
71
- docker_options['base_image'] ||= {}
72
- docker_options['base_image']['name'] = machine_spec.from_image
73
- end
74
-
75
- # Respect :container_config
76
- config = stringize_keys(docker_options.delete('container_config') || {})
77
-
78
- # Respect :base_image
79
- image = base_image(action_handler, docker_options.delete('base_image'))
80
- config['Image'] = image if image
81
-
82
- # Respect everything else
83
- DockerRunOptions.include_command_line_options_in_container_config(config, docker_options)
84
- end
85
-
86
- # Get the converge container for this machine
87
- def converge_container_for(machine_spec)
88
- begin
89
- Docker::Container.get("chef-converge.#{machine_spec.name}", {}, @connection)
90
- rescue Docker::Error::NotFoundError
91
- end
92
- end
93
-
94
- def container_for(machine_spec)
95
- begin
96
- Docker::Container.get(machine_spec.name, {}, @connection)
97
- rescue Docker::Error::NotFoundError
98
- end
99
- end
100
-
101
- # Builds a container that has the same properties as the final container,
102
- # but with a couple of tweaks to allow processes to run and converge the
103
- # container.
104
- def build_converge_container(action_handler)
105
- # If a converge container already exists, do nothing. TODO check if it's different!!!
106
- converge_container = converge_container_for(machine_spec)
107
- if converge_container
108
- return converge_container
109
- end
110
-
111
- # Create a chef-capable container (just like the final one, but with --net=host
112
- # and a command that keeps it open). Base it on the image.
113
- config = container_config(action_handler, machine_spec)
114
- config.merge!(
115
- 'name' => "chef-converge.#{machine_spec.reference['container_name']}",
116
- 'Cmd' => [ "/bin/sh", "-c", "while true;do sleep 1000; done" ],
19
+ super action_handler
20
+ Chef::Log.debug("DockerContainerMachine converge complete, executing #{@command} in #{@container_name}")
21
+ image = transport.container.commit(
22
+ 'repo' => 'chef',
23
+ 'tag' => machine_spec.reference['container_name']
117
24
  )
118
- # If we're using Docker Toolkit, we need to use host networking for the converge
119
- # so we can open up the port we need. Don't force it in other cases, though.
120
- if transport.is_local_machine(URI(transport.config[:chef_server_url]).host) &&
121
- transport.docker_toolkit_transport(@connection.url)
122
- config['HostConfig'] ||= {}
123
- config['HostConfig'].merge!('NetworkMode' => 'host')
124
- # These are incompatible with NetworkMode: host
125
- config['HostConfig'].delete('Links')
126
- config['HostConfig'].delete('ExtraHosts')
127
- config.delete('NetworkSettings')
128
- end
129
- # Don't use any resources that need to be shared (such as exposed ports)
130
- config.delete('ExposedPorts')
131
-
132
- Chef::Log.debug("Creating converge container with config #{config} ...")
133
- action_handler.perform_action "create container to converge #{machine_spec.name}" do
134
- # create deletes the name :(
135
- Docker::Container.create(config.dup, @connection)
136
- converge_container = Docker::Container.get(config['name'], {}, @connection)
137
- Chef::Log.debug("Created converge container #{converge_container.id}")
138
- end
139
- converge_container
140
- end
141
-
142
- # Commit the converged container to an image. Called by converge.
143
- def commit_converged_image(action_handler, machine_spec, converge_container)
144
- # Commit the converged container to an image
145
- converged_image = nil
146
- action_handler.perform_action "commit and delete converged container for #{machine_spec.name}" do
147
- converged_image = converge_container.commit
148
- converge_container.stop!
149
- converge_container.delete
150
- end
151
- converged_image
152
- end
153
-
154
- # Create the final container from the converged image
155
- def create_container(action_handler, machine_spec, converged_image)
156
- # Check if the container already exists.
157
- container = container_for(machine_spec)
158
- if container
159
- # If it's the same image, just return; don't stop and start.
160
- if container.info['Image'] == converged_image.id
161
- return container
162
- else
163
- # If the container exists but is based on an old image, destroy it.
164
- action_handler.perform_action "stop and delete running container for #{machine_spec.name}" do
165
- container.stop!
166
- container.delete
167
- end
168
- end
169
- end
25
+ machine_spec.reference['image_id'] = image.id
170
26
 
171
- # Create the new container
172
- config = container_config(action_handler, machine_spec)
173
- config.merge!(
174
- 'name' => machine_spec.reference['container_name'],
175
- 'Image' => converged_image.id
176
- )
177
- action_handler.perform_action "create final container for #{machine_spec.name}" do
178
- container = Docker::Container.create(config, @connection)
27
+ if @command && transport.container.info['Config']['Cmd'].join(' ') != @command
28
+ transport.container.delete(:force => true)
29
+ container = image.run(Shellwords.split(@command))
30
+ container.rename(machine_spec.reference['container_name'])
179
31
  machine_spec.reference['container_id'] = container.id
180
- machine_spec.save(action_handler)
181
- end
182
- container
183
- end
184
-
185
- def stringize_keys(hash)
186
- hash.each_with_object({}) do |(k,v),hash|
187
- v = stringize_keys(v) if v.is_a?(Hash)
188
- hash[k.to_s] = v
189
- end
190
- end
191
-
192
- def base_image(action_handler, base_image_value)
193
- case base_image_value
194
- when Hash
195
- params = base_image_value.dup
196
- if !params['fromImage']
197
- params['fromImage'] = params.delete('name')
198
- params['fromImage'] = "#{params['fromImage']}:#{params.delete('tag')}" if params['tag']
199
- end
200
- when String
201
- params = { 'fromImage' => base_image_value }
202
- when nil
203
- return nil
204
- else
205
- raise "Unexpected type #{base_image_value.class} for docker_options[:base_image]!"
206
- end
207
-
208
- image_name = params['fromImage']
209
- repo, image_name = params['fromImage'].split('/', 2) if params['fromImage'].include?('/')
210
-
211
- begin
212
- image = Docker::Image.get(image_name, {}, @connection)
213
- rescue Docker::Error::NotFoundError
214
- # If it's not found, pull it.
215
- action_handler.perform_action "pull #{params}" do
216
- image = Docker::Image.create(params, @connection)
217
- end
32
+ transport.container = container
218
33
  end
219
-
220
- image.id
34
+ machine_spec.save(action_handler)
221
35
  end
222
36
  end
223
37
  end
@@ -1,6 +1,5 @@
1
1
  require 'chef/provisioning/transport'
2
2
  require 'chef/provisioning/transport/ssh'
3
- require 'chef/provisioning/docker_driver/chef_zero_http_proxy'
4
3
  require 'docker'
5
4
  require 'archive/tar/minitar'
6
5
  require 'shellwords'
@@ -22,12 +21,11 @@ module DockerDriver
22
21
  attr_reader :config
23
22
  attr_accessor :container
24
23
 
25
- def execute(command, timeout: nil, keep_stdin_open: nil, tty: nil, detached: nil, **options)
24
+ def execute(command, options={})
26
25
  opts = {}
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?
26
+ if options[:keep_stdin_open]
27
+ opts[:stdin] = true
28
+ end
31
29
 
32
30
  command = Shellwords.split(command) if command.is_a?(String)
33
31
  Chef::Log.debug("execute #{command.inspect} on container #{container.id} with options #{opts}'")
@@ -81,7 +79,7 @@ module DockerDriver
81
79
  end
82
80
 
83
81
  def download_file(path, local_path)
84
- file = File.open(local_path, 'wb')
82
+ file = File.open(local_path, 'w')
85
83
  begin
86
84
  file.write(read_file(path))
87
85
  file.close
@@ -91,7 +89,7 @@ module DockerDriver
91
89
  end
92
90
 
93
91
  def upload_file(local_path, path)
94
- write_file(path, IO.read(local_path, mode: "rb"))
92
+ write_file(path, IO.read(local_path))
95
93
  end
96
94
 
97
95
  def make_url_available_to_remote(local_url)
@@ -117,35 +115,16 @@ module DockerDriver
117
115
  Chef::Log.debug("Session loop completed normally")
118
116
  end
119
117
  else
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 = ENV["PROXY_HOST_OVERRIDE"] ? ENV["PROXY_HOST_OVERRIDE"] : $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
-
118
+ # We are the host. The docker machine was run with --net=host, so it
119
+ # will be able to talk to us automatically.
148
120
  end
121
+ else
122
+ old_uri = uri.dup
123
+ # Find out our external network address of the URL and report it
124
+ # to the container in case it has no DNS (often the case).
125
+ uri.scheme = 'http' if 'chefzero' == uri.scheme && uri.host == 'localhost'
126
+ uri.host = Socket.getaddrinfo(uri.host, uri.scheme, nil, :STREAM)[0][3]
127
+ Chef::Log.debug("Looked up IP address of #{old_uri} and modified URL to point at it: #{uri}")
149
128
  end
150
129
 
151
130
  uri.to_s
@@ -161,6 +140,8 @@ module DockerDriver
161
140
  def available?
162
141
  end
163
142
 
143
+ private
144
+
164
145
  def is_local_machine(host)
165
146
  local_addrs = Socket.ip_address_list
166
147
  host_addrs = Addrinfo.getaddrinfo(host, nil)
@@ -171,7 +152,7 @@ module DockerDriver
171
152
  end
172
153
  end
173
154
 
174
- def docker_toolkit_transport(connection_url=nil)
155
+ def docker_toolkit_transport
175
156
  if !defined?(@docker_toolkit_transport)
176
157
  # Figure out which docker-machine this container is in
177
158
  begin
@@ -181,21 +162,18 @@ module DockerDriver
181
162
  @docker_toolkit_transport = nil
182
163
  return
183
164
  end
184
-
185
- connection_url ||= container.connection.url
186
-
187
165
  Chef::Log.debug("Found docker machines:")
188
166
  docker_machine = nil
189
167
  docker_machines.lines.each do |line|
190
168
  machine_name, machine_url = line.chomp.split(',', 2)
191
169
  Chef::Log.debug("- #{machine_name} at URL #{machine_url.inspect}")
192
- if machine_url == connection_url
193
- Chef::Log.debug("Docker machine #{machine_name} at URL #{machine_url} matches the container's URL #{connection_url}! Will use it for port forwarding.")
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.")
194
172
  docker_machine = machine_name
195
173
  end
196
174
  end
197
175
  if !docker_machine
198
- Chef::Log.debug("Docker Toolkit is installed, but no Docker machine's URL matches #{connection_url.inspect}. Assuming docker must be installed as well ...")
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 ...")
199
177
  @docker_toolkit_transport = nil
200
178
  return
201
179
  end