diff --git a/Gemfile.lock b/Gemfile.lock index ba45e56..20bef2d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,6 +26,7 @@ GEM byebug (5.0.0) columnize (= 0.9.0) columnize (0.9.0) + daemons (1.2.2) diff-lcs (1.2.5) haml (4.0.6) tilt @@ -69,6 +70,7 @@ DEPENDENCIES bundler (>= 1.0.0) byebug crono! + daemons haml rack-test rake (~> 10.0) @@ -76,6 +78,3 @@ DEPENDENCIES sinatra sqlite3 timecop (~> 0.7) - -BUNDLED WITH - 1.10.6 diff --git a/crono.gemspec b/crono.gemspec index 81cc773..be94e79 100644 --- a/crono.gemspec +++ b/crono.gemspec @@ -30,4 +30,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'sinatra' spec.add_development_dependency 'haml' spec.add_development_dependency 'rack-test' + spec.add_development_dependency 'daemons' end diff --git a/lib/crono/cli.rb b/lib/crono/cli.rb index 1c59a0f..674ff53 100644 --- a/lib/crono/cli.rb +++ b/lib/crono/cli.rb @@ -7,6 +7,8 @@ module Crono include Singleton include Logging + COMMANDS = %w(start stop restart run zap reload status) + attr_accessor :config def initialize @@ -16,16 +18,21 @@ module Crono def run parse_options(ARGV) + parse_command(ARGV) - setup_log + setup_log - write_pid + write_pid unless config.daemonize load_rails Cronotab.process(File.expand_path(config.cronotab)) print_banner check_jobs - start_working_loop + if config.daemonize + start_working_loop_in_daemon + else + start_working_loop + end end private @@ -33,13 +40,15 @@ module Crono def setup_log if config.daemonize self.logfile = config.logfile - daemonize + elsif config.deprecated_daemonize + self.logfile = config.logfile + deprecated_daemonize else self.logfile = STDOUT end end - def daemonize + def deprecated_daemonize ::Process.daemon(true, true) [$stdout, $stderr].each do |io| @@ -71,7 +80,7 @@ module Crono ENV['RACK_ENV'] = ENV['RAILS_ENV'] = config.environment require 'rails' require File.expand_path('config/environment.rb') - ::Rails.application.eager_load! + ::Rails.application.eager_load! if config.daemonize end def check_jobs @@ -79,6 +88,30 @@ module Crono logger.error "You have no jobs in you cronotab file #{config.cronotab}" end + def start_working_loop_in_daemon + unless ENV['RAILS_ENV'] == 'test' + begin + require 'daemons' + rescue LoadError + raise "You need to add gem 'daemons' to your Gemfile if you wish to use it." + end + end + Daemons.run_proc(config.process_name, dir: config.piddir, dir_mode: :normal, monitor: config.monitor, ARGV: @argv) do |*_argv| + Dir.chdir(root) + Crono.logger = Logger.new(config.logfile) + + start_working_loop + end + end + + def root + @root ||= rails_root_defined? ? ::Rails.root : DIR_PWD + end + + def rails_root_defined? + defined?(::Rails.root) + end + def start_working_loop loop do next_time, jobs = Crono.scheduler.next_jobs @@ -88,8 +121,8 @@ module Crono end def parse_options(argv) - OptionParser.new do |opts| - opts.banner = "Usage: crono [options]" + @argv = OptionParser.new do |opts| + opts.banner = "Usage: crono [options] start|stop|restart|run" opts.on("-C", "--cronotab PATH", "Path to cronotab file (Default: #{config.cronotab})") do |cronotab| config.cronotab = cronotab @@ -103,8 +136,20 @@ module Crono config.pidfile = pidfile end - opts.on("-d", "--[no-]daemonize", "Daemonize process (Default: #{config.daemonize})") do |daemonize| - config.daemonize = daemonize + opts.on("--piddir PATH", "Path to piddir (Default: #{config.piddir})") do |piddir| + config.piddir = piddir + end + + opts.on("-N", "--process_name name", "Name of the process (Default: #{config.process_name})") do |process_name| + config.process_name = process_name + end + + opts.on("-d", "--[no-]daemonize", "Deprecated! Instead use crono [start|stop|restart] without this option; Daemonize process (Default: #{config.daemonize})") do |daemonize| + config.deprecated_daemonize = daemonize + end + + opts.on("-m", "--monitor", "Start monitor process for a deamon (Default #{config.monitor})") do + config.monitor = true end opts.on '-e', '--environment ENV', "Application environment (Default: #{config.environment})" do |env| @@ -112,5 +157,12 @@ module Crono end end.parse!(argv) end + + def parse_command(argv) + if COMMANDS.include? argv[0] + config.daemonize = true + end + end + end end diff --git a/lib/crono/config.rb b/lib/crono/config.rb index 0165477..3a693dc 100644 --- a/lib/crono/config.rb +++ b/lib/crono/config.rb @@ -4,18 +4,31 @@ module Crono CRONOTAB = 'config/cronotab.rb' LOGFILE = 'log/crono.log' PIDFILE = 'tmp/pids/crono.pid' + PIDDIR = 'tmp/pids' + PROCESS_NAME = 'crono' - attr_accessor :cronotab, :logfile, :pidfile, :daemonize, :environment + attr_accessor :cronotab, :logfile, :pidfile, :piddir, :process_name, + :monitor, :daemonize, :deprecated_daemonize, :environment def initialize self.cronotab = CRONOTAB self.logfile = LOGFILE + self.piddir = PIDDIR + self.process_name = PROCESS_NAME self.daemonize = false + self.deprecated_daemonize = false + self.monitor = false self.environment = ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development' end + def pidfile=(pidfile) + @pidfile = pidfile + self.process_name = Pathname.new(pidfile).basename(".*").to_s + self.piddir = Pathname.new(pidfile).dirname.to_s + end + def pidfile - @pidfile || (daemonize ? PIDFILE : nil) + @pidfile || (deprecated_daemonize ? PIDFILE : nil) end end end diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb index 7f389c6..b80a621 100644 --- a/spec/cli_spec.rb +++ b/spec/cli_spec.rb @@ -9,10 +9,25 @@ describe Crono::CLI do expect(cli).to receive(:load_rails) expect(cli).to receive(:start_working_loop) expect(cli).to receive(:parse_options) + expect(cli).to receive(:parse_command) expect(cli).to receive(:write_pid) expect(Crono::Cronotab).to receive(:process) cli.run end + context 'should run as daemon' do + + before {cli.config.daemonize = true} + + it 'should initialize rails with #load_rails and start working loop' do + expect(cli).to receive(:load_rails) + expect(cli).to receive(:start_working_loop_in_daemon) + expect(cli).to receive(:parse_options) + expect(cli).to receive(:parse_command) + expect(cli).not_to receive(:write_pid) + expect(Crono::Cronotab).to receive(:process) + cli.run + end + end end describe '#parse_options' do @@ -31,9 +46,24 @@ describe Crono::CLI do expect(cli.config.pidfile).to be_eql 'tmp/pids/crono.0.log' end - it 'should set daemonize' do + it 'should set piddir' do + cli.send(:parse_options, ['--piddir', 'tmp/pids']) + expect(cli.config.piddir).to be_eql 'tmp/pids' + end + + it 'should set process_name' do + cli.send(:parse_options, ['--process_name', 'crono0']) + expect(cli.config.process_name).to be_eql 'crono0' + end + + it 'should set monitor' do + cli.send(:parse_options, ['--monitor']) + expect(cli.config.monitor).to be true + end + + it 'should set deprecated_daemonize' do cli.send(:parse_options, ['--daemonize']) - expect(cli.config.daemonize).to be true + expect(cli.config.deprecated_daemonize).to be true end it 'should set environment' do @@ -41,4 +71,42 @@ describe Crono::CLI do expect(cli.config.environment).to be_eql('production') end end + + describe '#parse_command' do + + it 'should set daemonize on start' do + cli.send(:parse_command, ['start']) + expect(cli.config.daemonize).to be true + end + + it 'should set daemonize on stop' do + cli.send(:parse_command, ['stop']) + expect(cli.config.daemonize).to be true + end + + it 'should set daemonize on restart' do + cli.send(:parse_command, ['restart']) + expect(cli.config.daemonize).to be true + end + + it 'should set daemonize on run' do + cli.send(:parse_command, ['run']) + expect(cli.config.daemonize).to be true + end + + it 'should set daemonize on zap' do + cli.send(:parse_command, ['zap']) + expect(cli.config.daemonize).to be true + end + + it 'should set daemonize on reload' do + cli.send(:parse_command, ['reload']) + expect(cli.config.daemonize).to be true + end + + it 'should set daemonize on status' do + cli.send(:parse_command, ['status']) + expect(cli.config.daemonize).to be true + end + end end diff --git a/spec/config_spec.rb b/spec/config_spec.rb index 043f44a..5efa762 100644 --- a/spec/config_spec.rb +++ b/spec/config_spec.rb @@ -9,7 +9,11 @@ describe Crono::Config do expect(@config.cronotab).to be Crono::Config::CRONOTAB expect(@config.logfile).to be Crono::Config::LOGFILE expect(@config.pidfile).to be nil + expect(@config.piddir).to be Crono::Config::PIDDIR + expect(@config.process_name).to be Crono::Config::PROCESS_NAME expect(@config.daemonize).to be false + expect(@config.deprecated_daemonize).to be false + expect(@config.monitor).to be false expect(@config.environment).to be_eql ENV['RAILS_ENV'] end @@ -23,8 +27,8 @@ describe Crono::Config do specify { expect(pidfile).to be_nil } end - context "daemonize is true" do - before { config.daemonize = true } + context "deprecated_daemonize is true" do + before { config.deprecated_daemonize = true } specify { expect(pidfile).to eq Crono::Config::PIDFILE } end @@ -36,7 +40,16 @@ describe Crono::Config do before { config.pidfile = path } specify { expect(pidfile).to eq path } + + it "trys to set piddir" do + expect(config.piddir).to eq "foo/bar" + end + + it "trys to set process_name" do + expect(config.process_name).to eq "pid" + end end + end end end