lobster 0.1.0.alpha2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lobster (0.1.0.alpha2)
4
+ lobster (0.1.0)
5
5
  daemons (>= 1.1.4)
6
6
 
7
7
  GEM
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2011 Criteo
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ Lobster
2
+ =======
3
+
4
+ Simple job service that was originally written to run hadoop jobs, but can be
5
+ used for any batch-processing scripts that you want to run.
6
+
7
+ Alpha Version Warning
8
+ ---------------------
9
+
10
+ Lobster has not been fully released and tested yet.
11
+
12
+ How It Works
13
+ ------------
14
+
15
+ ### Create a `config/schedule.rb` file
16
+
17
+ ~~~~~ ruby
18
+ job "my-job" do
19
+ cmd "runthis && runthat >> log/here.log"
20
+ delay 5 # minutes
21
+ end
22
+ ~~~~~
23
+
24
+ ### Run Lobster
25
+
26
+ - In the console with `LOBSTER_DIR=/path/to/jobs/directory lobster run`
27
+ - As a service with `LOBSTER_DIR=/path/to/jobs/directory sudo lobster start`
28
+
29
+ The job `my-job` will start in the next 5 minutes, and once it's done,
30
+ lobster will wait 5 minutes before starting it again.
31
+
32
+ Any log in stderr/stdout will be written in `$LOBSTER_DIR/log/lobster.output`
33
+ along with some other useful information for monitoring.
34
+
35
+ The current directory where the job command is run is `$LOBSTER_DIR`.
36
+
37
+ How To Install
38
+ --------------
39
+
40
+ gem install lobster --pre
41
+
42
+
data/bin/lobster CHANGED
@@ -1,25 +1,32 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'lobster'
4
+ require 'logger'
4
5
  require 'daemons'
5
6
 
6
7
  lobster_dir = File.expand_path(ENV['LOBSTER_DIR'] || '.')
7
- log_dir = File.join(lobster_dir, 'log')
8
+ lobster_env = ENV['LOBSTER_ENV'] || 'development'
8
9
 
9
- Dir.mkdir log_dir unless Dir.exist?(log_dir) || ARGV.first == "run"
10
+ Lobster.logger = Logger.new(STDOUT)
11
+ config = Lobster::Configuration.new(lobster_dir, lobster_env)
10
12
 
11
13
  #puts "LOBSTER_DIR set to #{lobster_dir}"
14
+ #puts "Config: #{config}"
15
+
16
+ log_dir = config[:log_dir]
17
+ Dir.mkdir log_dir unless Dir.exist?(log_dir) || ARGV.first == "run"
18
+
12
19
 
13
- options = {
20
+ daemon_options = {
14
21
  :multiple => false,
15
- :monitor => true,
22
+ :monitor => config[:monitor],
16
23
  :backtrace => true,
17
- :log_dir => File.join(lobster_dir, 'log'),
18
- :log_output => true
24
+ :log_dir => config[:log_dir],
25
+ :log_output => true,
26
+ :dir_mode => :normal,
27
+ :dir => config[:pid_dir]
19
28
  }
20
- options[:dir_mode] = :system unless ARGV.first == "run"
21
- options[:log_dir] = File.join(lobster_dir, 'log')
22
29
 
23
- Daemons.run_proc('lobster', options) do
24
- Lobster::Service.start(lobster_dir)
30
+ Daemons.run_proc('lobster', daemon_options) do
31
+ Lobster::Service.start(config)
25
32
  end
@@ -0,0 +1,39 @@
1
+ require 'yaml'
2
+
3
+ module Lobster
4
+ class Configuration
5
+ def initialize(dir, env)
6
+ @config = {
7
+ :monitor => true,
8
+ :log_dir => 'log',
9
+ :pid_dir => File.join('/', 'var', 'run', 'lobster'),
10
+ :schedule_file => File.join('config', 'schedule.rb')
11
+ }
12
+
13
+ config_file_path = File.join(dir, 'config', 'lobster.yml')
14
+ if File.exist? config_file_path
15
+ config_file = YAML.load_file(config_file_path)
16
+ @config.keys.each do |key|
17
+ @config[key] = config_file[env][key.to_s] || config_file[key.to_s] || @config[key]
18
+ end
19
+ end
20
+
21
+ # these keys cannot be set by the config file
22
+ @config[:lobster_dir] = dir
23
+ @config[:environment] = env
24
+
25
+ # make paths absolute
26
+ [:log_dir, :pid_dir, :schedule_file].each do |k|
27
+ @config[k] = File.absolute_path(@config[k], dir)
28
+ end
29
+ end
30
+
31
+ def [](key)
32
+ @config[key]
33
+ end
34
+
35
+ def to_s
36
+ @config.to_s
37
+ end
38
+ end
39
+ end
data/lib/lobster/job.rb CHANGED
@@ -39,7 +39,7 @@ module Lobster
39
39
  end
40
40
 
41
41
  def run(out,err,dir)
42
- Lobster.logger.info "Starting job #{@name} from directory #{dir}"
42
+ Lobster.logger.info "Starting job #{@name}"
43
43
  begin
44
44
  @pid = spawn(@command, :out=>out, :err=>err, :chdir=>dir)
45
45
  rescue Exception => e
@@ -47,5 +47,35 @@ module Lobster
47
47
  @next_run = Time.now + 10
48
48
  end
49
49
  end
50
+
51
+ def kill(sig)
52
+ if @pid
53
+ Lobster.logger.info "Killing job #{@name} with pid #{@pid} and all its children"
54
+ kill_tree sig, @pid
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def kill_tree(sig, pid)
61
+ child_parent_processes = `ps -eo pid,ppid | grep #{pid}`
62
+ child_parent_processes = child_parent_processes.split("\n").map do |child_and_parent|
63
+ child_and_parent.strip.split(/\s+/).map(&:to_i)
64
+ end
65
+ child_parent_processes.each do |child, parent|
66
+ if parent == pid
67
+ kill_tree(sig, child)
68
+ end
69
+ end
70
+
71
+ Lobster.logger.info "Killing pid #{pid}"
72
+ begin
73
+ Process.kill sig, pid
74
+ rescue Errno::ESRCH
75
+ # Process already got killed somehow
76
+ rescue Exception => e
77
+ Lobster.logger.warn "Process #{pid} exception: #{e}"
78
+ end
79
+ end
50
80
  end
51
81
  end
@@ -1,17 +1,16 @@
1
- require 'logger'
2
1
  require 'lobster/version'
3
2
 
4
3
  module Lobster
5
4
  class Service
6
- def self.start(dir)
7
- Lobster.logger = Logger.new(STDOUT)
8
- new(dir).run
5
+ def self.start(config)
6
+ new(config).run
9
7
  end
10
8
 
11
- def initialize(dir)
9
+ def initialize(config)
12
10
  @job_list = nil
13
- @directory = dir || '.'
14
- @file = File.join(@directory, 'config', 'schedule.rb')
11
+ @running = true
12
+ @sleeping = false
13
+ @config = config
15
14
  @poll_delay = 60
16
15
 
17
16
  rout, @wout = IO.pipe
@@ -29,37 +28,43 @@ module Lobster
29
28
  end
30
29
  end
31
30
 
32
- Signal.trap "INT" do
33
- Lobster.logger.info "All jobs are getting killed."
34
- exit 0
31
+ at_exit do
32
+ @running = false
33
+ sleep 0.01 until @sleeping # make sure no new jobs are created
34
+
35
+ Lobster.logger.info "Exiting, all jobs are getting killed."
36
+ @job_list.jobs.each_value do |job|
37
+ job.kill 'INT'
38
+ end if @job_list
35
39
  end
36
40
  end
37
41
 
38
42
  def run
39
43
  Lobster.logger.info "Lobster started version #{Lobster::VERSION}"
40
- Lobster.logger.info "Schedule file: #{@file}"
41
- Lobster.logger.info "Poll delay: #{@poll_delay}"
44
+ Lobster.logger.info "Lobster config: #{@config}"
42
45
 
43
- loop do
46
+ while @running
47
+ @sleeping = false
44
48
  now = Time.now
45
49
 
46
- reload_config
50
+ reload_schedule
47
51
 
48
52
  @job_list.jobs.each_value do |job|
49
53
  if not job.running? and now >= job.next_run
50
- job.run(@wout, @werr, @directory)
54
+ job.run(@wout, @werr, @config[:lobster_dir])
51
55
  end
52
56
  end
57
+ @sleeping = true
53
58
  sleep @poll_delay
54
59
  end
55
60
  end
56
61
 
57
- def reload_config
58
- @job_list ||= JobList.new(@file)
62
+ def reload_schedule
63
+ @job_list ||= JobList.new(@config[:schedule_file])
59
64
  begin
60
65
  @job_list.reload
61
66
  rescue Exception => e
62
- Lobster.logger.error "#{e}: error while reading config file in #{@file}, not updating"
67
+ Lobster.logger.error "#{e}: error while reading config file in #{@config[:schedule_file]}, not updating"
63
68
  end
64
69
  end
65
70
  end
@@ -1,3 +1,3 @@
1
1
  module Lobster
2
- VERSION = '0.1.0.alpha2'
2
+ VERSION = '0.1.0'
3
3
  end
data/lib/lobster.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Lobster
2
+ autoload :Configuration, 'lobster/configuration'
2
3
  autoload :JobList, 'lobster/job_list'
3
4
  autoload :Job, 'lobster/job'
4
5
  autoload :Service, 'lobster/service'
data/lobster.gemspec CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Maxime Brugidou"]
10
10
  s.email = ["m.brugidou@criteo.com"]
11
- s.homepage = ""
11
+ s.homepage = "https://github.com/brugidou/lobster"
12
12
  s.summary = %q{Simple loop job runner service.}
13
13
  s.description = %q{}
14
14
  s.files = `git ls-files`.split("\n")
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lobster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.alpha2
5
- prerelease: 6
4
+ version: 0.1.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Maxime Brugidou
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-19 00:00:00.000000000 Z
12
+ date: 2011-12-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: daemons
16
- requirement: &79856860 !ruby/object:Gem::Requirement
16
+ requirement: &80998950 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: 1.1.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *79856860
24
+ version_requirements: *80998950
25
25
  description: ''
26
26
  email:
27
27
  - m.brugidou@criteo.com
@@ -30,16 +30,20 @@ executables:
30
30
  extensions: []
31
31
  extra_rdoc_files: []
32
32
  files:
33
+ - .gitignore
33
34
  - Gemfile
34
35
  - Gemfile.lock
36
+ - LICENSE
37
+ - README.md
35
38
  - bin/lobster
36
39
  - lib/lobster.rb
40
+ - lib/lobster/configuration.rb
37
41
  - lib/lobster/job.rb
38
42
  - lib/lobster/job_list.rb
39
43
  - lib/lobster/service.rb
40
44
  - lib/lobster/version.rb
41
45
  - lobster.gemspec
42
- homepage: ''
46
+ homepage: https://github.com/brugidou/lobster
43
47
  licenses: []
44
48
  post_install_message:
45
49
  rdoc_options: []
@@ -54,9 +58,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
58
  required_rubygems_version: !ruby/object:Gem::Requirement
55
59
  none: false
56
60
  requirements:
57
- - - ! '>'
61
+ - - ! '>='
58
62
  - !ruby/object:Gem::Version
59
- version: 1.3.1
63
+ version: '0'
60
64
  requirements: []
61
65
  rubyforge_project:
62
66
  rubygems_version: 1.8.11