chef-metal-docker 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +201 -0
- data/README.md +3 -0
- data/Rakefile +6 -0
- data/lib/chef/provider/docker_container.rb +127 -0
- data/lib/chef/resource/docker_container.rb +58 -0
- data/lib/chef_metal/provisioner_init/docker_init.rb +4 -0
- data/lib/chef_metal_docker.rb +4 -0
- data/lib/chef_metal_docker/docker_convergence_strategy.rb +55 -0
- data/lib/chef_metal_docker/docker_provisioner.rb +215 -0
- data/lib/chef_metal_docker/docker_transport.rb +244 -0
- data/lib/chef_metal_docker/docker_unix_machine.rb +9 -0
- data/lib/chef_metal_docker/helpers.rb +146 -0
- data/lib/chef_metal_docker/helpers/container.rb +15 -0
- data/lib/chef_metal_docker/helpers/container/actions.rb +313 -0
- data/lib/chef_metal_docker/helpers/container/helpers.rb +156 -0
- data/lib/chef_metal_docker/version.rb +3 -0
- metadata +131 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'chef/mixin/shell_out'
|
2
|
+
include Chef::Mixin::ShellOut
|
3
|
+
|
4
|
+
# Helpers module
|
5
|
+
module ChefMetalDocker
|
6
|
+
# Helpers::Docker module
|
7
|
+
module Helpers
|
8
|
+
# Exception to signify that the Docker daemon is not yet ready to handle
|
9
|
+
# docker commands.
|
10
|
+
class DockerNotReady < StandardError
|
11
|
+
def initialize(timeout)
|
12
|
+
super <<-EOH
|
13
|
+
The Docker daemon did not become ready within #{timeout} seconds.
|
14
|
+
This most likely means that Docker failed to start.
|
15
|
+
Docker can fail to start if:
|
16
|
+
|
17
|
+
- a configuration file is invalid
|
18
|
+
- permissions are incorrect for the root directory of the docker runtime.
|
19
|
+
|
20
|
+
If this problem persists, check your service log files.
|
21
|
+
EOH
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Exception to signify that the docker command timed out.
|
26
|
+
class CommandTimeout < RuntimeError; end
|
27
|
+
|
28
|
+
def cli_args(spec)
|
29
|
+
cli_line = ''
|
30
|
+
spec.each_pair do |arg, value|
|
31
|
+
case value
|
32
|
+
when Array
|
33
|
+
next if value.empty?
|
34
|
+
args = value.map do |v|
|
35
|
+
v = "\"#{v}\"" if v.is_a?(String)
|
36
|
+
" --#{arg}=#{v}"
|
37
|
+
end
|
38
|
+
cli_line += args.join
|
39
|
+
when FalseClass, Fixnum, Integer, String, TrueClass
|
40
|
+
value = "\"#{value}\"" if value.is_a?(String)
|
41
|
+
cli_line += " --#{arg}=#{value}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
cli_line
|
45
|
+
end
|
46
|
+
|
47
|
+
def docker_inspect(id)
|
48
|
+
require 'json'
|
49
|
+
JSON.parse(docker_cmd("inspect #{id}").stdout)[0]
|
50
|
+
end
|
51
|
+
|
52
|
+
def docker_inspect_id(id)
|
53
|
+
inspect = docker_inspect(id)
|
54
|
+
inspect['id'] if inspect
|
55
|
+
end
|
56
|
+
|
57
|
+
def dockercfg_parse
|
58
|
+
require 'json'
|
59
|
+
dockercfg = JSON.parse(::File.read(::File.join(::Dir.home, '.dockercfg')))
|
60
|
+
dockercfg.each_pair do |k, v|
|
61
|
+
dockercfg[k].merge!(dockercfg_parse_auth(v['auth']))
|
62
|
+
end
|
63
|
+
dockercfg
|
64
|
+
end
|
65
|
+
|
66
|
+
def dockercfg_parse_auth(str)
|
67
|
+
require 'base64'
|
68
|
+
decoded_str = Base64.decode64(str)
|
69
|
+
if decoded_str
|
70
|
+
auth = {}
|
71
|
+
auth['username'], auth['password'] = decoded_str.split(':')
|
72
|
+
auth
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def timeout
|
77
|
+
10
|
78
|
+
end
|
79
|
+
|
80
|
+
# This is based upon wait_until_ready! from the opscode jenkins cookbook.
|
81
|
+
#
|
82
|
+
# Since the docker service returns immediately and the actual docker
|
83
|
+
# process is started as a daemon, we block the Chef Client run until the
|
84
|
+
# daemon is actually ready.
|
85
|
+
#
|
86
|
+
# This method will effectively "block" the current thread until the docker
|
87
|
+
# daemon is ready
|
88
|
+
#
|
89
|
+
# @raise [DockerNotReady]
|
90
|
+
# if the Docker master does not respond within (+timeout+) seconds
|
91
|
+
#
|
92
|
+
def wait_until_ready!
|
93
|
+
Timeout.timeout(timeout) do
|
94
|
+
loop do
|
95
|
+
result = shell_out('docker info')
|
96
|
+
break if Array(result.valid_exit_codes).include?(result.exitstatus)
|
97
|
+
Chef::Log.debug("Docker daemon is not running - #{result.stdout}\n#{result.stderr}")
|
98
|
+
sleep(0.5)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
rescue Timeout::Error
|
102
|
+
raise DockerNotReady.new(timeout), 'docker timeout exceeded'
|
103
|
+
end
|
104
|
+
|
105
|
+
# the Error message to display if a command times out. Subclasses
|
106
|
+
# may want to override this to provide more details on the timeout.
|
107
|
+
def command_timeout_error_message
|
108
|
+
<<-EOM
|
109
|
+
|
110
|
+
Command timed out:
|
111
|
+
#{cmd}
|
112
|
+
|
113
|
+
EOM
|
114
|
+
end
|
115
|
+
|
116
|
+
# Runs a docker command. Does not raise exception on non-zero exit code.
|
117
|
+
def docker_cmd(cmd, timeout = new_resource.cmd_timeout)
|
118
|
+
execute_cmd('docker ' + cmd, timeout)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Executes the given command with the specified timeout. Does not raise an
|
122
|
+
# exception on a non-zero exit code.
|
123
|
+
def execute_cmd(cmd, timeout = new_resource.cmd_timeout)
|
124
|
+
Chef::Log.debug('Executing: ' + cmd)
|
125
|
+
begin
|
126
|
+
shell_out(cmd, :timeout => timeout)
|
127
|
+
rescue Mixlib::ShellOut::CommandTimeout
|
128
|
+
raise CommandTimeout, command_timeout_error_message(cmd)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Executes the given docker command with the specified timeout. Raises an
|
133
|
+
# exception if the command returns a non-zero exit code.
|
134
|
+
def docker_cmd!(cmd, timeout = new_resource.cmd_timeout)
|
135
|
+
execute_cmd!('docker ' + cmd, timeout)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Executes the given command with the specified timeout. Raises an
|
139
|
+
# exception if the command returns a non-zero exit code.
|
140
|
+
def execute_cmd!(cmd, timeout = new_resource.cmd_timeout)
|
141
|
+
cmd = execute_cmd(cmd, timeout)
|
142
|
+
cmd.error!
|
143
|
+
cmd
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'chef_metal_docker/helpers'
|
2
|
+
require 'chef_metal_docker/helpers/container/actions'
|
3
|
+
require 'chef_metal_docker/helpers/container/helpers'
|
4
|
+
|
5
|
+
module ChefMetalDocker
|
6
|
+
module Helpers
|
7
|
+
module Container
|
8
|
+
|
9
|
+
include ChefMetalDocker::Helpers
|
10
|
+
include ChefMetalDocker::Helpers::Container::Helpers
|
11
|
+
include ChefMetalDocker::Helpers::Container::Actions
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
module ChefMetalDocker
|
2
|
+
module Helpers
|
3
|
+
module Container
|
4
|
+
# This is a collection of helper methods that are used to trigger actions in
|
5
|
+
# the LWRPs. By putting them in a separate file we can a) keep the LWRPs cleaner
|
6
|
+
# and b) unit test them!
|
7
|
+
module Actions
|
8
|
+
|
9
|
+
def commit
|
10
|
+
commit_args = cli_args(
|
11
|
+
'author' => new_resource.author,
|
12
|
+
'message' => new_resource.message,
|
13
|
+
'run' => new_resource.run
|
14
|
+
)
|
15
|
+
commit_end_args = ''
|
16
|
+
|
17
|
+
if new_resource.repository
|
18
|
+
commit_end_args = new_resource.repository
|
19
|
+
commit_end_args += ":#{new_resource.tag}" if new_resource.tag
|
20
|
+
end
|
21
|
+
|
22
|
+
docker_cmd!("commit #{commit_args} #{current_resource.id} #{commit_end_args}")
|
23
|
+
end
|
24
|
+
|
25
|
+
def cp
|
26
|
+
docker_cmd!("cp #{current_resource.id}:#{new_resource.source} #{new_resource.destination}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def export
|
30
|
+
docker_cmd!("export #{current_resource.id} > #{new_resource.destination}")
|
31
|
+
end
|
32
|
+
|
33
|
+
def kill
|
34
|
+
if service?
|
35
|
+
service_stop
|
36
|
+
else
|
37
|
+
docker_cmd!("kill #{current_resource.id}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove
|
42
|
+
rm_args = cli_args(
|
43
|
+
'force' => new_resource.force,
|
44
|
+
'link' => new_resource.link
|
45
|
+
)
|
46
|
+
docker_cmd!("rm #{rm_args} #{current_resource.id}")
|
47
|
+
service_remove if service?
|
48
|
+
end
|
49
|
+
|
50
|
+
def redeploy
|
51
|
+
stop if running?
|
52
|
+
remove if exists?
|
53
|
+
run
|
54
|
+
end
|
55
|
+
|
56
|
+
def restart
|
57
|
+
if service?
|
58
|
+
service_restart
|
59
|
+
else
|
60
|
+
docker_cmd!("restart #{current_resource.id}")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# rubocop:disable MethodLength
|
65
|
+
def run
|
66
|
+
run_args = cli_args(
|
67
|
+
'cpu-shares' => new_resource.cpu_shares,
|
68
|
+
'cidfile' => cidfile,
|
69
|
+
'detach' => new_resource.detach,
|
70
|
+
'dns' => Array(new_resource.dns),
|
71
|
+
'dns-search' => Array(new_resource.dns_search),
|
72
|
+
'env' => Array(new_resource.env),
|
73
|
+
'entrypoint' => new_resource.entrypoint,
|
74
|
+
'expose' => Array(new_resource.expose),
|
75
|
+
'hostname' => new_resource.hostname,
|
76
|
+
'interactive' => new_resource.stdin,
|
77
|
+
'label' => new_resource.label,
|
78
|
+
'link' => Array(new_resource.link),
|
79
|
+
'lxc-conf' => Array(new_resource.lxc_conf),
|
80
|
+
'memory' => new_resource.memory,
|
81
|
+
'networking' => new_resource.networking,
|
82
|
+
'name' => container_name,
|
83
|
+
'opt' => Array(new_resource.opt),
|
84
|
+
'publish' => Array(port),
|
85
|
+
'publish-all' => new_resource.publish_exposed_ports,
|
86
|
+
'privileged' => new_resource.privileged,
|
87
|
+
'rm' => new_resource.remove_automatically,
|
88
|
+
'tty' => new_resource.tty,
|
89
|
+
'user' => new_resource.user,
|
90
|
+
'volume' => Array(new_resource.volume),
|
91
|
+
'volumes-from' => new_resource.volumes_from,
|
92
|
+
'workdir' => new_resource.working_directory
|
93
|
+
)
|
94
|
+
dr = docker_cmd!("run #{run_args} #{new_resource.image} #{new_resource.command}")
|
95
|
+
dr.error!
|
96
|
+
new_resource.id(dr.stdout.chomp)
|
97
|
+
service_create if service?
|
98
|
+
end
|
99
|
+
# rubocop:enable MethodLength
|
100
|
+
|
101
|
+
def service_action(actions)
|
102
|
+
if new_resource.init_type == 'runit'
|
103
|
+
runit_service service_name do
|
104
|
+
run_template_name 'docker-container'
|
105
|
+
action actions
|
106
|
+
end
|
107
|
+
else
|
108
|
+
service service_name do
|
109
|
+
case new_resource.init_type
|
110
|
+
when 'systemd'
|
111
|
+
provider Chef::Provider::Service::Systemd
|
112
|
+
when 'upstart'
|
113
|
+
provider Chef::Provider::Service::Upstart
|
114
|
+
end
|
115
|
+
supports :status => true, :restart => true, :reload => true
|
116
|
+
action actions
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def service_create
|
122
|
+
case new_resource.init_type
|
123
|
+
when 'runit'
|
124
|
+
service_create_runit
|
125
|
+
when 'systemd'
|
126
|
+
service_create_systemd
|
127
|
+
when 'sysv'
|
128
|
+
service_create_sysv
|
129
|
+
when 'upstart'
|
130
|
+
service_create_upstart
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def service_create_runit
|
135
|
+
runit_service service_name do
|
136
|
+
cookbook new_resource.cookbook
|
137
|
+
default_logger true
|
138
|
+
options(
|
139
|
+
'service_name' => service_name
|
140
|
+
)
|
141
|
+
run_template_name service_template
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def service_create_systemd
|
146
|
+
template "/usr/lib/systemd/system/#{service_name}.socket" do
|
147
|
+
if new_resource.socket_template.nil?
|
148
|
+
source 'docker-container.socket.erb'
|
149
|
+
else
|
150
|
+
source new_resource.socket_template
|
151
|
+
end
|
152
|
+
cookbook new_resource.cookbook
|
153
|
+
mode '0644'
|
154
|
+
owner 'root'
|
155
|
+
group 'root'
|
156
|
+
variables(
|
157
|
+
:service_name => service_name,
|
158
|
+
:sockets => sockets
|
159
|
+
)
|
160
|
+
not_if port.empty?
|
161
|
+
end
|
162
|
+
|
163
|
+
template "/usr/lib/systemd/system/#{service_name}.service" do
|
164
|
+
source service_template
|
165
|
+
cookbook new_resource.cookbook
|
166
|
+
mode '0644'
|
167
|
+
owner 'root'
|
168
|
+
group 'root'
|
169
|
+
variables(
|
170
|
+
:cmd_timeout => new_resource.cmd_timeout,
|
171
|
+
:service_name => service_name
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
service_action([:start, :enable])
|
176
|
+
end
|
177
|
+
|
178
|
+
def service_create_sysv
|
179
|
+
template "/etc/init.d/#{service_name}" do
|
180
|
+
source service_template
|
181
|
+
cookbook new_resource.cookbook
|
182
|
+
mode '0755'
|
183
|
+
owner 'root'
|
184
|
+
group 'root'
|
185
|
+
variables(
|
186
|
+
:cmd_timeout => new_resource.cmd_timeout,
|
187
|
+
:service_name => service_name
|
188
|
+
)
|
189
|
+
end
|
190
|
+
|
191
|
+
service_action([:start, :enable])
|
192
|
+
end
|
193
|
+
|
194
|
+
def service_create_upstart
|
195
|
+
# The upstart init script requires inotifywait, which is in inotify-tools
|
196
|
+
package 'inotify-tools'
|
197
|
+
|
198
|
+
template "/etc/init/#{service_name}.conf" do
|
199
|
+
source service_template
|
200
|
+
cookbook new_resource.cookbook
|
201
|
+
mode '0600'
|
202
|
+
owner 'root'
|
203
|
+
group 'root'
|
204
|
+
variables(
|
205
|
+
:cmd_timeout => new_resource.cmd_timeout,
|
206
|
+
:service_name => service_name
|
207
|
+
)
|
208
|
+
end
|
209
|
+
|
210
|
+
service_action([:start, :enable])
|
211
|
+
end
|
212
|
+
|
213
|
+
def service_remove
|
214
|
+
case new_resource.init_type
|
215
|
+
when 'runit'
|
216
|
+
service_remove_runit
|
217
|
+
when 'systemd'
|
218
|
+
service_remove_systemd
|
219
|
+
when 'sysv'
|
220
|
+
service_remove_sysv
|
221
|
+
when 'upstart'
|
222
|
+
service_remove_upstart
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def service_remove_runit
|
227
|
+
runit_service service_name do
|
228
|
+
action :disable
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def service_remove_systemd
|
233
|
+
service_action([:stop, :disable])
|
234
|
+
|
235
|
+
%w(service socket).each do |f|
|
236
|
+
file "/usr/lib/systemd/system/#{service_name}.#{f}" do
|
237
|
+
action :delete
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def service_remove_sysv
|
243
|
+
service_action([:stop, :disable])
|
244
|
+
|
245
|
+
file "/etc/init.d/#{service_name}" do
|
246
|
+
action :delete
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def service_remove_upstart
|
251
|
+
service_action([:stop, :disable])
|
252
|
+
|
253
|
+
file "/etc/init/#{service_name}" do
|
254
|
+
action :delete
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def service_restart
|
259
|
+
service_action([:restart])
|
260
|
+
end
|
261
|
+
|
262
|
+
def service_start
|
263
|
+
service_action([:start])
|
264
|
+
end
|
265
|
+
|
266
|
+
def service_stop
|
267
|
+
service_action([:stop])
|
268
|
+
end
|
269
|
+
|
270
|
+
def service_template
|
271
|
+
return new_resource.init_template unless new_resource.init_template.nil?
|
272
|
+
case new_resource.init_type
|
273
|
+
when 'runit'
|
274
|
+
'docker-container'
|
275
|
+
when 'systemd'
|
276
|
+
'docker-container.service.erb'
|
277
|
+
when 'upstart'
|
278
|
+
'docker-container.conf.erb'
|
279
|
+
when 'sysv'
|
280
|
+
'docker-container.sysv.erb'
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def start
|
285
|
+
start_args = cli_args(
|
286
|
+
'attach' => new_resource.attach,
|
287
|
+
'interactive' => new_resource.stdin
|
288
|
+
)
|
289
|
+
if service?
|
290
|
+
service_create
|
291
|
+
else
|
292
|
+
docker_cmd!("start #{start_args} #{current_resource.id}")
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def stop
|
297
|
+
stop_args = cli_args(
|
298
|
+
'time' => new_resource.cmd_timeout
|
299
|
+
)
|
300
|
+
if service?
|
301
|
+
service_stop
|
302
|
+
else
|
303
|
+
docker_cmd!("stop #{stop_args} #{current_resource.id}", (new_resource.cmd_timeout + 15))
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
def wait
|
308
|
+
docker_cmd!("wait #{current_resource.id}")
|
309
|
+
end
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|