rq 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/DEPENDS +5 -0
- data/HISTORY +26 -0
- data/README +552 -0
- data/TODO +13 -0
- data/VERSION +1 -0
- data/bin/rq +391 -0
- data/bin/rq-0.1.7 +410 -0
- data/install.rb +143 -0
- data/lib/rq-0.1.7.rb +82 -0
- data/lib/rq-0.1.7/backer.rb +27 -0
- data/lib/rq-0.1.7/configfile.rb +78 -0
- data/lib/rq-0.1.7/configurator.rb +36 -0
- data/lib/rq-0.1.7/creator.rb +23 -0
- data/lib/rq-0.1.7/defaultconfig.txt +5 -0
- data/lib/rq-0.1.7/deleter.rb +39 -0
- data/lib/rq-0.1.7/executor.rb +41 -0
- data/lib/rq-0.1.7/feeder.rb +367 -0
- data/lib/rq-0.1.7/job.rb +51 -0
- data/lib/rq-0.1.7/jobqueue.rb +432 -0
- data/lib/rq-0.1.7/jobrunner.rb +63 -0
- data/lib/rq-0.1.7/jobrunnerdaemon.rb +179 -0
- data/lib/rq-0.1.7/lister.rb +22 -0
- data/lib/rq-0.1.7/locker.rb +37 -0
- data/lib/rq-0.1.7/logging.rb +117 -0
- data/lib/rq-0.1.7/mainhelper.rb +53 -0
- data/lib/rq-0.1.7/qdb.rb +634 -0
- data/lib/rq-0.1.7/querier.rb +33 -0
- data/lib/rq-0.1.7/refresher.rb +72 -0
- data/lib/rq-0.1.7/sleepcycle.rb +46 -0
- data/lib/rq-0.1.7/snapshotter.rb +25 -0
- data/lib/rq-0.1.7/statuslister.rb +22 -0
- data/lib/rq-0.1.7/submitter.rb +90 -0
- data/lib/rq-0.1.7/updater.rb +95 -0
- data/lib/rq-0.1.7/usage.rb +609 -0
- data/lib/rq-0.1.7/util.rb +286 -0
- data/lib/rq.rb +84 -0
- data/rdoc.cmd +2 -0
- data/rq +2 -0
- data/rq.gemspec +36 -0
- data/rq.help +552 -0
- data/white_box/crontab +2 -0
- data/white_box/killrq +18 -0
- data/white_box/rq_killer +27 -0
- metadata +126 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
unless defined? $__rq_jobrunner__
|
2
|
+
module RQ
|
3
|
+
#{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require 'drb/drb'
|
8
|
+
|
9
|
+
require LIBDIR + 'util'
|
10
|
+
|
11
|
+
class JobRunner
|
12
|
+
#{{{
|
13
|
+
include DRbUndumped
|
14
|
+
attr :job
|
15
|
+
attr :jid
|
16
|
+
attr :cid
|
17
|
+
attr :shell
|
18
|
+
attr :command
|
19
|
+
alias pid cid
|
20
|
+
def initialize job
|
21
|
+
#{{{
|
22
|
+
@job = job
|
23
|
+
@jid = job['jid']
|
24
|
+
@command = job['command']
|
25
|
+
@shell = job['shell'] || 'bash'
|
26
|
+
@r,@w = IO::pipe
|
27
|
+
@env = {}
|
28
|
+
@job.fields.each do |field|
|
29
|
+
key = "RQ_#{ field }".upcase.gsub(%r/\s+/,'_')
|
30
|
+
val = @job[field]
|
31
|
+
@env[key] = "#{ val }"
|
32
|
+
end
|
33
|
+
@cid =
|
34
|
+
Util::fork do
|
35
|
+
begin
|
36
|
+
@env.each{|k,v| ENV[k] = v}
|
37
|
+
ENV['RQ_PID'] = "#{ $$ }"
|
38
|
+
@w.close
|
39
|
+
STDIN.reopen @r
|
40
|
+
if File::basename(@shell) == 'bash' || File::basename(@shell) == 'sh'
|
41
|
+
exec [@shell, "__rq_job__#{ @jid }__#{ File::basename(@shell) }__"], '--login'
|
42
|
+
else
|
43
|
+
exec [@shell, "__rq_job__#{ @jid }__#{ File::basename(@shell) }__"], '-l'
|
44
|
+
end
|
45
|
+
rescue Exception => e
|
46
|
+
STDERR.puts(Util::errmsg(e))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@r.close
|
50
|
+
#}}}
|
51
|
+
end
|
52
|
+
def run
|
53
|
+
#{{{
|
54
|
+
@w.puts @command
|
55
|
+
@w.close
|
56
|
+
#}}}
|
57
|
+
end
|
58
|
+
#}}}
|
59
|
+
end # class JobRunner
|
60
|
+
#}}}
|
61
|
+
end # module RQ
|
62
|
+
$__rq_jobrunner__ = __FILE__
|
63
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
unless defined? $__rq_jobrunnerdaemon__
|
2
|
+
module RQ
|
3
|
+
#{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require 'drb/drb'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'tmpdir'
|
10
|
+
require 'tempfile'
|
11
|
+
|
12
|
+
require LIBDIR + 'job'
|
13
|
+
require LIBDIR + 'jobrunner'
|
14
|
+
|
15
|
+
class JobRunnerDaemon
|
16
|
+
#{{{
|
17
|
+
include Logging
|
18
|
+
|
19
|
+
class << self
|
20
|
+
#{{{
|
21
|
+
def daemon(*a,&b)
|
22
|
+
#{{{
|
23
|
+
jrd = new(*a, &b)
|
24
|
+
|
25
|
+
r, w = IO::pipe
|
26
|
+
|
27
|
+
unless((pid = fork)) # child
|
28
|
+
$0 = "#{ self }".gsub(%r/[^a-zA-Z]+/,'_').downcase
|
29
|
+
begin
|
30
|
+
r.close
|
31
|
+
n = 0
|
32
|
+
uri = nil
|
33
|
+
socket = nil
|
34
|
+
|
35
|
+
42.times do
|
36
|
+
begin
|
37
|
+
s = "%s/%s_%s_%s_%s" %
|
38
|
+
[Dir::tmpdir, File::basename($0), Process::ppid, n, rand(42)]
|
39
|
+
u = "drbunix://#{ s }"
|
40
|
+
DRb::start_service u, jrd
|
41
|
+
socket = s
|
42
|
+
uri = u
|
43
|
+
rescue Errno::EADDRINUSE
|
44
|
+
n += 1
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
if socket and uri
|
49
|
+
w.write socket
|
50
|
+
w.close
|
51
|
+
pid = Process::pid
|
52
|
+
ppid = Process::ppid
|
53
|
+
cur = Thread::current
|
54
|
+
Thread::new(pid, ppid, cur) do |pid, ppid, cur|
|
55
|
+
loop do
|
56
|
+
begin
|
57
|
+
Process::kill 0, ppid
|
58
|
+
sleep 42
|
59
|
+
rescue
|
60
|
+
cur.raise "parent <#{ ppid }> died unexpectedly"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
DRb::thread.join
|
65
|
+
else
|
66
|
+
w.close
|
67
|
+
end
|
68
|
+
ensure
|
69
|
+
exit!
|
70
|
+
end
|
71
|
+
else # parent
|
72
|
+
w.close
|
73
|
+
socket = r.read
|
74
|
+
r.close
|
75
|
+
|
76
|
+
if socket and File::exist?(socket)
|
77
|
+
at_exit{ FileUtils::rm_f socket }
|
78
|
+
uri = "drbunix://#{ socket }"
|
79
|
+
DRb::start_service 'druby://localhost:0', nil
|
80
|
+
jrd = DRbObject::new nil, uri
|
81
|
+
jrd.pid = pid
|
82
|
+
jrd.uri = uri
|
83
|
+
else
|
84
|
+
raise "failed to start job runner daemon"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
return jrd
|
89
|
+
#}}}
|
90
|
+
end
|
91
|
+
#}}}
|
92
|
+
end
|
93
|
+
attr :runners
|
94
|
+
attr :pid, true
|
95
|
+
attr :uri, true
|
96
|
+
def initialize
|
97
|
+
#{{{
|
98
|
+
@runners = {}
|
99
|
+
@uri = nil
|
100
|
+
@pid = Process::pid
|
101
|
+
#}}}
|
102
|
+
end
|
103
|
+
def runner job
|
104
|
+
#{{{
|
105
|
+
r = nil
|
106
|
+
retried = false
|
107
|
+
begin
|
108
|
+
r = JobRunner::new job
|
109
|
+
rescue Errno::ENOMEM, Errno::EAGAIN
|
110
|
+
GC::start
|
111
|
+
unless retried
|
112
|
+
retried = true
|
113
|
+
retry
|
114
|
+
else
|
115
|
+
raise
|
116
|
+
end
|
117
|
+
end
|
118
|
+
@runners[r.pid] = r
|
119
|
+
r
|
120
|
+
#}}}
|
121
|
+
end
|
122
|
+
def wait
|
123
|
+
#{{{
|
124
|
+
pid = Process::wait
|
125
|
+
@runners.delete pid
|
126
|
+
pid
|
127
|
+
#}}}
|
128
|
+
end
|
129
|
+
def wait2
|
130
|
+
#{{{
|
131
|
+
pid, status = Process::wait2
|
132
|
+
@runners.delete pid
|
133
|
+
[pid, status]
|
134
|
+
#}}}
|
135
|
+
end
|
136
|
+
def waitpid pid = -1, flags = 0
|
137
|
+
#{{{
|
138
|
+
pid = pid.pid if pid.respond_to? 'pid'
|
139
|
+
pid = Process::waitpid pid, flags
|
140
|
+
@runners.delete pid
|
141
|
+
pid
|
142
|
+
#}}}
|
143
|
+
end
|
144
|
+
def waitpid2 pid = -1, flags = 0
|
145
|
+
#{{{
|
146
|
+
pid = pid.pid if pid.respond_to? 'pid'
|
147
|
+
pid, status = Process::waitpid2 pid, flags
|
148
|
+
@runners.delete pid
|
149
|
+
[pid, status]
|
150
|
+
#}}}
|
151
|
+
end
|
152
|
+
def shutdown
|
153
|
+
#{{{
|
154
|
+
@death =
|
155
|
+
Thread::new do
|
156
|
+
begin
|
157
|
+
while not @runners.empty?
|
158
|
+
pid = Process::wait
|
159
|
+
@runners.delete pid
|
160
|
+
end
|
161
|
+
ensure
|
162
|
+
sleep 4.2
|
163
|
+
DRb::thread.kill
|
164
|
+
Thread::main exit!
|
165
|
+
end
|
166
|
+
end
|
167
|
+
#}}}
|
168
|
+
end
|
169
|
+
def install_signal_handlers
|
170
|
+
#{{{
|
171
|
+
%w(TERM INT HUP).each{|sig| trap sig, 'SIG_IGN'}
|
172
|
+
#}}}
|
173
|
+
end
|
174
|
+
#}}}
|
175
|
+
end # class JobRunnerDaemon
|
176
|
+
#}}}
|
177
|
+
end # module RQ
|
178
|
+
$__rq_jobrunnerdaemon__ = __FILE__
|
179
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
unless defined? $__rq_lister__
|
2
|
+
module RQ
|
3
|
+
#{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require LIBDIR + 'mainhelper'
|
8
|
+
|
9
|
+
class Lister < MainHelper
|
10
|
+
#{{{
|
11
|
+
def list
|
12
|
+
#{{{
|
13
|
+
set_q
|
14
|
+
@q.list(*@argv)
|
15
|
+
#}}}
|
16
|
+
end
|
17
|
+
#}}}
|
18
|
+
end # class Lister
|
19
|
+
#}}}
|
20
|
+
end # module RQ
|
21
|
+
$__rq_lister__ = __FILE__
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
unless defined? $__rq_locker__
|
2
|
+
module RQ
|
3
|
+
#{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require LIBDIR + 'util'
|
8
|
+
require LIBDIR + 'locker'
|
9
|
+
|
10
|
+
class Locker < MainHelper
|
11
|
+
#{{{
|
12
|
+
def lock
|
13
|
+
#{{{
|
14
|
+
set_q
|
15
|
+
ltype = @argv.shift
|
16
|
+
debug{ "ltype <#{ ltype }>" }
|
17
|
+
read_only =
|
18
|
+
case ltype
|
19
|
+
when /^\s*r(?:ead)?|^\s*sh(?:ared)?/io
|
20
|
+
true
|
21
|
+
when /^\s*w(?:rite)?|^\s*ex(?:clusive)?/io
|
22
|
+
false
|
23
|
+
else
|
24
|
+
raise "lock type must be one of (r)ead|(sh)ared|(w)rite|(ex)clusive, not <#{ ltype }>"
|
25
|
+
end
|
26
|
+
cmd = @argv.join(' ').strip
|
27
|
+
raise "no command given for lock type <#{ ltype }>" if cmd.empty?
|
28
|
+
debug{ "cmd <#{ cmd }>" }
|
29
|
+
@q.lock(:read_only => read_only){ Util::system cmd }
|
30
|
+
#}}}
|
31
|
+
end
|
32
|
+
#}}}
|
33
|
+
end # class Locker
|
34
|
+
#}}}
|
35
|
+
end # module RQ
|
36
|
+
$__rq_locker__ = __FILE__
|
37
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
unless defined? $__rq_logging__
|
2
|
+
module RQ
|
3
|
+
#{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require "logger"
|
8
|
+
|
9
|
+
require LIBDIR + 'util'
|
10
|
+
#
|
11
|
+
# module which adds logging methods to classes
|
12
|
+
#
|
13
|
+
module Logging
|
14
|
+
#{{{
|
15
|
+
#
|
16
|
+
# a module that adds an accessor to Logging objects in ored to fix a bug where
|
17
|
+
# not all logging devices are put into sync mode, resulting in improper log
|
18
|
+
# rolling. this is a hack.
|
19
|
+
#
|
20
|
+
module LoggerExt
|
21
|
+
#{{{
|
22
|
+
attr :logdev
|
23
|
+
#}}}
|
24
|
+
end # module LoggerExt
|
25
|
+
#
|
26
|
+
# implementations of the methods shared by both classes and objects of classes
|
27
|
+
# which include Logging
|
28
|
+
#
|
29
|
+
module LogMethods
|
30
|
+
#{{{
|
31
|
+
def logger
|
32
|
+
#{{{
|
33
|
+
if defined?(@logger) and @logger
|
34
|
+
@logger
|
35
|
+
else
|
36
|
+
if Class === self
|
37
|
+
@logger = self.default_logger
|
38
|
+
else
|
39
|
+
@logger = self::class::logger
|
40
|
+
end
|
41
|
+
raise "@logger is undefined!" unless defined?(@logger) and @logger
|
42
|
+
@logger
|
43
|
+
end
|
44
|
+
#}}}
|
45
|
+
end
|
46
|
+
def logger= log
|
47
|
+
#{{{
|
48
|
+
@logger = log
|
49
|
+
@logger.extend LoggerExt
|
50
|
+
@logger.logdev.dev.sync = true
|
51
|
+
@logger
|
52
|
+
#}}}
|
53
|
+
end
|
54
|
+
def debug(*args, &block); logger.debug(*args, &block); end
|
55
|
+
def info(*args, &block); logger.info(*args, &block) ; end
|
56
|
+
def warn(*args, &block); logger.warn(*args, &block) ; end
|
57
|
+
def error(*args, &block); logger.error(*args, &block); end
|
58
|
+
def fatal(*args, &block); logger.fatal(*args, &block); end
|
59
|
+
def logerr e
|
60
|
+
#{{{
|
61
|
+
if logger.debug?
|
62
|
+
error{ Util::errmsg e }
|
63
|
+
else
|
64
|
+
error{ Util::emsg e }
|
65
|
+
end
|
66
|
+
#}}}
|
67
|
+
end
|
68
|
+
#}}}
|
69
|
+
end # module LogMethods
|
70
|
+
EOL = "\n"
|
71
|
+
DIV0 = ("." * 79) << EOL
|
72
|
+
DIV1 = ("-" * 79) << EOL
|
73
|
+
DIV2 = ("=" * 79) << EOL
|
74
|
+
DIV3 = ("#" * 79) << EOL
|
75
|
+
SEC0 = ("." * 16) << EOL
|
76
|
+
SEC1 = ("-" * 16) << EOL
|
77
|
+
SEC2 = ("=" * 16) << EOL
|
78
|
+
SEC3 = ("#" * 16) << EOL
|
79
|
+
class << self
|
80
|
+
#{{{
|
81
|
+
def append_features c
|
82
|
+
#{{{
|
83
|
+
ret = super
|
84
|
+
c.extend LogMethods
|
85
|
+
class << c
|
86
|
+
def default_logger
|
87
|
+
#{{{
|
88
|
+
if defined?(@default_logger) and @default_logger
|
89
|
+
@default_logger
|
90
|
+
else
|
91
|
+
self.default_logger = Logger.new STDOUT
|
92
|
+
@default_logger.debug{ "<#{ self }> using default logger"}
|
93
|
+
@default_logger
|
94
|
+
end
|
95
|
+
#}}}
|
96
|
+
end
|
97
|
+
def default_logger= log
|
98
|
+
#{{{
|
99
|
+
@default_logger = log
|
100
|
+
@default_logger.extend LoggerExt
|
101
|
+
@default_logger.logdev.dev.sync = true
|
102
|
+
@default_logger
|
103
|
+
#}}}
|
104
|
+
end
|
105
|
+
end
|
106
|
+
ret
|
107
|
+
#}}}
|
108
|
+
end
|
109
|
+
#}}}
|
110
|
+
end
|
111
|
+
include LogMethods
|
112
|
+
#}}}
|
113
|
+
end # module Logging
|
114
|
+
#}}}
|
115
|
+
end # module rq
|
116
|
+
$__rq_logging__ = __FILE__
|
117
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
unless defined? $__rq_mainhelper__
|
2
|
+
module RQ
|
3
|
+
#{{{
|
4
|
+
LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
|
5
|
+
defined? LIBDIR
|
6
|
+
|
7
|
+
require LIBDIR + 'util'
|
8
|
+
require LIBDIR + 'logging'
|
9
|
+
|
10
|
+
class MainHelper
|
11
|
+
#{{{
|
12
|
+
include Util
|
13
|
+
include Logging
|
14
|
+
attr :main
|
15
|
+
attr :logger
|
16
|
+
attr :argv
|
17
|
+
attr :env
|
18
|
+
attr :cmd
|
19
|
+
attr :options
|
20
|
+
attr :qpath
|
21
|
+
attr :mode
|
22
|
+
attr :q
|
23
|
+
def initialize main
|
24
|
+
#{{{
|
25
|
+
@main = main
|
26
|
+
@logger = main.logger
|
27
|
+
@argv = main.argv
|
28
|
+
@env = main.env
|
29
|
+
@cmd = main.cmd
|
30
|
+
@options = main.options
|
31
|
+
@qpath = main.qpath
|
32
|
+
@mode = main.mode
|
33
|
+
@q = nil
|
34
|
+
#}}}
|
35
|
+
end
|
36
|
+
def set_q
|
37
|
+
#{{{
|
38
|
+
raise "q <#{ @qpath }> does not exist" unless test ?d, @qpath
|
39
|
+
@q = JobQueue::new @qpath, 'logger' => @logger
|
40
|
+
if @options['snapshot']
|
41
|
+
ss = "#{ $0 }_#{ Process::pid }_#{ Thread::current.id.abs }_#{ rand Time::now.to_i }".gsub(%r|/|o,'_')
|
42
|
+
qtmp = File::join Dir::tmpdir, ss
|
43
|
+
@q = @q.snapshot qtmp, @options['retries']
|
44
|
+
at_exit{ FileUtils::rm_rf qtmp }
|
45
|
+
end
|
46
|
+
#}}}
|
47
|
+
end
|
48
|
+
#}}}
|
49
|
+
end # class MainHelper
|
50
|
+
#}}}
|
51
|
+
end # module RQ
|
52
|
+
$__rq_mainhelper__ = __FILE__
|
53
|
+
end
|