/
provider_docker_container.rb
358 lines (332 loc) · 14.8 KB
/
provider_docker_container.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
$LOAD_PATH.unshift *Dir[File.expand_path('../../files/default/vendor/gems/**/lib', __FILE__)]
require 'docker'
require 'shellwords'
require_relative 'helpers_container'
class Chef
class Provider
class DockerContainer < Chef::Provider::LWRPBase
# register with the resource resolution system
provides :docker_container if Chef::Provider.respond_to?(:provides)
include DockerHelpers::Container
use_inline_resources
def load_current_resource
@api_version = Docker.version['ApiVersion'].to_f
@current_resource = Chef::Resource::DockerContainer.new(new_resource.name)
begin
c = Docker::Container.get(new_resource.container_name)
@current_resource.attach_stderr c.info['Config']['AttachStderr']
@current_resource.attach_stdin c.info['Config']['AttachStdin']
@current_resource.attach_stdout c.info['Config']['AttachStdout']
@current_resource.binds c.info['HostConfig']['Binds']
@current_resource.cap_add c.info['HostConfig']['CapAdd']
@current_resource.cap_drop c.info['HostConfig']['CapDrop']
@current_resource.cgroup_parent c.info['HostConfig']['CgroupParent']
@current_resource.command c.info['Config']['Cmd']
@current_resource.cpu_shares c.info['HostConfig']['CpuShares']
@current_resource.cpuset_cpus c.info['HostConfig']['CpusetCpus']
@current_resource.devices c.info['HostConfig']['Devices']
@current_resource.dns c.info['HostConfig']['Dns']
@current_resource.dns_search c.info['HostConfig']['DnsSearch']
@current_resource.domainname c.info['Config']['Domainname']
@current_resource.entrypoint c.info['Config']['Entrypoint']
@current_resource.env c.info['Config']['Env']
@current_resource.exposed_ports c.info['Config']['ExposedPorts']
@current_resource.extra_hosts c.info['HostConfig']['ExtraHosts']
@current_resource.hostname c.info['Config']['Hostname']
@current_resource.image c.info['Config']['Image']
@current_resource.links c.info['HostConfig']['Links']
@current_resource.log_config c.info['HostConfig']['LogConfig']
@current_resource.mac_address c.info['Config']['MacAddress']
@current_resource.memory c.info['HostConfig']['Memory']
@current_resource.memory_swap c.info['HostConfig']['MemorySwap']
@current_resource.network_disabled c.info['Config']['NetworkDisabled']
@current_resource.network_mode c.info['HostConfig']['NetworkMode']
@current_resource.open_stdin c.info['Config']['OpenStdin']
@current_resource.port_bindings c.info['HostConfig']['PortBindings']
@current_resource.privileged c.info['HostConfig']['Privileged']
@current_resource.publish_all_ports c.info['HostConfig']['PublishAllPorts']
@current_resource.restart_policy c.info['HostConfig']['RestartPolicy']
@current_resource.stdin_once c.info['Config']['StdinOnce']
@current_resource.tty c.info['Config']['Tty']
@current_resource.ulimits c.info['HostConfig']['Ulimits']
@current_resource.user c.info['Config']['User']
@current_resource.volumes c.info['Config']['Volumes']
@current_resource.volumes_from c.info['HostConfig']['VolumesFrom']
@current_resource.working_dir c.info['Config']['WorkingDir']
rescue Docker::Error::NotFoundError
return @current_resource
end
end
def resource_changes
changes = []
changes << :attach_stderr if current_resource.attach_stderr != parsed_attach_stderr
changes << :attach_stdin if current_resource.attach_stdin != parsed_attach_stdin
changes << :attach_stdout if current_resource.attach_stdout != parsed_attach_stdout
changes << :binds if current_resource.binds != parsed_binds
changes << :cap_add if current_resource.cap_add != parsed_cap_add
changes << :cap_drop if current_resource.cap_drop != parsed_cap_drop
changes << :cgroup_parent if current_resource.cgroup_parent != new_resource.cgroup_parent
changes << :command if update_command?
changes << :cpu_shares if current_resource.cpu_shares != new_resource.cpu_shares
changes << :cpuset_cpus if current_resource.cpuset_cpus != new_resource.cpuset_cpus
changes << :devices if current_resource.devices != parsed_devices
changes << :dns if current_resource.dns != parsed_dns
changes << :dns_search if current_resource.dns_search != parsed_dns_search
changes << :domainname if current_resource.domainname != new_resource.domainname
changes << :entrypoint if update_entrypoint?
changes << :env if update_env?
changes << :exposed_ports if current_resource.exposed_ports != exposed_ports
changes << :extra_hosts if current_resource.extra_hosts != parsed_extra_hosts
changes << :hostname if (!new_resource.hostname.nil?) && (current_resource.hostname != new_resource.hostname)
changes << :image if current_resource.image != "#{parsed_repo}:#{new_resource.tag}"
changes << :links if current_resource.links != serialized_links
changes << :log_config if current_resource.log_config != serialized_log_config
changes << :mac_address if current_resource.mac_address != new_resource.mac_address
changes << :memory if current_resource.memory != new_resource.memory
changes << :memory_swap if current_resource.memory_swap != new_resource.memory_swap
changes << :network_disabled if current_resource.network_disabled != new_resource.network_disabled
changes << :network_mode if current_resource.network_mode != parsed_network_mode
changes << :open_stdin if current_resource.open_stdin != new_resource.open_stdin
changes << :port_bindings if current_resource.port_bindings != port_bindings
changes << :privileged if current_resource.privileged != new_resource.privileged
changes << :publish_all_ports if current_resource.publish_all_ports != new_resource.publish_all_ports
changes << :restart_policy if current_resource.restart_policy != parsed_restart_policy
changes << :stdin_once if current_resource.stdin_once != parsed_stdin_once
changes << :tty if current_resource.tty != new_resource.tty
changes << :ulimits if current_resource.ulimits != new_resource.ulimits
changes << :user if current_resource.user != new_resource.user
changes << :volumes if current_resource.volumes != parsed_volumes
changes << :volumes_from if current_resource.volumes_from != parsed_volumes_from
changes << :working_dir if current_resource.working_dir != new_resource.working_dir
changes
end
# Most important work is done here.
def create_container
api_timeouts
tries ||= new_resource.api_retries
Docker::Container.create(
'name' => new_resource.container_name,
'Image' => "#{parsed_repo}:#{new_resource.tag}",
'Cmd' => parsed_command,
'AttachStderr' => parsed_attach_stderr,
'AttachStdin' => parsed_attach_stdin,
'AttachStdout' => parsed_attach_stdout,
'Domainname' => new_resource.domain_name,
'Entrypoint' => parsed_entrypoint,
'Env' => parsed_env,
'ExposedPorts' => exposed_ports,
'Hostname' => new_resource.host_name,
'MacAddress' => new_resource.mac_address,
'NetworkDisabled' => new_resource.network_disabled,
'OpenStdin' => new_resource.open_stdin,
'StdinOnce' => parsed_stdin_once,
'Tty' => new_resource.tty,
'User' => new_resource.user,
'Volumes' => parsed_volumes,
'WorkingDir' => new_resource.working_dir,
'HostConfig' => {
'Binds' => parsed_binds,
'CapAdd' => parsed_cap_add,
'CapDrop' => parsed_cap_drop,
'CgroupParent' => new_resource.cgroup_parent,
'CpuShares' => new_resource.cpu_shares,
'CpusetCpus' => new_resource.cpuset_cpus,
'Devices' => parsed_devices,
'Dns' => parsed_dns,
'DnsSearch' => parsed_dns_search,
'ExtraHosts' => parsed_extra_hosts,
'Links' => parsed_links,
'LogConfig' => new_resource.log_config,
'Memory' => new_resource.memory,
'MemorySwap' => new_resource.memory_swap,
'NetworkMode' => parsed_network_mode,
'Privileged' => new_resource.privileged,
'PortBindings' => port_bindings,
'PublishAllPorts' => new_resource.publish_all_ports,
'RestartPolicy' => parsed_restart_policy,
'Ulimits' => new_resource.ulimits,
'VolumesFrom' => parsed_volumes_from
}
)
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
#########
# Actions
#########
# Super handy visual reference!
# http://gliderlabs.com/images/docker_events.png
action :create do
action_delete unless resource_changes.empty? || !container_created?
next if container_created?
converge_by "creating #{new_resource.container_name}" do
create_container
end
new_resource.updated_by_last_action(true)
end
action :start do
api_timeouts
c = Docker::Container.get(new_resource.container_name)
next if c.info['State']['Restarting']
next if c.info['State']['Running']
converge_by "starting #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
if new_resource.detach
new_resource.attach_stdin false
new_resource.attach_stdout false
new_resource.attach_stderr false
new_resource.stdin_once false
c.start
else
c.start
new_resource.timeout ? c.wait(new_resource.timeout) : c.wait
end
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
new_resource.updated_by_last_action(true)
end
action :stop do
api_timeouts
next unless container_created?
c = Docker::Container.get(new_resource.container_name)
next unless c.info['State']['Running']
converge_by "stopping #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
c.stop
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
new_resource.updated_by_last_action(true)
end
action :kill do
api_timeouts
next unless container_created?
c = Docker::Container.get(new_resource.container_name)
next unless c.info['State']['Running']
converge_by "killing #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
c.kill(signal: new_resource.signal)
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
new_resource.updated_by_last_action(true)
end
action :run do
action_create
action_start
action_delete if new_resource.autoremove
end
action :run_if_missing do
next if container_created?
action_run
end
action :pause do
api_timeouts
next unless container_created?
c = Docker::Container.get(new_resource.container_name)
next if c.info['State']['Paused']
converge_by "pausing #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
c.pause
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
new_resource.updated_by_last_action(true)
end
action :unpause do
api_timeouts
next unless container_created?
c = Docker::Container.get(new_resource.container_name)
next unless c.info['State']['Paused']
converge_by "unpausing #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
c.unpause
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
new_resource.updated_by_last_action(true)
end
action :restart do
action_stop
action_start
end
action :redeploy do
action_delete
action_run
end
action :delete do
next unless container_created?
action_unpause
action_stop
c = Docker::Container.get(new_resource.container_name)
converge_by "deleting #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
c.delete(force: new_resource.force, v: new_resource.remove_volumes)
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
new_resource.updated_by_last_action(true)
end
action :remove do
action_delete
end
action :remove_link do
# Help! I couldn't get this working from the CLI in docker 1.6.2.
# It's of dubious usefulness, and it looks like this stuff is
# changing in 1.7.x anyway.
converge_by "removing links for #{new_resource.container_name}" do
Chef::Log.info(':remove_link not currently implemented')
end
new_resource.updated_by_last_action(true)
end
action :commit do
api_timeouts
c = Docker::Container.get(new_resource.container_name)
converge_by "committing #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
new_image = c.commit
new_image.tag('repo' => new_resource.repo, 'tag' => new_resource.tag, 'force' => new_resource.force)
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
end
action :export do
api_timeouts
fail "Please set outfile property on #{new_resource.container_name}" if new_resource.outfile.nil?
c = Docker::Container.get(new_resource.container_name)
converge_by "exporting #{new_resource.container_name}" do
begin
tries ||= new_resource.api_retries
::File.open(new_resource.outfile, 'w') { |f| c.export { |chunk| f.write(chunk) } }
rescue Docker::Error => e
retry unless (tries -= 1).zero?
raise e.message
end
end
end
end
end
end