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.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 08110106616725e446a740a4a3a309ab9afca476
|
4
|
+
data.tar.gz: d35e453ec12ac3b2a2fcb33999fcb4d3014d3fc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 664c9d9ff139e4f84425b2f5561053c4bf7da9fc610d280830575dcf768401a1febfc84a2c7fb4d3d3aebc1adbd3ca7d66a530a4b44488757ea49e009f13bb05
|
7
|
+
data.tar.gz: 81605d7138ce4f1b95a7725ec4748af023811c768bfd181676a94b9fd680b258ad01879292f0897a6fbce56d4a1f4e077f1d43d2ef0ee0318f0cd040fcf1132e
|
data/README.md
CHANGED
@@ -16,15 +16,16 @@ Using this , you can then define a machine similar to the following example:
|
|
16
16
|
|
17
17
|
```ruby
|
18
18
|
require 'chef/provisioning/docker_driver'
|
19
|
+
with_driver 'docker'
|
19
20
|
|
20
21
|
machine 'wario' do
|
21
|
-
|
22
|
+
recipe 'openssh::default'
|
22
23
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
machine_options docker_options: {
|
25
|
+
base_image: {
|
26
|
+
name: 'ubuntu',
|
27
|
+
repository: 'ubuntu',
|
28
|
+
tag: '14.04'
|
28
29
|
},
|
29
30
|
:command => '/usr/sbin/sshd -p 8022 -D',
|
30
31
|
|
@@ -97,4 +98,3 @@ end
|
|
97
98
|
This will create a docker container based on Ubuntu 14.04 and
|
98
99
|
then execute the Apache recipe and run the /usr/sbin/httpd command
|
99
100
|
as the container's run command.
|
100
|
-
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.add_dependency 'chef'
|
16
16
|
s.add_dependency 'chef-provisioning', '~> 1.0'
|
17
|
-
s.add_dependency 'docker-api', '~> 1.
|
17
|
+
s.add_dependency 'docker-api', '~> 1.26', '>= 1.26.2'
|
18
18
|
s.add_dependency 'minitar'
|
19
19
|
s.add_dependency 'sys-proctable'
|
20
20
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
class ChefZeroHttpProxy
|
5
|
+
|
6
|
+
def initialize(local_address, local_port, remote_address, remote_port)
|
7
|
+
@local_address = local_address
|
8
|
+
@local_port = local_port
|
9
|
+
@remote_address = remote_address
|
10
|
+
@remote_port = remote_port
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
begin
|
15
|
+
Chef::Log.debug("Running proxy main loop on #{@local_address}:#{@local_port}!")
|
16
|
+
|
17
|
+
# Start our server to handle connections (will raise things on errors)
|
18
|
+
@socket = TCPServer.new @local_address, @local_port
|
19
|
+
|
20
|
+
# Handle every request in another thread
|
21
|
+
loop do
|
22
|
+
s = @socket.accept
|
23
|
+
Thread.new s, &method(:handle_request)
|
24
|
+
end
|
25
|
+
|
26
|
+
ensure
|
27
|
+
@socket.close if @socket
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def handle_request(to_client)
|
32
|
+
begin
|
33
|
+
request_line = to_client.readline
|
34
|
+
|
35
|
+
verb = request_line[/^\w+/]
|
36
|
+
url = request_line[/^\w+\s+(\S+)/, 1]
|
37
|
+
version = request_line[/HTTP\/(1\.\d)\s*$/, 1]
|
38
|
+
uri = URI::parse url
|
39
|
+
|
40
|
+
# Show what got requested
|
41
|
+
Chef::Log.debug("[C->S]: #{verb} -> #{url}")
|
42
|
+
|
43
|
+
querystr = if uri.query
|
44
|
+
"#{uri.path}?#{uri.query}"
|
45
|
+
else
|
46
|
+
uri.path
|
47
|
+
end
|
48
|
+
|
49
|
+
to_server = TCPSocket.new(@remote_address, @remote_port)
|
50
|
+
|
51
|
+
to_server.write("#{verb} #{querystr} HTTP/#{version}\r\n")
|
52
|
+
|
53
|
+
content_len = 0
|
54
|
+
|
55
|
+
loop do
|
56
|
+
line = to_client.readline
|
57
|
+
|
58
|
+
if line =~ /^Content-Length:\s+(\d+)\s*$/
|
59
|
+
content_len = $1.to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
# Strip proxy headers
|
63
|
+
if line =~ /^proxy/i
|
64
|
+
next
|
65
|
+
elsif line.strip.empty?
|
66
|
+
to_server.write("Connection: close\r\n\r\n")
|
67
|
+
|
68
|
+
if content_len >= 0
|
69
|
+
to_server.write(to_client.read(content_len))
|
70
|
+
Chef::Log.debug("[C->S]: Wrote #{content_len} bytes")
|
71
|
+
end
|
72
|
+
|
73
|
+
break
|
74
|
+
else
|
75
|
+
to_server.write(line)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
buff = ''
|
80
|
+
while to_server.read(8192, buff)
|
81
|
+
to_client.write(buff)
|
82
|
+
end
|
83
|
+
|
84
|
+
rescue
|
85
|
+
Chef::Log.error $!
|
86
|
+
raise
|
87
|
+
|
88
|
+
ensure
|
89
|
+
# Close the sockets
|
90
|
+
to_client.close
|
91
|
+
to_server.close
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'chef/provisioning/machine/unix_machine'
|
2
|
+
require 'chef/provisioning/docker_driver/docker_run_options'
|
2
3
|
|
3
4
|
class Chef
|
4
5
|
module Provisioning
|
@@ -9,29 +10,214 @@ module DockerDriver
|
|
9
10
|
# Options is expected to contain the optional keys
|
10
11
|
# :command => the final command to execute
|
11
12
|
# :ports => a list of port numbers to listen on
|
12
|
-
def initialize(machine_spec, transport, convergence_strategy, command = nil)
|
13
|
+
def initialize(machine_spec, transport, convergence_strategy, connection, command = nil)
|
13
14
|
super(machine_spec, transport, convergence_strategy)
|
14
15
|
@command = command
|
15
16
|
@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
|
16
32
|
end
|
17
33
|
|
18
34
|
def converge(action_handler)
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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" ],
|
24
117
|
)
|
25
|
-
|
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
|
26
170
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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)
|
31
179
|
machine_spec.reference['container_id'] = container.id
|
32
|
-
|
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
|
33
218
|
end
|
34
|
-
|
219
|
+
|
220
|
+
image.id
|
35
221
|
end
|
36
222
|
end
|
37
223
|
end
|