lobster 0.1.0.alpha2 → 0.1.0

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/.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