rummager 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/rummager.rb +29 -0
- data/lib/rummager/containers.rb +473 -0
- data/lib/rummager/images.rb +223 -0
- data/lib/rummager/util.rb +39 -0
- metadata +103 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 16d898882e16fff98a44d7fb1337cecea686894f
|
4
|
+
data.tar.gz: e4ced1135eeaf4be86843c30e14cc43fcc9e67bb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b0c82166972288d407cb0c1c0518758ef421245cb063500aa74be18373506227958267a99b3743e464a5af69ca1c8d3c77f30e9c1aa136285f7d34718fe89485
|
7
|
+
data.tar.gz: 6b806770648f370378f580ca686109382720d55322efe84bcb92b2c8555a664389722738f04813435d2716481e0d7204baf65a1fda0949c3e0cdeae3040265d8
|
data/lib/rummager.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'logger'
|
3
|
+
require 'excon'
|
4
|
+
|
5
|
+
module Rummager
|
6
|
+
class << self; attr_accessor :repo_base; end
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rummager/containers'
|
10
|
+
require 'rummager/images'
|
11
|
+
require 'rummager/util'
|
12
|
+
|
13
|
+
# allow long runs through Excon, otherwise commands that
|
14
|
+
# take a long time will fail due to timeout
|
15
|
+
Excon.defaults[:write_timeout] = 10000
|
16
|
+
Excon.defaults[:read_timeout] = 10000
|
17
|
+
|
18
|
+
# provide Docker verboseness
|
19
|
+
if Rake.verbose == true
|
20
|
+
Docker.logger = Logger.new(STDOUT)
|
21
|
+
Docker.logger.level = Logger::DEBUG
|
22
|
+
end
|
23
|
+
|
24
|
+
task :"clean" => [ :"containers:clean", :"images:clean" ]
|
25
|
+
|
26
|
+
task :"clobber" => [ :"containers:clobber", :"images:clobber" ]
|
27
|
+
|
28
|
+
|
29
|
+
__END__
|
@@ -0,0 +1,473 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'logger'
|
3
|
+
require 'rake/tasklib'
|
4
|
+
require 'docker'
|
5
|
+
require 'date'
|
6
|
+
require 'digest'
|
7
|
+
require 'json'
|
8
|
+
require 'excon'
|
9
|
+
require 'time'
|
10
|
+
|
11
|
+
# Create and describe generic container operation targets
|
12
|
+
namespace :containers do
|
13
|
+
|
14
|
+
desc "Start background containers"
|
15
|
+
task :"start"
|
16
|
+
|
17
|
+
desc "Stop background containers"
|
18
|
+
task :"stop"
|
19
|
+
|
20
|
+
desc "Remove temporary containers"
|
21
|
+
task :"clean"
|
22
|
+
|
23
|
+
desc "Remove all Docker containters including caches"
|
24
|
+
task :"clobber" => [ :"containers:clean" ]
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
module Rummager
|
29
|
+
|
30
|
+
CNTNR_ARGS_CREATE = {
|
31
|
+
'AttachStdin' => true,
|
32
|
+
'AttachStdout' => true,
|
33
|
+
'AttachStderr' => true,
|
34
|
+
'OpenStdin' => true,
|
35
|
+
'Tty' => true,
|
36
|
+
}
|
37
|
+
|
38
|
+
###########################################################
|
39
|
+
##
|
40
|
+
## Container Handling Pieces
|
41
|
+
##
|
42
|
+
###########################################################
|
43
|
+
|
44
|
+
# Abstract base class for Container tasks
|
45
|
+
class ContainerTaskBase < Rake::Task
|
46
|
+
attr_accessor :container_name
|
47
|
+
|
48
|
+
def docker_obj
|
49
|
+
begin
|
50
|
+
@container_obj ||= Docker::Container.get(@container_name.to_s)
|
51
|
+
rescue
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_container?
|
56
|
+
! docker_obj.nil?
|
57
|
+
end
|
58
|
+
|
59
|
+
def is_running?
|
60
|
+
container_obj.json['State']['Running'] == false
|
61
|
+
end
|
62
|
+
|
63
|
+
def never_ran?
|
64
|
+
if has_container?
|
65
|
+
if docker_obj.json['State']['Running'] == true
|
66
|
+
puts "#{@container_name} is running!" if Rake.verbose == true
|
67
|
+
false
|
68
|
+
else
|
69
|
+
end
|
70
|
+
else
|
71
|
+
true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def exit_code
|
76
|
+
container_obj.json['State']['ExitCode']
|
77
|
+
end
|
78
|
+
|
79
|
+
end # ContainerTaskBase
|
80
|
+
|
81
|
+
class ContainerCreateTask < Rummager::ContainerTaskBase
|
82
|
+
attr_accessor :args_create
|
83
|
+
attr_accessor :command
|
84
|
+
attr_accessor :exposed_ports
|
85
|
+
attr_accessor :image_name
|
86
|
+
attr_accessor :repo_base
|
87
|
+
|
88
|
+
def needed?
|
89
|
+
! has_container?
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(task_name, app)
|
93
|
+
super(task_name,app)
|
94
|
+
@actions << Proc.new {
|
95
|
+
create_args = @args_create
|
96
|
+
create_args['Image'] = "#{@repo_base}/#{@image_name}:latest"
|
97
|
+
create_args['name'] = @container_name
|
98
|
+
if @command
|
99
|
+
create_args['Cmd'] = @command
|
100
|
+
end
|
101
|
+
if @exposed_ports
|
102
|
+
create_args['ExposedPorts'] = {};
|
103
|
+
@exposed_ports.each do |prt|
|
104
|
+
create_args['ExposedPorts'][prt] = {}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
newcont = Docker::Container.create( create_args )
|
108
|
+
puts "created container '#{@container_name}' -> #{newcont.json}" if Rake.verbose == true
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
end #ContainerCreateTask
|
113
|
+
|
114
|
+
|
115
|
+
class ContainerStartTask < Rummager::ContainerTaskBase
|
116
|
+
attr_accessor :volumes_from
|
117
|
+
attr_accessor :args_start
|
118
|
+
attr_accessor :binds
|
119
|
+
attr_accessor :port_bindings
|
120
|
+
attr_accessor :publishall
|
121
|
+
attr_accessor :exec_on_start
|
122
|
+
attr_accessor :start_once
|
123
|
+
|
124
|
+
def needed?
|
125
|
+
if has_container?
|
126
|
+
puts "checking if #{@container_name} is running" if Rake.verbose == true
|
127
|
+
if docker_obj.json['State']['Running'] == false
|
128
|
+
puts "#{@container_name} is NOT running" if Rake.verbose == true
|
129
|
+
if Time.parse(docker_obj.json['State']['StartedAt']) != Time.parse('0001-01-01T00:00:00Z')
|
130
|
+
puts "#{@container_name} previously ran" if Rake.verbose == true
|
131
|
+
if @start_once == true
|
132
|
+
puts "#{@container_name} is a start_once container, not needed" if Rake.verbose == true
|
133
|
+
return false
|
134
|
+
end
|
135
|
+
else
|
136
|
+
puts "#{@container_name} has never run" if Rake.verbose == true
|
137
|
+
end
|
138
|
+
else
|
139
|
+
puts "#{@container_name} is running" if Rake.verbose == true
|
140
|
+
end
|
141
|
+
else
|
142
|
+
puts "#{@container_name} doesnt exist" if Rake.verbose == true
|
143
|
+
end
|
144
|
+
true
|
145
|
+
end
|
146
|
+
|
147
|
+
def initialize(task_name, app)
|
148
|
+
super(task_name,app)
|
149
|
+
@actions << Proc.new {
|
150
|
+
start_args = @args_start
|
151
|
+
if @volumes_from
|
152
|
+
puts "using VF:#{@volumes_from}" if Rake.verbose == true
|
153
|
+
start_args.merge!( {'VolumesFrom' => @volumes_from} )
|
154
|
+
end
|
155
|
+
if @binds
|
156
|
+
puts "using BINDS:#{@binds}" if Rake.verbose == true
|
157
|
+
start_args['Binds'] = @binds
|
158
|
+
end
|
159
|
+
if @port_bindings
|
160
|
+
puts "using PortBindings:#{@port_bindings}" if Rake.verbose == true
|
161
|
+
start_args['PortBindings'] = @port_bindings
|
162
|
+
end
|
163
|
+
if @publishall
|
164
|
+
start_args['PublishAllPorts'] = true
|
165
|
+
end
|
166
|
+
puts "Starting: #{@container_name}"
|
167
|
+
docker_obj.start( start_args )
|
168
|
+
if @exec_on_start
|
169
|
+
begin
|
170
|
+
puts "issuing exec calls" if Rake.verbose == true
|
171
|
+
exec_on_start.each do |ae|
|
172
|
+
if ae.delete(:hide_output)
|
173
|
+
docker_obj.exec(ae.delete(:cmd),ae)
|
174
|
+
else
|
175
|
+
docker_obj.exec(ae.delete(:cmd),ae) { |stream,chunk| puts "#{chunk}" }
|
176
|
+
end
|
177
|
+
puts "all exec calls complete" if Rake.verbose == true
|
178
|
+
end
|
179
|
+
rescue => ex
|
180
|
+
raise IOError, "exec failed:#{ex.message}"
|
181
|
+
end
|
182
|
+
end # @exec_on_start
|
183
|
+
}
|
184
|
+
end # initialize
|
185
|
+
|
186
|
+
end #ContainerStartTask
|
187
|
+
|
188
|
+
class ContainerStopTask < Rummager::ContainerTaskBase
|
189
|
+
|
190
|
+
def needed?
|
191
|
+
if has_container?
|
192
|
+
docker_obj.json['State']['Running'] == true
|
193
|
+
else
|
194
|
+
false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def initialize(task_name, app)
|
199
|
+
super(task_name,app)
|
200
|
+
@actions << Proc.new {
|
201
|
+
puts "Stopping #{@container_name}" if Rake.verbose == true
|
202
|
+
docker_obj.stop
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
end #ContainerStopTask
|
207
|
+
|
208
|
+
|
209
|
+
class ContainerRMTask < Rummager::ContainerTaskBase
|
210
|
+
|
211
|
+
def needed?
|
212
|
+
puts "checking needed? for rm:#{@container_name}" if Rake.verbose == true
|
213
|
+
has_container?
|
214
|
+
end
|
215
|
+
|
216
|
+
def initialize(task_name, app)
|
217
|
+
super(task_name,app)
|
218
|
+
@actions << Proc.new {
|
219
|
+
puts "removing container #{@name}:#{docker_obj.to_s}" if Rake.verbose == true
|
220
|
+
docker_obj.delete(:force => true, :v => true)
|
221
|
+
}
|
222
|
+
end #initialize
|
223
|
+
|
224
|
+
end #ContainerRMTask
|
225
|
+
|
226
|
+
class ContainerEnterTask < Rummager::ContainerTaskBase
|
227
|
+
attr_accessor :env_name
|
228
|
+
def needed?
|
229
|
+
true
|
230
|
+
end
|
231
|
+
|
232
|
+
def initialize(task_name, app)
|
233
|
+
super(task_name,app)
|
234
|
+
@actions << Proc.new {
|
235
|
+
puts "Entering: #{@container_name}"
|
236
|
+
exec "docker attach #{docker_obj.id}"
|
237
|
+
}
|
238
|
+
end # initialize
|
239
|
+
|
240
|
+
end #ContainerEnterTask
|
241
|
+
|
242
|
+
|
243
|
+
# Template to generate tasks for Container lifecycle
|
244
|
+
class ClickContainer < Rake::TaskLib
|
245
|
+
attr_accessor :container_name
|
246
|
+
attr_accessor :image_name
|
247
|
+
attr_accessor :repo_base
|
248
|
+
attr_accessor :command
|
249
|
+
attr_accessor :args_create
|
250
|
+
attr_accessor :args_start
|
251
|
+
attr_accessor :volumes_from
|
252
|
+
attr_accessor :binds
|
253
|
+
attr_accessor :exposed_ports
|
254
|
+
attr_accessor :port_bindings
|
255
|
+
attr_accessor :publishall
|
256
|
+
attr_accessor :dep_jobs
|
257
|
+
attr_accessor :exec_on_start
|
258
|
+
attr_accessor :allow_enter
|
259
|
+
attr_accessor :enter_dep_jobs
|
260
|
+
attr_accessor :noclean
|
261
|
+
|
262
|
+
def initialize(container_name,args={})
|
263
|
+
@container_name = container_name
|
264
|
+
@image_name = args.delete(:image_name) || container_name
|
265
|
+
@repo_base = args.delete(:repo_base) || Rummager.repo_base
|
266
|
+
@command = args.delete(:command)
|
267
|
+
@args_create = args.delete(:args_create) || CNTNR_ARGS_CREATE
|
268
|
+
@args_start = args.delete(:args_start) || {}
|
269
|
+
@volumes_from = args.delete(:volumes_from)
|
270
|
+
@binds = args.delete(:binds)
|
271
|
+
@exposed_ports = args.delete(:exposed_ports)
|
272
|
+
@port_bindings = args.delete(:port_bindings)
|
273
|
+
if (!@exposed_ports.nil? && !@port_bindings.nil?)
|
274
|
+
puts "WARNING: both 'exposed_ports' and 'port_bindings' are defined on #{@container_name}"
|
275
|
+
end
|
276
|
+
@publishall = args.delete(:publishall)
|
277
|
+
@dep_jobs = args.delete(:dep_jobs)
|
278
|
+
@exec_on_start = args.delete(:exec_on_start)
|
279
|
+
@enter_dep_jobs = args.delete(:enter_dep_jobs) || []
|
280
|
+
@allow_enter = args.delete(:allow_enter)
|
281
|
+
@noclean = args.delete(:noclean)
|
282
|
+
if !args.empty?
|
283
|
+
raise ArgumentError, "ClickContainer'#{@container_name}' defenition has unused/invalid key-values:#{args}"
|
284
|
+
end
|
285
|
+
yield self if block_given?
|
286
|
+
define
|
287
|
+
end
|
288
|
+
|
289
|
+
def define
|
290
|
+
namespace "containers" do
|
291
|
+
namespace @container_name do
|
292
|
+
# create task
|
293
|
+
realcreatetask = Rummager::ContainerCreateTask.define_task :create
|
294
|
+
realcreatetask.container_name = @container_name
|
295
|
+
realcreatetask.image_name = @image_name
|
296
|
+
realcreatetask.repo_base = @repo_base
|
297
|
+
realcreatetask.args_create = @args_create
|
298
|
+
realcreatetask.command = @command
|
299
|
+
realcreatetask.exposed_ports = @exposed_ports
|
300
|
+
Rake::Task["containers:#{@container_name}:create"].enhance( [ :"images:#{@image_name}:build" ] )
|
301
|
+
if @dep_jobs
|
302
|
+
@dep_jobs.each do |dj|
|
303
|
+
Rake::Task["containers:#{@container_name}:create"].enhance( [ :"#{dj}" ] )
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
# start task
|
308
|
+
starttask = Rummager::ContainerStartTask.define_task :start
|
309
|
+
starttask.container_name = @container_name
|
310
|
+
starttask.args_start = @args_start
|
311
|
+
starttask.volumes_from = @volumes_from
|
312
|
+
starttask.binds = @binds
|
313
|
+
starttask.port_bindings = @port_bindings
|
314
|
+
starttask.publishall = @publishall
|
315
|
+
starttask.exec_on_start = @exec_on_start
|
316
|
+
|
317
|
+
Rake::Task["containers:#{@container_name}:start"].enhance( [ :"containers:#{@container_name}:create" ] )
|
318
|
+
if @volumes_from
|
319
|
+
@volumes_from.each do |vf|
|
320
|
+
Rake::Task["containers:#{@container_name}:create"].enhance([:"containers:#{vf}:start" ])
|
321
|
+
end
|
322
|
+
end
|
323
|
+
if @allow_enter
|
324
|
+
# build and jump into an environment
|
325
|
+
entertask = Rummager::ContainerEnterTask.define_task :enter
|
326
|
+
entertask.container_name = @container_name
|
327
|
+
@enter_dep_jobs.each do |edj|
|
328
|
+
Rake::Task["containers:#{@container_name}:enter"].enhance([ :"containers:#{@container_name}:jobs:#{edj}" ])
|
329
|
+
end
|
330
|
+
Rake::Task["containers:#{@container_name}:enter"].enhance([ :"containers:#{@container_name}:start" ])
|
331
|
+
end # allow_enter
|
332
|
+
|
333
|
+
# stop task
|
334
|
+
stoptask = Rummager::ContainerStopTask.define_task :stop
|
335
|
+
stoptask.container_name = @container_name
|
336
|
+
Rake::Task[:"containers:stop"].enhance( [ :"containers:#{@container_name}:stop" ] )
|
337
|
+
|
338
|
+
# Remove task
|
339
|
+
rmtask = Rummager::ContainerRMTask.define_task :rm
|
340
|
+
rmtask.container_name = @container_name
|
341
|
+
Rake::Task["images:#{@image_name}:rmi"].enhance( [ "containers:#{@container_name}:rm" ] )
|
342
|
+
Rake::Task["containers:#{@container_name}:rm"].enhance( [ :"containers:#{@container_name}:stop" ] )
|
343
|
+
|
344
|
+
if @noclean == true
|
345
|
+
Rake::Task[:"containers:clobber"].enhance( [ :"containers:#{@container_name}:rm" ] )
|
346
|
+
else
|
347
|
+
Rake::Task[:"containers:clean"].enhance( [ :"containers:#{@container_name}:rm" ] )
|
348
|
+
end
|
349
|
+
|
350
|
+
end # namespace @container_name
|
351
|
+
end # namespace "containers"
|
352
|
+
|
353
|
+
Rake::Task["containers:#{@container_name}:rm"].enhance( [ :"containers:#{@container_name}:stop" ] )
|
354
|
+
Rake::Task[:"containers:stop"].enhance( [ :"containers:#{@container_name}:stop" ] )
|
355
|
+
|
356
|
+
end # define
|
357
|
+
end # class ClickContainer
|
358
|
+
|
359
|
+
|
360
|
+
class ContainerExecTask < Rummager::ContainerTaskBase
|
361
|
+
attr_accessor :exec_list
|
362
|
+
attr_accessor :ident_hash
|
363
|
+
|
364
|
+
def ident_filename
|
365
|
+
"/.once-#{@ident_hash}"
|
366
|
+
end
|
367
|
+
|
368
|
+
def needed?
|
369
|
+
if ! @ident_hash.nil?
|
370
|
+
puts "checking for #{ident_filename} in container"
|
371
|
+
begin
|
372
|
+
docker_obj.copy("#{ident_filename}")
|
373
|
+
return false
|
374
|
+
rescue
|
375
|
+
puts "#{ident_filename} not found"
|
376
|
+
end
|
377
|
+
end
|
378
|
+
# no ident hash, or not found
|
379
|
+
true
|
380
|
+
end
|
381
|
+
|
382
|
+
def initialize(task_name, app)
|
383
|
+
super(task_name,app)
|
384
|
+
@actions << Proc.new {
|
385
|
+
|
386
|
+
@exec_list.each do |e|
|
387
|
+
|
388
|
+
hide_output = e.delete(:hide_output)
|
389
|
+
cmd = e.delete(:cmd)
|
390
|
+
restart_after = e.delete(:restart_after)
|
391
|
+
|
392
|
+
if hide_output == true
|
393
|
+
docker_obj.exec(cmd)
|
394
|
+
else
|
395
|
+
docker_obj.exec(cmd) { |stream,chunk| puts "#{chunk}" }
|
396
|
+
end
|
397
|
+
|
398
|
+
if restart_after==true
|
399
|
+
puts "exec item requires container restart" if Rake.verbose == true
|
400
|
+
docker_obj.restart()
|
401
|
+
end
|
402
|
+
|
403
|
+
end # @exec_list.each
|
404
|
+
|
405
|
+
if ! @ident_hash.nil?
|
406
|
+
puts "marking #{task_name} completed: #{ident_filename}" if Rake.verbose == true
|
407
|
+
docker_obj.exec(["/usr/bin/sudo","/usr/bin/touch","#{ident_filename}"])
|
408
|
+
end
|
409
|
+
|
410
|
+
}
|
411
|
+
|
412
|
+
end # initialize
|
413
|
+
|
414
|
+
end # class ContainerExecTask
|
415
|
+
|
416
|
+
|
417
|
+
class ClickCntnrExec < Rake::TaskLib
|
418
|
+
attr_accessor :job_name
|
419
|
+
attr_accessor :container_name
|
420
|
+
attr_accessor :exec_list
|
421
|
+
attr_accessor :ident_hash
|
422
|
+
attr_accessor :dep_jobs
|
423
|
+
|
424
|
+
def initialize(job_name,args={})
|
425
|
+
@job_name = job_name
|
426
|
+
if !args.delete(:run_always)
|
427
|
+
@ident_hash = Digest::MD5.hexdigest(args.to_s)
|
428
|
+
puts "#{job_name} ident: #{@ident_hash}" if Rake.verbose == true
|
429
|
+
end
|
430
|
+
|
431
|
+
@container_name = args.delete(:container_name)
|
432
|
+
if !defined? @container_name
|
433
|
+
raise ArgumentError, "ClickContainer'#{@job_name}' missing comtainer_name:#{args}"
|
434
|
+
end
|
435
|
+
@exec_list = args.delete(:exec_list)
|
436
|
+
@dep_jobs = args.delete(:dep_jobs)
|
437
|
+
if !args.empty?
|
438
|
+
raise ArgumentError, "ClickExec'#{@job_name}' defenition has unused/invalid key-values:#{args}"
|
439
|
+
end
|
440
|
+
yield self if block_given?
|
441
|
+
define
|
442
|
+
end # initialize
|
443
|
+
|
444
|
+
|
445
|
+
def define
|
446
|
+
|
447
|
+
namespace "containers" do
|
448
|
+
namespace @container_name do
|
449
|
+
namespace "jobs" do
|
450
|
+
|
451
|
+
exectask = Rummager::ContainerExecTask.define_task :"#{job_name}"
|
452
|
+
exectask.container_name = @container_name
|
453
|
+
exectask.exec_list = @exec_list
|
454
|
+
exectask.ident_hash = @ident_hash
|
455
|
+
Rake::Task[:"containers:#{@container_name}:jobs:#{job_name}"].enhance( [:"containers:#{@container_name}:start"] )
|
456
|
+
if @dep_jobs
|
457
|
+
@dep_jobs.each do |dj|
|
458
|
+
Rake::Task["containers:#{@container_name}:jobs:#{job_name}"].enhance([ :"containers:#{@container_name}:jobs:#{dj}" ])
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
end # namespave "jobs"
|
463
|
+
end # namespace @container_name
|
464
|
+
end # namespace "containers"
|
465
|
+
|
466
|
+
end # define
|
467
|
+
|
468
|
+
end # class ClickCntnrExec
|
469
|
+
|
470
|
+
|
471
|
+
end # module Rummager
|
472
|
+
|
473
|
+
__END__
|
@@ -0,0 +1,223 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'logger'
|
3
|
+
require 'rake/tasklib'
|
4
|
+
require 'docker'
|
5
|
+
require 'date'
|
6
|
+
require 'digest'
|
7
|
+
require 'json'
|
8
|
+
require 'excon'
|
9
|
+
require 'time'
|
10
|
+
require 'rummager/util'
|
11
|
+
|
12
|
+
# Create and describe generic image operation targets
|
13
|
+
namespace :images do
|
14
|
+
desc "Build all Docker images"
|
15
|
+
task :"build"
|
16
|
+
|
17
|
+
desc "Remove temporary images"
|
18
|
+
task :"clean" => [ :"containers:clean" ]
|
19
|
+
|
20
|
+
desc "Remove all Docker images"
|
21
|
+
task :"clobber" => [ :"containers:clobber", :"images:clean" ]
|
22
|
+
end
|
23
|
+
|
24
|
+
###########################################################
|
25
|
+
##
|
26
|
+
## Image Handling Pieces
|
27
|
+
##
|
28
|
+
###########################################################
|
29
|
+
|
30
|
+
# Abstract base class for Image handling tasks
|
31
|
+
class Rummager::ImageTaskBase < Rake::Task
|
32
|
+
attr_accessor :repo
|
33
|
+
attr_accessor :tag
|
34
|
+
|
35
|
+
def has_repo?
|
36
|
+
Docker::Image.all(:all => true).any? { |image| image.info['RepoTags'].any? { |s| s.include?(@repo) } }
|
37
|
+
end
|
38
|
+
|
39
|
+
end # ImageTaskBase
|
40
|
+
|
41
|
+
# Image build tasks
|
42
|
+
class Rummager::ImageBuildTask < Rummager::ImageTaskBase
|
43
|
+
attr_accessor :source
|
44
|
+
attr_accessor :add_files
|
45
|
+
|
46
|
+
IMG_BUILD_ARGS = {
|
47
|
+
:forcerm => true,
|
48
|
+
:rm => true,
|
49
|
+
}
|
50
|
+
|
51
|
+
def fingerprint_sources
|
52
|
+
content = nil
|
53
|
+
# grab the source string or source directory as the content
|
54
|
+
if @source.is_a?( String )
|
55
|
+
content = @source
|
56
|
+
# include any added files in the hash
|
57
|
+
if @add_files
|
58
|
+
@add_files.each { |af| content << IO.read(af) }
|
59
|
+
end
|
60
|
+
elsif source.is_a?( Dir )
|
61
|
+
files = Dir["#{source.path}/**/*"].reject{ |f| File.directory?(f) }
|
62
|
+
content = files.map{|f| File.read(f)}.join
|
63
|
+
else
|
64
|
+
raise ArgumentError, "Unhandled argument: #{source.to_s} is not a String or Dir"
|
65
|
+
end
|
66
|
+
# return an MD5 hash sum
|
67
|
+
Digest::MD5.hexdigest(content)
|
68
|
+
end
|
69
|
+
|
70
|
+
def fingerprint
|
71
|
+
@fingerprint ||= fingerprint_sources
|
72
|
+
end
|
73
|
+
|
74
|
+
def needed?
|
75
|
+
!Docker::Image.all(:all => true).any? { |image| image.info['RepoTags'].any? { |s| s.include?("#{@repo}:#{fingerprint}") } }
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize(task_name, app)
|
79
|
+
super(task_name,app)
|
80
|
+
@build_args = IMG_BUILD_ARGS
|
81
|
+
@actions << Proc.new {
|
82
|
+
@build_args[:'t'] = "#{@repo}:#{fingerprint}"
|
83
|
+
puts "Image '#{@repo}:#{fingerprint}' begin build"
|
84
|
+
|
85
|
+
if @source.is_a?( Dir )
|
86
|
+
puts "building from dir '#{Dir.to_s}'"
|
87
|
+
newimage = Docker::Image.build_from_dir( @source, @build_args ) do |c|
|
88
|
+
begin
|
89
|
+
print JSON(c)['stream']
|
90
|
+
rescue
|
91
|
+
print "WARN JSON parse error with:" + c
|
92
|
+
end
|
93
|
+
end
|
94
|
+
else
|
95
|
+
puts "building from string/tar" if Rake.verbose == true
|
96
|
+
tarout = StringIO.new
|
97
|
+
Gem::Package::TarWriter.new(tarout) do |tarin|
|
98
|
+
tarin.add_file('Dockerfile',0640) { |tf| tf.write(@source) }
|
99
|
+
if @add_files
|
100
|
+
@add_files.each do | af |
|
101
|
+
puts "add:#{af} to tarfile" if Rake.verbose == true
|
102
|
+
tarin.add_file( af, 0640 ) { |tf| tf.write(IO.read(af)) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
newimage = Docker::Image.build_from_tar( tarout.tap(&:rewind), @build_args ) do |c|
|
108
|
+
begin
|
109
|
+
print JSON(c)['stream']
|
110
|
+
rescue
|
111
|
+
print "WARN JSON parse error with:" + c
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
newimage.tag( 'repo' => @repo,
|
116
|
+
'tag' => 'latest',
|
117
|
+
'force' => true )
|
118
|
+
puts "Image '#{@repo}': build complete"
|
119
|
+
puts "#{@build_args} -> #{newimage.json}" if Rake.verbose == true
|
120
|
+
}
|
121
|
+
end #initialize
|
122
|
+
|
123
|
+
end #ImageBuildTask
|
124
|
+
|
125
|
+
|
126
|
+
# Image removal tasks
|
127
|
+
class Rummager::ImageRMITask < Rummager::ImageTaskBase
|
128
|
+
|
129
|
+
def needed?
|
130
|
+
has_repo?
|
131
|
+
end
|
132
|
+
|
133
|
+
def initialize(task_name, app)
|
134
|
+
super(task_name,app)
|
135
|
+
@actions << Proc.new {
|
136
|
+
puts "removing image '#{t.repo}'" if Rake.verbose == true
|
137
|
+
Docker::Image.all(:all => true).each do |img|
|
138
|
+
if img.info['RepoTags'].any? { |s| s.include?(@repo) }
|
139
|
+
begin
|
140
|
+
img.delete(:force => true)
|
141
|
+
rescue Exception => e
|
142
|
+
puts "exception: #{e.message}" if Rake.verbose == true
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end #each
|
146
|
+
}
|
147
|
+
end # initialize
|
148
|
+
|
149
|
+
end #DockerImageRMITask
|
150
|
+
|
151
|
+
|
152
|
+
# Template to generate tasks for Docker Images
|
153
|
+
class Rummager::ClickImage < Rake::TaskLib
|
154
|
+
|
155
|
+
attr_accessor :image_name
|
156
|
+
attr_accessor :repo_base
|
157
|
+
attr_accessor :source
|
158
|
+
attr_accessor :add_files
|
159
|
+
attr_accessor :dep_image
|
160
|
+
attr_accessor :dep_other
|
161
|
+
attr_accessor :noclean
|
162
|
+
attr_accessor :image_args
|
163
|
+
|
164
|
+
def initialize(image_name,args={})
|
165
|
+
@image_name = image_name
|
166
|
+
@repo_base = args.delete(:repo_base) || Rummager.repo_base
|
167
|
+
@source = args.delete(:source)
|
168
|
+
@add_files = args.delete(:add_files)
|
169
|
+
if @source.nil?
|
170
|
+
@source = Dir.new("./#{@image_name}/")
|
171
|
+
puts "no direct source, use #{@source.path}" if Rake.verbose == true
|
172
|
+
end
|
173
|
+
@dep_image = args.delete(:dep_image)
|
174
|
+
@dep_other = args.delete(:dep_other)
|
175
|
+
@noclean = args.delete(:noclean)
|
176
|
+
@image_args = args
|
177
|
+
@image_args[:repo] = "#{@repo_base}/#{image_name}"
|
178
|
+
puts "repo for #{@image_name} will be #{@image_args[:repo]}" if Rake.verbose == true
|
179
|
+
@image_args[:tag] ||= 'latest'
|
180
|
+
yield self if block_given?
|
181
|
+
define
|
182
|
+
end
|
183
|
+
|
184
|
+
def define
|
185
|
+
namespace "images" do
|
186
|
+
namespace @image_name do
|
187
|
+
|
188
|
+
rmitask = Rummager::ImageRMITask.define_task :rmi
|
189
|
+
rmitask.repo = "#{@image_args[:repo]}"
|
190
|
+
rmitask.tag = 'latest'
|
191
|
+
|
192
|
+
buildtask = Rummager::ImageBuildTask.define_task :build
|
193
|
+
buildtask.source = @source
|
194
|
+
buildtask.repo = @image_args[:repo]
|
195
|
+
buildtask.tag = buildtask.fingerprint
|
196
|
+
buildtask.add_files = @add_files
|
197
|
+
|
198
|
+
end # namespace
|
199
|
+
end # namespace
|
200
|
+
|
201
|
+
if @dep_image
|
202
|
+
puts "'#{@image_name}' is dependent on '#{@dep_image}'" if Rake.verbose == true
|
203
|
+
# forward prereq for build
|
204
|
+
Rake::Task[:"images:#{@image_name}:build"].enhance( [ :"images:#{@dep_image}:build" ] )
|
205
|
+
# reverse prereq on parent image for delete
|
206
|
+
Rake::Task[:"images:#{@dep_image}:rmi"].enhance(["images:#{@image_name}:rmi"])
|
207
|
+
end
|
208
|
+
if @dep_other
|
209
|
+
Rake.Task[:"images:#{@image_name}"].enhance( @dep_other )
|
210
|
+
end
|
211
|
+
|
212
|
+
if @noclean
|
213
|
+
Rake::Task[:"images:clobber"].enhance( [ :"images:#{@image_name}:rmi"] )
|
214
|
+
else
|
215
|
+
Rake::Task[:"images:clean"].enhance( [ :"images:#{@image_name}:rmi"] )
|
216
|
+
end
|
217
|
+
|
218
|
+
end # ClickImage.define
|
219
|
+
|
220
|
+
end # class ClickImage
|
221
|
+
|
222
|
+
|
223
|
+
__END__
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Rummager
|
2
|
+
|
3
|
+
def Rummager.cmd_gitmirror(filepath,giturl)
|
4
|
+
{
|
5
|
+
:cmd=> [ "/bin/bash","-c",
|
6
|
+
"if [[ -d #{filepath} ]]; then\n" \
|
7
|
+
" /usr/bin/git --git-dir=#{filepath} fetch --all\n" \
|
8
|
+
"else\n" \
|
9
|
+
" /usr/bin/git clone --mirror #{giturl} #{filepath}\n" \
|
10
|
+
"fi\n"],
|
11
|
+
}
|
12
|
+
end # mirror_or_update
|
13
|
+
|
14
|
+
def Rummager.cmd_gitupdate(filepath)
|
15
|
+
{
|
16
|
+
:cmd=> [ "/bin/bash","-c",
|
17
|
+
"/usr/bin/git --git-dir=#{filepath} fetch --all",
|
18
|
+
],
|
19
|
+
}
|
20
|
+
end # cmd_gitupdate
|
21
|
+
|
22
|
+
def Rummager.cmd_gitclone(branch,srcpath,clonepath)
|
23
|
+
{
|
24
|
+
:cmd => ["/bin/sh","-c",
|
25
|
+
"/usr/bin/git clone --branch #{branch} #{srcpath} #{clonepath}"
|
26
|
+
],
|
27
|
+
}
|
28
|
+
end # cmd_gitclone
|
29
|
+
|
30
|
+
def Rummager.cmd_gitcheckout(commithash,srcpath,clonepath)
|
31
|
+
{
|
32
|
+
:cmd => ["/bin/sh","-c",
|
33
|
+
"/usr/bin/git clone --no-checkout #{srcpath} #{clonepath}\n" \
|
34
|
+
"/usr/bin/git --work-tree #{clonepath} --git-dir #{clonepath}/.git checkout #{commithash}\n"
|
35
|
+
],
|
36
|
+
}
|
37
|
+
end # cmd_gitclone
|
38
|
+
|
39
|
+
end # Rummager
|
metadata
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rummager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- y3ddet, ted@xassembly.com
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 10.3.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 10.3.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: logger
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.2.8
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.2.8
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.7.7
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.7.7
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: docker-api
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.21.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.21.0
|
69
|
+
description: Rake integration with docker-api
|
70
|
+
email: ted@xassembly.com
|
71
|
+
executables: []
|
72
|
+
extensions: []
|
73
|
+
extra_rdoc_files: []
|
74
|
+
files:
|
75
|
+
- lib/rummager.rb
|
76
|
+
- lib/rummager/containers.rb
|
77
|
+
- lib/rummager/images.rb
|
78
|
+
- lib/rummager/util.rb
|
79
|
+
homepage: https://github.com/exactassembly/rummager
|
80
|
+
licenses:
|
81
|
+
- GPLv2
|
82
|
+
metadata: {}
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
requirements: []
|
98
|
+
rubyforge_project:
|
99
|
+
rubygems_version: 2.0.14
|
100
|
+
signing_key:
|
101
|
+
specification_version: 4
|
102
|
+
summary: Rummager
|
103
|
+
test_files: []
|