lobster 0.1.0 → 0.1.1

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.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lobster (0.1.0)
4
+ lobster (0.1.1.pre)
5
5
  daemons (>= 1.1.4)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -4,17 +4,29 @@ Lobster
4
4
  Simple job service that was originally written to run hadoop jobs, but can be
5
5
  used for any batch-processing scripts that you want to run.
6
6
 
7
- Alpha Version Warning
8
- ---------------------
7
+ Lobster runs as a service (daemon) and regularly checks a schedule in
8
+ `$LOBSTER_DIR/config/schedule.rb`. The goal is to run the scheduled jobs in
9
+ parallel but a job can't run twice at the same time (no overlap). The only
10
+ configuration needed is the delay between 2 job runs.
9
11
 
10
- Lobster has not been fully released and tested yet.
11
-
12
- How It Works
12
+ Get Started
13
13
  ------------
14
14
 
15
+ ### Install
16
+
17
+ gem install lobster
18
+
15
19
  ### Create a `config/schedule.rb` file
16
20
 
21
+ Run `lobsterize` in the directory where you need to setup and run your jobs
22
+ (defined by the LOBSTER_DIR variable)
23
+
24
+ lobsterize
25
+
26
+ Then add a job in `config/schedule.rb`
27
+
17
28
  ~~~~~ ruby
29
+ # config/schedule.rb
18
30
  job "my-job" do
19
31
  cmd "runthis && runthat >> log/here.log"
20
32
  delay 5 # minutes
@@ -23,20 +35,55 @@ end
23
35
 
24
36
  ### Run Lobster
25
37
 
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`
38
+ Two environment variables are used (with defaults):
39
+
40
+ - `LOBSTER_DIR` is the directory used for all job commands, and where the
41
+ configuration/schedule files are (default: current
42
+ directory)
43
+ - `LOBSTER_ENV` is a variable used to handle different environments (default:
44
+ development)
45
+
46
+ You need write permission to `/var/run/lobster/` for pids, this path is
47
+ configurable.
48
+
49
+ - `lobster run` will run in the console
50
+ - or `lobster start` as a deamon
51
+
52
+ ### Configuration
53
+
54
+ The lobster configuration file should be located in
55
+ `$LOBSTER_DIR/config/lobster.yml` and has the following layout
28
56
 
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.
57
+ ~~~~ yaml
58
+ # default values
31
59
 
32
- Any log in stderr/stdout will be written in `$LOBSTER_DIR/log/lobster.output`
33
- along with some other useful information for monitoring.
60
+ # monitor the daemon and restart it if it stops
61
+ monitor: true
62
+ # log directory, absolute or relative to the LOBSTER_DIR
63
+ log_dir: log
64
+ # pids directory, absolute or relative to the LOBSTER_DIR
65
+ pid_dir: /var/run/lobster/
66
+ # schedule file, absolute or relative to the LOBSTER_DIR
67
+ schedule_file: config/schedule.rb
34
68
 
35
- The current directory where the job command is run is `$LOBSTER_DIR`.
69
+ # environment overrides, the env variable LOBSTER_ENV has
70
+ # to be set (default: development)
71
+ my_test_env:
72
+ monitor: false
73
+ log_dir: test_los
74
+ my_prod_env:
75
+ pid_dir: /var/run/
76
+ ~~~~
36
77
 
37
- How To Install
38
- --------------
78
+ Any log in stderr/stdout will be written in the log directory as
79
+ `lobster.output`, the actual lobster log is in `lobster.log`
39
80
 
40
- gem install lobster --pre
81
+ Capistrano Deployment
82
+ =====================
41
83
 
84
+ It is very easy to have a lobster project with a proper `config/lobster.yml` to
85
+ handle different environments such as development, testing and production. You
86
+ can simply have a `schedule.rb`file per environment.
42
87
 
88
+ Then you can use capistrano to deploy the new jobs and schedule to your servers
89
+ (the schedule is automatically reloaded).
data/bin/lobster CHANGED
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'optparse'
3
4
  require 'lobster'
4
- require 'logger'
5
5
  require 'daemons'
6
6
 
7
7
  lobster_dir = File.expand_path(ENV['LOBSTER_DIR'] || '.')
8
8
  lobster_env = ENV['LOBSTER_ENV'] || 'development'
9
9
 
10
- Lobster.logger = Logger.new(STDOUT)
11
10
  config = Lobster::Configuration.new(lobster_dir, lobster_env)
12
11
 
13
12
  #puts "LOBSTER_DIR set to #{lobster_dir}"
@@ -16,7 +15,6 @@ config = Lobster::Configuration.new(lobster_dir, lobster_env)
16
15
  log_dir = config[:log_dir]
17
16
  Dir.mkdir log_dir unless Dir.exist?(log_dir) || ARGV.first == "run"
18
17
 
19
-
20
18
  daemon_options = {
21
19
  :multiple => false,
22
20
  :monitor => config[:monitor],
data/bin/lobsterize ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ unless Dir.exist?('config')
4
+ puts 'create: config/'
5
+ Dir.mkdir 'config'
6
+ end
7
+
8
+ config = File.join('config', 'lobster.yml')
9
+ schedule = File.join('config', 'schedule.rb')
10
+
11
+ unless File.exist?(config)
12
+ puts "create: #{config}"
13
+ File.open(config,'w') do |f|
14
+ f.write <<-EOF.gsub /^\s+/,''
15
+ ## config/lobster.yml
16
+ ## configuration file for Lobster
17
+ ##
18
+ ## Default values
19
+ ## All paths are absolute or relative to the LOBSTER_DIR
20
+ ##
21
+ ## monitor the daemon and restart it if it stops
22
+ ## (requires a restart if modified)
23
+ #monitor: true
24
+ #
25
+ ## log directory
26
+ ## (requires a restart if modified)
27
+ #log_dir: log
28
+ #
29
+ ## pids directory
30
+ ## (requires a restart if modified)
31
+ #pid_dir: /var/run/lobster/
32
+ #
33
+ ## schedule file location
34
+ #schedule_file: config/schedulr.rb
35
+ #
36
+ ##
37
+ ## Environment values
38
+ ## You can use environement with the LOBSTER_ENV env variable,
39
+ ## the default is set to 'development'
40
+ ##
41
+ ## Example:
42
+ #development:
43
+ # monitor: false
44
+ # pid_dir: pids
45
+ #production:
46
+ # log_dir: /var/log/lobster/
47
+ EOF
48
+ end
49
+ end
50
+
51
+ unless File.exist?(schedule)
52
+ puts "create: #{schedule}"
53
+ File.open(schedule,'w') do |f|
54
+ f.write <<-EOF.gsub /^\s+/,''
55
+ ## config/schedule.rb
56
+ ## schedule file for Lobster
57
+ ##
58
+ ## Example:
59
+ ##
60
+ ## Job named my_job
61
+ #job 'my_job' do
62
+ #
63
+ ## will just sleep for 5 seconds, this command is run from the
64
+ ## LOBSTER_DIR directory, so you can use relative paths from here.
65
+ # command 'sleep 5'
66
+ #
67
+ ## the user my_user will be use to run the command, Lobster needs
68
+ ## password-less sudo access to this user.
69
+ # user 'my_user'
70
+ #
71
+ ## Lobster will wait 2 minutes between 2 runs
72
+ # delay 2
73
+ #
74
+ ## end of job definition
75
+ #end
76
+ EOF
77
+ end
78
+ end
79
+
80
+ puts "edit your schedule and try \"lobster run\""
@@ -14,7 +14,9 @@ module Lobster
14
14
  if File.exist? config_file_path
15
15
  config_file = YAML.load_file(config_file_path)
16
16
  @config.keys.each do |key|
17
- @config[key] = config_file[env][key.to_s] || config_file[key.to_s] || @config[key]
17
+ val = config_file[env][key.to_s] # environment-specific override
18
+ val = config_file[key.to_s] if val.nil?
19
+ @config[key] = val unless val.nil? # keep default if no config
18
20
  end
19
21
  end
20
22
 
data/lib/lobster/job.rb CHANGED
@@ -11,17 +11,16 @@ module Lobster
11
11
  def reload(options)
12
12
  options[:delay] ||= 10
13
13
 
14
- if options[:command] != @command
15
- Lobster.logger.info "Job command updated for #{@name}, was \"#{@command}\", now \"#{options[:command]}\"" if @command
16
- @command = options.delete(:command)
14
+ [:command, :delay, :user].each do |opt|
15
+ val = instance_variable_get "@#{opt}"
16
+ if options[opt] != val
17
+ Lobster.logger.info "Job #{opt} updated for #{@name}, was \"#{val}\", now \"#{options[opt]}\"" if val
18
+ instance_variable_set "@#{opt}", options.delete(opt)
19
+ # special case: reset @next_run if delay is updated
20
+ @next_run = nil if opt == :delay and not running?
21
+ end
17
22
  end
18
23
 
19
- if options[:delay] != @delay
20
- Lobster.logger.info "Job delay updated for #{@name}, was \"#{@delay}\", now \"#{options[:delay]}\"" if @delay
21
- @delay = options.delete(:delay)
22
- @next_run = nil unless running?
23
- end
24
-
25
24
  @name ||= "<unnamed_job_#{command.hash.abs}>"
26
25
  @next_run ||= Time.now + rand(@delay*60)
27
26
  end
@@ -40,8 +39,10 @@ module Lobster
40
39
 
41
40
  def run(out,err,dir)
42
41
  Lobster.logger.info "Starting job #{@name}"
42
+ command_line = @user ? "sudo -nu #{@user} sh -c \"#{@command}\"" : @command
43
+
43
44
  begin
44
- @pid = spawn(@command, :out=>out, :err=>err, :chdir=>dir)
45
+ @pid = spawn(command_line, :out=>out, :err=>err, :chdir=>dir)
45
46
  rescue Exception => e
46
47
  Lobster.logger.error "#{e}: error when starting job #{@name}"
47
48
  @next_run = Time.now + 10
@@ -4,14 +4,12 @@ module Lobster
4
4
 
5
5
  def initialize(file)
6
6
  @file = file
7
- #@options = {}
8
7
  @current_options = nil
9
8
  @jobs = {}
10
9
  end
11
10
 
12
11
  def reload
13
12
  @new_jobs = {}
14
- #@new_options = {}
15
13
 
16
14
  instance_eval(File.read(@file),@file)
17
15
 
@@ -19,12 +17,8 @@ module Lobster
19
17
  @jobs.each do |name, job|
20
18
  Lobster.logger.info "Job #{name} deleted." unless @new_jobs[name]
21
19
  end
22
- #@options.each do |key, value|
23
- # Lobster.logger.info "#{key} unset." unless @new_options[key]
24
- #end
25
20
 
26
21
  @jobs = @new_jobs
27
- #@options = @new_options
28
22
  end
29
23
 
30
24
  def job(name)
@@ -35,21 +29,15 @@ module Lobster
35
29
  @current_options = nil
36
30
  end
37
31
 
38
- def cmd(command)
39
- @current_options[:command] = command
32
+ [:command, :delay, :user].each do |opt|
33
+ define_method opt do |value|
34
+ @current_options[opt] = value
35
+ end
40
36
  end
41
37
 
42
- def delay(delay)
43
- @current_options[:delay] = delay
38
+ # backward compatibility
39
+ def cmd(command)
40
+ @current_options[:command] = command
44
41
  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
42
  end
55
43
  end
@@ -10,6 +10,7 @@ module Lobster
10
10
  @job_list = nil
11
11
  @running = true
12
12
  @sleeping = false
13
+ @main_thread = Thread.current
13
14
  @config = config
14
15
  @poll_delay = 60
15
16
 
@@ -28,6 +29,16 @@ module Lobster
28
29
  end
29
30
  end
30
31
 
32
+ trap 'HUP' do
33
+ begin
34
+ @config = Configuration.new(@config[:lobster_dir], @config[:environment])
35
+ rescue Exception => e
36
+ Lobster.logger.error "Cannot reload conf, Exception: #{e}"
37
+ break
38
+ end
39
+ Lobster.logger.info "Lobster config reloaded: #{@config}"
40
+ end
41
+
31
42
  at_exit do
32
43
  @running = false
33
44
  sleep 0.01 until @sleeping # make sure no new jobs are created
@@ -36,6 +47,8 @@ module Lobster
36
47
  @job_list.jobs.each_value do |job|
37
48
  job.kill 'INT'
38
49
  end if @job_list
50
+
51
+ @main_thread.wakeup # stop sleeping and exit properly
39
52
  end
40
53
  end
41
54
 
@@ -1,3 +1,3 @@
1
1
  module Lobster
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  end
data/lib/lobster.rb CHANGED
@@ -1,10 +1,12 @@
1
+ require 'logger'
2
+
1
3
  module Lobster
4
+ def self.logger
5
+ @@logger ||= Logger.new(STDOUT)
6
+ end
7
+
2
8
  autoload :Configuration, 'lobster/configuration'
3
9
  autoload :JobList, 'lobster/job_list'
4
10
  autoload :Job, 'lobster/job'
5
11
  autoload :Service, 'lobster/service'
6
-
7
- class << self
8
- attr_accessor :logger
9
- end
10
12
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lobster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-21 00:00:00.000000000 Z
12
+ date: 2012-01-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: daemons
16
- requirement: &80998950 !ruby/object:Gem::Requirement
16
+ requirement: &84449170 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,12 +21,13 @@ dependencies:
21
21
  version: 1.1.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *80998950
24
+ version_requirements: *84449170
25
25
  description: ''
26
26
  email:
27
27
  - m.brugidou@criteo.com
28
28
  executables:
29
29
  - lobster
30
+ - lobsterize
30
31
  extensions: []
31
32
  extra_rdoc_files: []
32
33
  files:
@@ -36,6 +37,7 @@ files:
36
37
  - LICENSE
37
38
  - README.md
38
39
  - bin/lobster
40
+ - bin/lobsterize
39
41
  - lib/lobster.rb
40
42
  - lib/lobster/configuration.rb
41
43
  - lib/lobster/job.rb