rummager 0.5.3
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/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: []
|