lobster 0.1.0.alpha
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.
- data/Gemfile +5 -0
- data/Gemfile.lock +16 -0
- data/bin/lobster +25 -0
- data/lib/lobster/job.rb +52 -0
- data/lib/lobster/job_list.rb +55 -0
- data/lib/lobster/service.rb +65 -0
- data/lib/lobster/version.rb +3 -0
- data/lib/lobster.rb +9 -0
- data/lobster.gemspec +22 -0
- metadata +66 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/bin/lobster
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'lobster'
|
4
|
+
require 'daemons'
|
5
|
+
|
6
|
+
lobster_dir = File.expand_path(ENV['LOBSTER_DIR'] || '.')
|
7
|
+
log_dir = File.join(lobster_dir, 'log')
|
8
|
+
|
9
|
+
Dir.mkdir log_dir unless Dir.exist?(log_dir) || ARGV.first == "run"
|
10
|
+
|
11
|
+
#puts "LOBSTER_DIR set to #{lobster_dir}"
|
12
|
+
|
13
|
+
options = {
|
14
|
+
:multiple => false,
|
15
|
+
:monitor => true,
|
16
|
+
:backtrace => true,
|
17
|
+
:log_dir => File.join(lobster_dir, 'log'),
|
18
|
+
:log_output => true
|
19
|
+
}
|
20
|
+
options[:dir_mode] = :system unless ARGV.first == "run"
|
21
|
+
options[:log_dir] = File.join(lobster_dir, 'log')
|
22
|
+
|
23
|
+
Daemons.run_proc('lobster', options) do
|
24
|
+
Lobster::Service.start(lobster_dir)
|
25
|
+
end
|
data/lib/lobster/job.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
module Lobster
|
2
|
+
class Job
|
3
|
+
attr_accessor :next_run
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
@name = name
|
7
|
+
Lobster.logger.info "Job #{name} created."
|
8
|
+
@pid = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def reload(options)
|
12
|
+
options[:delay] ||= 10
|
13
|
+
|
14
|
+
if options[:command] != @command
|
15
|
+
Lobster.logger.info "Job command updated for #{@name}" if @command
|
16
|
+
@command = options.delete(:command)
|
17
|
+
end
|
18
|
+
|
19
|
+
if options[:delay] != @delay
|
20
|
+
Lobster.logger.info "Job delay updated for #{@name}" if @delay
|
21
|
+
@delay = options.delete(:delay)
|
22
|
+
@next_run = nil unless running?
|
23
|
+
end
|
24
|
+
|
25
|
+
@name ||= "<unnamed_job_#{command.hash.abs}>"
|
26
|
+
@next_run ||= Time.now + rand(@delay*60)
|
27
|
+
end
|
28
|
+
|
29
|
+
def running?
|
30
|
+
return false if @pid.nil?
|
31
|
+
if Process.wait @pid, Process::WNOHANG
|
32
|
+
Lobster.logger.error "Job #{@name} Failed with status #{$?}" unless $?.success?
|
33
|
+
@pid = nil
|
34
|
+
@next_run = Time.now + @delay*60
|
35
|
+
false
|
36
|
+
else
|
37
|
+
true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(out,err,dir)
|
42
|
+
Lobster.logger.info "starting job #{@name} from directory #{dir}"
|
43
|
+
begin
|
44
|
+
@pid = spawn(@command, :out=>out, :err=>err, :chdir=>dir)
|
45
|
+
Process.detach @pid
|
46
|
+
rescue Exception => e
|
47
|
+
Lobster.logger.error "#{e}: error when starting job #{@name}"
|
48
|
+
@next_run = Time.now + 10
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Lobster
|
2
|
+
class JobList
|
3
|
+
attr_accessor :jobs
|
4
|
+
|
5
|
+
def initialize(file)
|
6
|
+
@file = file
|
7
|
+
#@options = {}
|
8
|
+
@current_options = nil
|
9
|
+
@jobs = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def reload
|
13
|
+
@new_jobs = {}
|
14
|
+
#@new_options = {}
|
15
|
+
|
16
|
+
instance_eval(File.read(@file),@file)
|
17
|
+
|
18
|
+
# purely for logging
|
19
|
+
@jobs.each do |name, job|
|
20
|
+
Lobster.logger.info "Job #{name} deleted." unless @new_jobs[name]
|
21
|
+
end
|
22
|
+
#@options.each do |key, value|
|
23
|
+
# Lobster.logger.info "#{key} unset." unless @new_options[key]
|
24
|
+
#end
|
25
|
+
|
26
|
+
@jobs = @new_jobs
|
27
|
+
#@options = @new_options
|
28
|
+
end
|
29
|
+
|
30
|
+
def job(name)
|
31
|
+
@current_options = {}
|
32
|
+
yield
|
33
|
+
@new_jobs[name] ||= @jobs[name] || Job.new(name)
|
34
|
+
@new_jobs[name].reload(@current_options)
|
35
|
+
@current_options = nil
|
36
|
+
end
|
37
|
+
|
38
|
+
def cmd(command)
|
39
|
+
@current_options[:command] = command
|
40
|
+
end
|
41
|
+
|
42
|
+
def delay(delay)
|
43
|
+
@current_options[:delay] = delay
|
44
|
+
end
|
45
|
+
|
46
|
+
# def set(option, value)
|
47
|
+
# Lobster.logger.info "set #{option}=#{value}" if value != @options[option]
|
48
|
+
# @new_options[option] = value
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# def get(option)
|
52
|
+
# @options[option]
|
53
|
+
# end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Lobster
|
4
|
+
class Service
|
5
|
+
def self.start(dir)
|
6
|
+
Lobster.logger = Logger.new(STDOUT)
|
7
|
+
new(dir).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(dir)
|
11
|
+
@job_list = nil
|
12
|
+
@directory = dir || '.'
|
13
|
+
@file = File.join(@directory, 'config', 'schedule.rb')
|
14
|
+
@poll_delay = 60
|
15
|
+
|
16
|
+
rout, @wout = IO.pipe
|
17
|
+
rerr, @werr = IO.pipe
|
18
|
+
|
19
|
+
Thread.new do
|
20
|
+
while (line = rout.gets)
|
21
|
+
Lobster.logger.info "out: #{line}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Thread.new do
|
26
|
+
while (line = rerr.gets)
|
27
|
+
Lobster.logger.error "err: #{line}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Signal.trap "INT" do
|
32
|
+
Lobster.logger.info "All jobs are getting killed."
|
33
|
+
exit 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
Lobster.logger.info "Lobster started."
|
39
|
+
Lobster.logger.info "Schedule file: #{@file}"
|
40
|
+
Lobster.logger.info "Poll delay: #{@poll_delay}"
|
41
|
+
|
42
|
+
loop do
|
43
|
+
now = Time.now
|
44
|
+
|
45
|
+
reload_config
|
46
|
+
|
47
|
+
@job_list.jobs.each_value do |job|
|
48
|
+
if not job.running? and now >= job.next_run
|
49
|
+
job.run(@wout, @werr, @directory)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
sleep @poll_delay
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def reload_config
|
57
|
+
@job_list ||= JobList.new(@file)
|
58
|
+
begin
|
59
|
+
@job_list.reload
|
60
|
+
rescue Exception => e
|
61
|
+
Lobster.logger.error "#{e}: error while reading config file in #{@file}, not updating"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/lobster.rb
ADDED
data/lobster.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "lobster/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "lobster"
|
7
|
+
s.version = Lobster::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Maxime Brugidou"]
|
10
|
+
s.email = ["m.brugidou@criteo.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Simple loop job runner service.}
|
13
|
+
s.description = %q{}
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- test/{functional,unit}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_dependency "daemons", ">= 1.1.4"
|
20
|
+
|
21
|
+
#s.add_development_dependency "shoulda", ">= 2.1.1"
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lobster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.alpha
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Maxime Brugidou
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: daemons
|
16
|
+
requirement: &76767520 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.4
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *76767520
|
25
|
+
description: ''
|
26
|
+
email:
|
27
|
+
- m.brugidou@criteo.com
|
28
|
+
executables:
|
29
|
+
- lobster
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- Gemfile
|
34
|
+
- Gemfile.lock
|
35
|
+
- bin/lobster
|
36
|
+
- lib/lobster.rb
|
37
|
+
- lib/lobster/job.rb
|
38
|
+
- lib/lobster/job_list.rb
|
39
|
+
- lib/lobster/service.rb
|
40
|
+
- lib/lobster/version.rb
|
41
|
+
- lobster.gemspec
|
42
|
+
homepage: ''
|
43
|
+
licenses: []
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>'
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 1.3.1
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.11
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: Simple loop job runner service.
|
66
|
+
test_files: []
|