daemonizer 0.4.18 → 0.5.0.beta.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/.gitignore +7 -0
- data/Gemfile +4 -0
- data/Rakefile +9 -29
- data/daemonizer.gemspec +20 -69
- data/lib/daemonizer.rb +10 -4
- data/lib/daemonizer/cli.rb +14 -1
- data/lib/daemonizer/config.rb +2 -2
- data/lib/daemonizer/dsl.rb +16 -4
- data/lib/daemonizer/engine.rb +41 -36
- data/lib/daemonizer/option.rb +11 -5
- data/lib/daemonizer/process_manager.rb +7 -3
- data/lib/daemonizer/version.rb +3 -0
- data/lib/daemonizer/worker.rb +16 -2
- data/lib/daemonizer/worker_pool.rb +10 -4
- data/spec/cli/debugging_spec.rb +45 -0
- data/spec/cli/not_started_spec.rb +127 -0
- data/spec/cli/started_spec.rb +121 -0
- data/spec/common/forking_spec.rb +35 -0
- data/spec/settings/callbacks_spec.rb +103 -0
- data/spec/settings/options_spec.rb +146 -0
- data/spec/settings/pools_spec.rb +43 -0
- data/spec/spec_helper.rb +56 -8
- data/spec/support/daemonfile_factory.rb +33 -0
- data/spec/support/helpers.rb +70 -0
- data/spec/support/path.rb +18 -0
- data/spec/support/processes.rb +23 -0
- data/spec/support/ruby_ext.rb +19 -0
- metadata +107 -50
- data/VERSION +0 -1
- data/spec/spec.opts +0 -1
- data/spec/test_spec.rb +0 -5
@@ -0,0 +1,103 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class CallbacksSpecHandler < SpecHandler
|
4
|
+
class <<self
|
5
|
+
attr_accessor :call_on_prepare
|
6
|
+
attr_accessor :call_on_start
|
7
|
+
end
|
8
|
+
|
9
|
+
def start
|
10
|
+
self.class.call_on_start && self.class.call_on_start.call
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def prepare(starter, &block)
|
15
|
+
self.class.call_on_prepare && self.class.call_on_prepare.call
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
describe "callbacks in Daemonfile" do
|
22
|
+
before :each do
|
23
|
+
stubs_logger
|
24
|
+
end
|
25
|
+
|
26
|
+
%w(after_prepare before_prepare before_start).each do |callback|
|
27
|
+
context "initializing #{callback} callbacks" do
|
28
|
+
before :each do
|
29
|
+
@eval = Daemonizer::Dsl.evaluate(<<-G)
|
30
|
+
#{callback} do
|
31
|
+
1
|
32
|
+
end
|
33
|
+
|
34
|
+
pool :test do
|
35
|
+
#{callback} do
|
36
|
+
2
|
37
|
+
end
|
38
|
+
|
39
|
+
#{callback} do
|
40
|
+
3
|
41
|
+
end
|
42
|
+
end
|
43
|
+
G
|
44
|
+
end
|
45
|
+
|
46
|
+
it { @eval.configs[:test][:callbacks].should be_kind_of(Hash) }
|
47
|
+
it { @eval.configs[:test][:callbacks][callback.to_sym].should be_kind_of(Array) }
|
48
|
+
it { @eval.configs[:test][:callbacks][callback.to_sym].size.should == 3 }
|
49
|
+
it "should keep valid callbacks order" do
|
50
|
+
@eval.configs[:test][:callbacks][callback.to_sym][0].call.should == 1
|
51
|
+
@eval.configs[:test][:callbacks][callback.to_sym][1].call.should == 2
|
52
|
+
@eval.configs[:test][:callbacks][callback.to_sym][2].call.should == 3
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "callbacks in Daemonizer::Config" do
|
59
|
+
before :each do
|
60
|
+
stubs_logger
|
61
|
+
end
|
62
|
+
|
63
|
+
before :each do
|
64
|
+
CallbacksSpecHandler.call_on_prepare = lambda { @engine_state.become('prepare_invoked') }
|
65
|
+
CallbacksSpecHandler.call_on_start = lambda { @engine_state.become('start_invoked') }
|
66
|
+
|
67
|
+
@engine_state = states('engine_state').starts_as('pending')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should work in correct order with many callbacks" do
|
71
|
+
@before_prepare = mock('before_prepare')
|
72
|
+
@before_prepare.expects('touch').when(@engine_state.is('pending')).twice
|
73
|
+
|
74
|
+
@after_prepare = mock('after_prepare')
|
75
|
+
@after_prepare.expects('touch').when(@engine_state.is('prepare_invoked')).twice
|
76
|
+
|
77
|
+
@before_start = mock('before_start')
|
78
|
+
@before_start.expects('touch').when(@engine_state.is('after_prepare_invoked')).twice
|
79
|
+
|
80
|
+
@callbacks = {
|
81
|
+
:before_prepare => [ Proc.new { @before_prepare.touch }, Proc.new { @before_prepare.touch; @engine_state.become('before_prepare_invoked') } ],
|
82
|
+
:after_prepare => [ Proc.new { @after_prepare.touch }, Proc.new { @after_prepare.touch; @engine_state.become('after_prepare_invoked') } ],
|
83
|
+
:before_start => [ Proc.new { @before_start.touch }, Proc.new { @before_start.touch; @engine_state.become('before_start_invoked') } ]
|
84
|
+
}
|
85
|
+
|
86
|
+
@engine = Daemonizer::Engine.new(Daemonizer::Config.new(:pool, {
|
87
|
+
:workers => 1,
|
88
|
+
:pid_file =>"#{tmp_dir}/test1.pid",
|
89
|
+
:log_file =>"#{tmp_dir}/test1.log",
|
90
|
+
:callbacks => @callbacks,
|
91
|
+
:handler => CallbacksSpecHandler
|
92
|
+
}))
|
93
|
+
@engine.run_prepare_with_callbacks do
|
94
|
+
@engine.run_start_with_callbacks
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
after :each do
|
99
|
+
CallbacksSpecHandler.call_on_prepare = nil
|
100
|
+
CallbacksSpecHandler.call_on_start = nil
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
class OptionsSpecHandler < SpecHandler; end
|
4
|
+
|
5
|
+
describe "set_option in Daemonfile" do
|
6
|
+
it "should initialize simple setting correctly" do
|
7
|
+
Daemonizer::Option.expects(:new).with(:simple, "simple_value").once
|
8
|
+
Daemonizer::Dsl.evaluate(<<-G)
|
9
|
+
pool :test do
|
10
|
+
set_option :simple, "simple_value"
|
11
|
+
end
|
12
|
+
G
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should initialize block setting correctly" do
|
16
|
+
Daemonizer::Option.expects(:new).with(:lambda, kind_of(Proc), any_of(true, nil)).once
|
17
|
+
Daemonizer::Dsl.evaluate(<<-G)
|
18
|
+
pool :test do
|
19
|
+
set_option :lambda do
|
20
|
+
"lambda_value"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
G
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should correctly set option_handlers" do
|
27
|
+
@eval = Daemonizer::Dsl.evaluate(<<-G)
|
28
|
+
pool :test do
|
29
|
+
set_option :option1, "option1_value"
|
30
|
+
set_option :option2, "option1_value"
|
31
|
+
set_option :option3, "option1_value"
|
32
|
+
end
|
33
|
+
G
|
34
|
+
@eval.configs[:test][:handler_options].should be_kind_of(Hash)
|
35
|
+
@eval.configs[:test][:handler_options].size.should == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should raise DslError if nothing provided to set_option" do
|
39
|
+
lambda {
|
40
|
+
Daemonizer::Dsl.evaluate(<<-G)
|
41
|
+
pool :test do
|
42
|
+
set_option :option1, nil
|
43
|
+
end
|
44
|
+
G
|
45
|
+
}.should raise_error Daemonizer::Dsl::DslError
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "pool options in Daemonizer::Config" do
|
50
|
+
context "with name and value" do
|
51
|
+
before :each do
|
52
|
+
@handler = Daemonizer::Engine.new(Daemonizer::Config.new(:pool, {
|
53
|
+
:workers => 1,
|
54
|
+
:pid_file =>"#{tmp_dir}/test1.pid",
|
55
|
+
:log_file =>"#{tmp_dir}/test1.log",
|
56
|
+
:handler_options => {
|
57
|
+
:simple => Daemonizer::Option.new(:simple, "simple_value"),
|
58
|
+
:lambda => Daemonizer::Option.new(:lambda, lambda { "lambda_value" }),
|
59
|
+
:block => Daemonizer::Option.new(:block, lambda { "block_value" }, true)
|
60
|
+
},
|
61
|
+
:handler => OptionsSpecHandler
|
62
|
+
})).run_start_with_callbacks
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return simple option" do
|
66
|
+
@handler.option(:simple).should == "simple_value"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should return lambda value" do
|
70
|
+
@handler.option(:lambda).should be_kind_of(Proc)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return simple option" do
|
74
|
+
@handler.option(:block).should == "block_value"
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
describe Daemonizer::Option do
|
82
|
+
context "with auto_eval set to true" do
|
83
|
+
context "and not lambda value" do
|
84
|
+
it do
|
85
|
+
lambda {
|
86
|
+
Daemonizer::Option.new(:option, "not_lambda", true)
|
87
|
+
}.should raise_error(Daemonizer::Option::OptionError)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "and lambda value" do
|
92
|
+
context "and handler is not defined" do
|
93
|
+
it do
|
94
|
+
lambda {
|
95
|
+
Daemonizer::Option.new(:option, lambda { "lambda" }, true).value
|
96
|
+
}.should raise_error(Daemonizer::Option::OptionError)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
context "and handler defined" do
|
100
|
+
context "with arity == 0" do
|
101
|
+
it do
|
102
|
+
Daemonizer::Option.new(:option, lambda { "lambda" }, true).
|
103
|
+
value(OptionsSpecHandler.new).
|
104
|
+
should == "lambda"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "with arity == 2" do
|
109
|
+
it do
|
110
|
+
Daemonizer::Option.new(:option, lambda { |worker_id, workers_count| "#{worker_id}/#{workers_count}" }, true).
|
111
|
+
value(OptionsSpecHandler.new).
|
112
|
+
should == "1/1"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "with arity == 1" do
|
117
|
+
|
118
|
+
it do
|
119
|
+
lambda {
|
120
|
+
Daemonizer::Option.new(:option, lambda { |worker_id| "lambda" }, true).
|
121
|
+
value(OptionsSpecHandler.new)
|
122
|
+
}.should raise_error(Daemonizer::Option::OptionError)
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "with auto_eval set to false" do
|
132
|
+
context "and not lambda value" do
|
133
|
+
it do
|
134
|
+
Daemonizer::Option.new(:option, "simple").value.should == "simple"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context "and lambda value" do
|
139
|
+
it do
|
140
|
+
Daemonizer::Option.new(:option, lambda { "lambda" }).value.
|
141
|
+
should be_kind_of(Proc)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Evaluating pool settings through dsl" do
|
4
|
+
before :each do
|
5
|
+
@dsl = Daemonizer::Dsl.evaluate(<<-EOF)
|
6
|
+
poll_period 10
|
7
|
+
|
8
|
+
pool "pool1" do
|
9
|
+
workers 1
|
10
|
+
|
11
|
+
not_cow_friendly
|
12
|
+
end
|
13
|
+
pool "pool2" do
|
14
|
+
workers 2
|
15
|
+
poll_period 2
|
16
|
+
end
|
17
|
+
EOF
|
18
|
+
|
19
|
+
@configuration = @dsl.configs
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should create 2 pool record" do
|
23
|
+
@configuration.size.should == 2
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should set parameters on pools isolated" do
|
27
|
+
@configuration[:pool1].keys.should include(:workers, :poll_period, :cow_friendly)
|
28
|
+
@configuration[:pool2].keys.should include(:workers, :poll_period)
|
29
|
+
@configuration[:pool2].keys.should_not include(:cow_friendly)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should correctly inherit parameters from top level" do
|
33
|
+
@configuration[:pool1][:poll_period].should == 10
|
34
|
+
@configuration[:pool2][:poll_period].should == 2
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should process pool definition on call Daemonizer::Dsl#process" do
|
38
|
+
pool_config = Daemonizer::Config
|
39
|
+
pool_config.expects(:new).with(:pool2, equals(@configuration[:pool2])).once
|
40
|
+
pool_config.expects(:new).with(:pool1, equals(@configuration[:pool1])).once
|
41
|
+
@processed_config = @dsl.process
|
42
|
+
end
|
43
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,57 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
#$:.unshift File.expand_path('..', __FILE__)
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
|
4
|
+
require 'daemonizer'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'rubygems'
|
7
|
+
require 'rspec'
|
8
|
+
require 'open3'
|
9
|
+
|
10
|
+
Dir["#{File.expand_path('support', File.dirname(__FILE__))}/*.rb"].each do |file|
|
11
|
+
require file
|
12
|
+
end
|
13
|
+
|
14
|
+
$debug = false
|
15
|
+
$show_err = true
|
16
|
+
|
17
|
+
FileUtils.rm_rf(Spec::Path.app_root)
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include Spec::Helpers
|
21
|
+
config.include Spec::Path
|
22
|
+
config.include Spec::DaemonfileFactory
|
23
|
+
config.include Spec::Processes
|
24
|
+
|
25
|
+
config.filter_run :focused => true
|
26
|
+
config.run_all_when_everything_filtered = true
|
27
|
+
config.alias_example_to :fit, :focused => true
|
28
|
+
|
29
|
+
config.mock_with :mocha
|
30
|
+
|
31
|
+
original_wd = Dir.pwd
|
32
|
+
|
33
|
+
config.before :each do
|
34
|
+
reset!
|
35
|
+
in_app_root
|
36
|
+
end
|
37
|
+
|
38
|
+
config.after :each do
|
39
|
+
Dir.chdir(original_wd)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class SpecHandler < Daemonizer::Handler
|
44
|
+
def initialize(*args)
|
45
|
+
@worker_id = 1
|
46
|
+
@workers_count = 1
|
47
|
+
super
|
48
|
+
end
|
49
|
+
|
50
|
+
def prepare(starter, &block)
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
def start
|
55
|
+
self
|
56
|
+
end
|
9
57
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Spec
|
2
|
+
module DaemonfileFactory
|
3
|
+
def simple_daemonfile(*pools)
|
4
|
+
code = ""
|
5
|
+
pid_files = pools.map do |pool|
|
6
|
+
if pool[:exit_on_start]
|
7
|
+
end
|
8
|
+
code << <<EOF
|
9
|
+
pool :#{pool[:name]} do
|
10
|
+
workers #{pool[:workers] || 1}
|
11
|
+
poll_period #{pool[:poll_period] || 1}
|
12
|
+
log_file "test.log"
|
13
|
+
pid_file "#{pool[:pid_file]}"
|
14
|
+
|
15
|
+
prepare do |block|
|
16
|
+
#{pool[:on_prepare]}
|
17
|
+
block.call
|
18
|
+
end
|
19
|
+
|
20
|
+
start do |worker_id, workers_count|
|
21
|
+
trap("TERM") { exit 0; }
|
22
|
+
#{pool[:on_start]}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
EOF
|
27
|
+
pool[:pid_file]
|
28
|
+
end
|
29
|
+
daemonfile code
|
30
|
+
pid_files
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Spec
|
2
|
+
module Helpers
|
3
|
+
def reset!
|
4
|
+
kill_if_running!
|
5
|
+
FileUtils.rm_rf(app_root)
|
6
|
+
FileUtils.mkdir_p(app_root)
|
7
|
+
end
|
8
|
+
|
9
|
+
def in_app_root(&blk)
|
10
|
+
Dir.chdir(app_root, &blk)
|
11
|
+
end
|
12
|
+
|
13
|
+
def daemonizer(cmd)
|
14
|
+
sys_exec File.join(gem_root, 'bin/daemonizer') + " " + cmd.to_s
|
15
|
+
end
|
16
|
+
|
17
|
+
def daemonfile(code)
|
18
|
+
File.open(File.join(app_root, "Daemonfile"), 'w') do |f|
|
19
|
+
f.puts code
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :out, :err, :exitstatus
|
24
|
+
|
25
|
+
def sys_exec(cmd, expect_err = false)
|
26
|
+
Open3.popen3(cmd.to_s) do |stdin, stdout, stderr|
|
27
|
+
@in_p, @out_p, @err_p = stdin, stdout, stderr
|
28
|
+
|
29
|
+
yield @in_p if block_given?
|
30
|
+
@in_p.close
|
31
|
+
|
32
|
+
@out = @out_p.read_available_bytes.strip
|
33
|
+
@err = @err_p.read_available_bytes.strip
|
34
|
+
end
|
35
|
+
|
36
|
+
puts @err unless expect_err || @err.empty? || !$show_err
|
37
|
+
@out
|
38
|
+
end
|
39
|
+
|
40
|
+
def kill_if_running!
|
41
|
+
Dir["#{tmp_dir}/**/*.pid"].each do |pid_file|
|
42
|
+
pid = File.read(pid_file).chomp
|
43
|
+
if pid.to_i > 0
|
44
|
+
puts "Warning: Daemonizer was not properly stopped. Stop it in after block. Killing #{pid.to_i}"
|
45
|
+
Process.kill("TERM", pid.to_i) rescue true
|
46
|
+
sleep 5
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def daemonizer_runned?(pid_file)
|
52
|
+
pid = File.read(pid_file).chomp.to_i
|
53
|
+
if pid.to_i > 0
|
54
|
+
return !!Process.kill(0, pid.to_i)
|
55
|
+
else
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
rescue Errno::ESRCH, Errno::ECHILD, Errno::EPERM
|
59
|
+
false
|
60
|
+
end
|
61
|
+
|
62
|
+
def stubs_logger
|
63
|
+
logger = stubs(:logger)
|
64
|
+
logger.stubs(:info).returns(true)
|
65
|
+
Daemonizer.stubs(:logger).returns(logger)
|
66
|
+
end
|
67
|
+
|
68
|
+
extend self
|
69
|
+
end
|
70
|
+
end
|