chef-metal-docker 0.1
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 +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
|