eye 0.1.11 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +2 -2
- data/README.md +41 -18
- data/bin/eye +4 -3
- data/examples/processes/em.rb +2 -1
- data/examples/processes/thin.ru +12 -0
- data/examples/sidekiq.eye +19 -0
- data/examples/test.eye +25 -16
- data/eye.gemspec +5 -3
- data/lib/eye.rb +2 -1
- data/lib/eye/checker/validation.rb +1 -1
- data/lib/eye/child_process.rb +27 -5
- data/lib/eye/controller/load.rb +13 -11
- data/lib/eye/controller/send_command.rb +32 -7
- data/lib/eye/controller/status.rb +4 -3
- data/lib/eye/dsl/config_opts.rb +38 -1
- data/lib/eye/dsl/opts.rb +12 -5
- data/lib/eye/dsl/pure_opts.rb +2 -2
- data/lib/eye/dsl/validate.rb +5 -0
- data/lib/eye/group.rb +1 -1
- data/lib/eye/group/chain.rb +2 -0
- data/lib/eye/notify.rb +86 -0
- data/lib/eye/notify/jabber.rb +30 -0
- data/lib/eye/notify/mail.rb +44 -0
- data/lib/eye/process.rb +5 -1
- data/lib/eye/process/child.rb +7 -8
- data/lib/eye/process/commands.rb +21 -18
- data/lib/eye/process/notify.rb +22 -7
- data/lib/eye/process/system.rb +18 -5
- data/lib/eye/process/validate.rb +23 -0
- data/lib/eye/system.rb +4 -1
- data/lib/eye/utils.rb +9 -0
- data/spec/checker_spec.rb +0 -1
- data/spec/client_server_spec.rb +0 -1
- data/spec/controller/controller_spec.rb +1 -1
- data/spec/controller/intergration_spec.rb +15 -0
- data/spec/controller/load_spec.rb +49 -4
- data/spec/dsl/chain_spec.rb +20 -14
- data/spec/dsl/checks_spec.rb +17 -0
- data/spec/dsl/notify_spec.rb +105 -0
- data/spec/dsl/process_spec.rb +50 -0
- data/spec/mock_spec.rb +0 -1
- data/spec/notify/jabber_spec.rb +25 -0
- data/spec/notify/mail_spec.rb +26 -0
- data/spec/notify_spec.rb +90 -0
- data/spec/process/config_spec.rb +0 -1
- data/spec/process/notify_spec.rb +27 -0
- data/spec/process/states_history_spec.rb +0 -1
- data/spec/process/stop_spec.rb +6 -0
- data/spec/process/system_spec.rb +34 -21
- data/spec/process/update_config_spec.rb +0 -1
- data/spec/spec_helper.rb +9 -2
- data/spec/support/spec_support.rb +0 -1
- data/spec/system_resources_spec.rb +0 -1
- data/spec/system_spec.rb +3 -6
- data/spec/utils/alive_array_spec.rb +0 -1
- data/spec/utils/celluloid_chain_spec.rb +0 -1
- data/spec/utils/tail_spec.rb +0 -1
- metadata +71 -7
data/lib/eye/process/commands.rb
CHANGED
@@ -72,21 +72,8 @@ module Eye::Process::Commands
|
|
72
72
|
switch :restarting
|
73
73
|
|
74
74
|
if self[:restart_command]
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
res = execute(cmd, config.merge(:timeout => self[:restart_timeout]))
|
79
|
-
|
80
|
-
if res[:error]
|
81
|
-
error "restart raised with #{res[:error].inspect}"
|
82
|
-
|
83
|
-
if res[:error].class == Timeout::Error
|
84
|
-
error 'you should tune restart_timeout setting'
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
sleep self[:restart_grace].to_f
|
89
|
-
|
75
|
+
execute_restart_command
|
76
|
+
sleep self[:restart_timeout].to_f
|
90
77
|
result = check_alive_with_refresh_pid_if_needed
|
91
78
|
switch(result ? :restarted : :crushed)
|
92
79
|
else
|
@@ -122,7 +109,7 @@ private
|
|
122
109
|
sleep self[:stop_grace].to_f
|
123
110
|
|
124
111
|
elsif self[:stop_signals]
|
125
|
-
info "executing
|
112
|
+
info "executing stop_signals #{self[:stop_signals].inspect}"
|
126
113
|
stop_signals = self[:stop_signals].clone
|
127
114
|
|
128
115
|
signal = stop_signals.shift
|
@@ -132,8 +119,7 @@ private
|
|
132
119
|
delay = stop_signals.shift
|
133
120
|
signal = stop_signals.shift
|
134
121
|
|
135
|
-
|
136
|
-
unless process_realy_running?
|
122
|
+
if wait_for_condition(delay.to_f, 0.1){ !process_realy_running? }
|
137
123
|
info 'has terminated'
|
138
124
|
break
|
139
125
|
end
|
@@ -157,6 +143,23 @@ private
|
|
157
143
|
end
|
158
144
|
end
|
159
145
|
end
|
146
|
+
|
147
|
+
def execute_restart_command
|
148
|
+
cmd = prepare_command(self[:restart_command])
|
149
|
+
info "executing: `#{cmd}` with restart_timeout: #{self[:restart_timeout].to_f}s and restart_grace: #{self[:restart_grace].to_f}s"
|
150
|
+
|
151
|
+
res = execute(cmd, config.merge(:timeout => self[:restart_timeout]))
|
152
|
+
|
153
|
+
if res[:error]
|
154
|
+
error "restart raised with #{res[:error].inspect}"
|
155
|
+
|
156
|
+
if res[:error].class == Timeout::Error
|
157
|
+
error 'you should tune restart_timeout setting'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
res
|
162
|
+
end
|
160
163
|
|
161
164
|
def daemonize_process
|
162
165
|
time_before = Time.now
|
data/lib/eye/process/notify.rb
CHANGED
@@ -1,17 +1,32 @@
|
|
1
1
|
module Eye::Process::Notify
|
2
2
|
|
3
3
|
# notify to user:
|
4
|
-
# 1) process crushed by itself, and we restart it
|
5
|
-
# 2) checker
|
6
|
-
# 3) flapping + switch to unmonitored
|
4
|
+
# 1) process crushed by itself, and we restart it [:warn]
|
5
|
+
# 2) checker bounded to restart process [:crit]
|
6
|
+
# 3) flapping + switch to unmonitored [:crit]
|
7
7
|
|
8
|
-
|
8
|
+
LEVELS = {:warn => 0, :crit => 1}
|
9
9
|
|
10
|
-
# TODO: add mail, jabber here
|
11
10
|
def notify(level, msg)
|
12
|
-
|
13
|
-
|
11
|
+
# logging it
|
12
|
+
error "NOTIFY: #{msg}" if ilevel(level) > 0
|
13
|
+
|
14
|
+
# send notifies
|
15
|
+
if self[:notify].present?
|
16
|
+
message = {:message => msg, :name => name,
|
17
|
+
:full_name => full_name, :pid => pid, :host => Eye::System.host, :level => level,
|
18
|
+
:at => Time.now }
|
19
|
+
|
20
|
+
self[:notify].each do |contact, not_level|
|
21
|
+
Eye::Notify.notify(contact, message) if ilevel(level) >= ilevel(not_level)
|
22
|
+
end
|
14
23
|
end
|
15
24
|
end
|
16
25
|
|
26
|
+
private
|
27
|
+
|
28
|
+
def ilevel(level)
|
29
|
+
LEVELS[level].to_i
|
30
|
+
end
|
31
|
+
|
17
32
|
end
|
data/lib/eye/process/system.rb
CHANGED
@@ -47,14 +47,27 @@ module Eye::Process::System
|
|
47
47
|
res[:status] == :ok
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
:timeout
|
50
|
+
# non blocking actor timeout
|
51
|
+
def wait_for_condition(timeout, step = 0.1, &block)
|
52
|
+
defer{ wait_for_condition_sync(timeout, step, &block) }
|
54
53
|
end
|
55
54
|
|
56
55
|
def execute(cmd, cfg = {})
|
57
56
|
defer{ Eye::System::execute cmd, cfg }
|
58
57
|
end
|
59
|
-
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def wait_for_condition_sync(timeout, step, &block)
|
62
|
+
res = nil
|
63
|
+
|
64
|
+
Timeout::timeout(timeout.to_f) do
|
65
|
+
sleep step.to_f until res = yield
|
66
|
+
end
|
67
|
+
|
68
|
+
res
|
69
|
+
rescue Timeout::Error
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
60
73
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Eye::Process::Validate
|
4
|
+
|
5
|
+
class Error < Exception; end
|
6
|
+
|
7
|
+
def validate(config)
|
8
|
+
if (str = config[:start_command])
|
9
|
+
# it should parse with Shellwords and not raise
|
10
|
+
spl = Shellwords.shellwords(str) * '#'
|
11
|
+
|
12
|
+
if config[:daemonize]
|
13
|
+
if spl =~ %r[sh#\-c|#&&#|;#]
|
14
|
+
raise Error, "#{config[:name]}, start_command in daemonize not supported shell concats like '&&'"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Shellwords.shellwords(config[:stop_command]) if config[:stop_command]
|
20
|
+
Shellwords.shellwords(config[:restart_command]) if config[:restart_command]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/eye/system.rb
CHANGED
@@ -67,11 +67,14 @@ module Eye::System
|
|
67
67
|
{:pid => pid}
|
68
68
|
|
69
69
|
rescue Timeout::Error => ex
|
70
|
-
send_signal(pid, 9)
|
70
|
+
send_signal(pid, 9) if pid
|
71
71
|
{:error => ex}
|
72
72
|
|
73
73
|
rescue Errno::ENOENT, Errno::EACCES => ex
|
74
74
|
{:error => ex}
|
75
|
+
|
76
|
+
ensure
|
77
|
+
Process.detach(pid) if pid
|
75
78
|
end
|
76
79
|
|
77
80
|
# get table
|
data/lib/eye/utils.rb
CHANGED
@@ -2,4 +2,13 @@ module Eye::Utils
|
|
2
2
|
autoload :Tail, 'eye/utils/tail'
|
3
3
|
autoload :AliveArray, 'eye/utils/alive_array'
|
4
4
|
autoload :CelluloidChain, 'eye/utils/celluloid_chain'
|
5
|
+
|
6
|
+
def self.deep_clone(value)
|
7
|
+
case
|
8
|
+
when value.is_a?(Array) then value.map{|v| deep_clone(v) }
|
9
|
+
when value.is_a?(Hash) then value.inject({}){|r, (k, v)| r[ deep_clone(k) ] = deep_clone(v); r }
|
10
|
+
else value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
5
14
|
end
|
data/spec/checker_spec.rb
CHANGED
data/spec/client_server_spec.rb
CHANGED
@@ -78,6 +78,21 @@ describe "Intergration" do
|
|
78
78
|
@p3.last_scheduled_reason.should == 'restart by user'
|
79
79
|
end
|
80
80
|
|
81
|
+
it "restart forking named child" do
|
82
|
+
@p3.childs.size.should == 3
|
83
|
+
dead_pid = @p3.childs.keys.sample
|
84
|
+
|
85
|
+
@c.send_command(:restart, "child-#{dead_pid}").should == ["int:forking:child-#{dead_pid}"]
|
86
|
+
sleep 11 # while it
|
87
|
+
|
88
|
+
new_childs = @p3.childs.keys
|
89
|
+
new_childs.size.should == 3
|
90
|
+
new_childs.should_not include(dead_pid)
|
91
|
+
(@childs - [dead_pid]).each do |pid|
|
92
|
+
new_childs.should include(pid)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
81
96
|
it "restart missing" do
|
82
97
|
@old_pid1 = @p1.pid
|
83
98
|
@old_pid2 = @p2.pid
|
@@ -308,19 +308,64 @@ describe "Eye::Controller::Load" do
|
|
308
308
|
|
309
309
|
describe "load is exclusive" do
|
310
310
|
it "run double in time" do
|
311
|
-
t = Time.now
|
312
311
|
Eye::Control.async.command(:load, fixture("dsl/long_load.eye"))
|
313
312
|
Eye::Control.async.command(:load, fixture("dsl/long_load.eye"))
|
314
|
-
sleep 2.5
|
315
|
-
|
313
|
+
sleep 2.5
|
314
|
+
should_spend(0, 0.2) do
|
315
|
+
Eye::Control.command(:info).should be_a(String)
|
316
|
+
end
|
316
317
|
end
|
317
318
|
|
318
319
|
it "load with subloads" do
|
319
320
|
silence_warnings{
|
320
321
|
Eye::Control.command(:load, fixture("dsl/subfolder2.eye"))
|
321
322
|
}
|
322
|
-
|
323
|
+
should_spend(0, 0.2) do
|
324
|
+
Eye::Control.command(:info).should be_a(String)
|
325
|
+
end
|
323
326
|
end
|
324
327
|
end
|
325
328
|
|
329
|
+
describe "cleanup configs on delete" do
|
330
|
+
it "load config, delete 1 process, load another config" do
|
331
|
+
subject.load(fixture('dsl/load.eye'))
|
332
|
+
subject.process_by_name('p1').should be
|
333
|
+
|
334
|
+
subject.command(:delete, "p1"); sleep 0.1
|
335
|
+
subject.process_by_name('p1').should be_nil
|
336
|
+
|
337
|
+
subject.load(fixture('dsl/load2.eye'))
|
338
|
+
subject.process_by_name('p1').should be_nil
|
339
|
+
end
|
340
|
+
|
341
|
+
it "load config, delete 1 group, load another config" do
|
342
|
+
subject.load(fixture('dsl/load.eye'))
|
343
|
+
subject.group_by_name('gr1').should be
|
344
|
+
|
345
|
+
subject.command(:delete, "gr1"); sleep 0.1
|
346
|
+
subject.group_by_name('p1').should be_nil
|
347
|
+
|
348
|
+
subject.load(fixture('dsl/load2.eye'))
|
349
|
+
subject.group_by_name('gr1').should be_nil
|
350
|
+
end
|
351
|
+
|
352
|
+
it "load config, then delete app, and load it with changed app-name" do
|
353
|
+
subject.load(fixture('dsl/load3.eye'))
|
354
|
+
subject.command(:delete, "app3"); sleep 0.1
|
355
|
+
subject.load(fixture('dsl/load4.eye')).should include(error: false)
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should update only changed apps" do
|
360
|
+
mock(subject).update_or_create_application('app1', is_a(Hash))
|
361
|
+
mock(subject).update_or_create_application('app2', is_a(Hash))
|
362
|
+
subject.load(fixture('dsl/load.eye'))
|
363
|
+
|
364
|
+
mock(subject).update_or_create_application('app3', is_a(Hash))
|
365
|
+
subject.load(fixture('dsl/load2.eye'))
|
366
|
+
|
367
|
+
mock(subject).update_or_create_application('app3', is_a(Hash))
|
368
|
+
subject.load(fixture('dsl/load3.eye'))
|
369
|
+
end
|
370
|
+
|
326
371
|
end
|
data/spec/dsl/chain_spec.rb
CHANGED
@@ -10,28 +10,34 @@ describe "Eye::Dsl::Chain" do
|
|
10
10
|
process("3") do
|
11
11
|
pid_file "3"
|
12
12
|
end
|
13
|
+
|
14
|
+
group :yy do
|
15
|
+
end
|
13
16
|
end
|
14
17
|
E
|
15
|
-
|
18
|
+
|
16
19
|
h = {
|
17
|
-
"bla" => {
|
18
|
-
:
|
19
|
-
|
20
|
-
:restart=>{:grace=>5, :action=>:restart}},
|
20
|
+
"bla" => {
|
21
|
+
:name=>"bla",
|
22
|
+
:chain=>{:start=>{:grace=>5, :action=>:start}, :restart=>{:grace=>5, :action=>:restart}},
|
21
23
|
:groups=>{
|
22
|
-
"__default__"=>{
|
23
|
-
:
|
24
|
-
|
25
|
-
|
24
|
+
"__default__"=>{
|
25
|
+
:name=>"__default__",
|
26
|
+
:chain=>{:start=>{:grace=>5, :action=>:start}, :restart=>{:grace=>5, :action=>:restart}},
|
27
|
+
:application=>"bla",
|
26
28
|
:processes=>{
|
27
29
|
"3"=>{
|
28
|
-
:
|
29
|
-
:restart=>{:grace=>5, :action=>:restart}},
|
30
|
-
:pid_file=>"3",
|
30
|
+
:name=>"3",
|
31
|
+
:chain=>{:start=>{:grace=>5, :action=>:start}, :restart=>{:grace=>5, :action=>:restart}},
|
31
32
|
:application=>"bla",
|
32
33
|
:group=>"__default__",
|
33
|
-
:
|
34
|
-
|
34
|
+
:pid_file=>"3"}}},
|
35
|
+
"yy"=>{
|
36
|
+
:name=>"yy",
|
37
|
+
:chain=>{:start=>{:grace=>5, :action=>:start}, :restart=>{:grace=>5, :action=>:restart}},
|
38
|
+
:application=>"bla", :processes=>{}}}}
|
39
|
+
}
|
40
|
+
|
35
41
|
Eye::Dsl.parse_apps(conf).should == h
|
36
42
|
end
|
37
43
|
|
data/spec/dsl/checks_spec.rb
CHANGED
@@ -191,12 +191,29 @@ describe "Eye::Dsl checks" do
|
|
191
191
|
|
192
192
|
checks :socket, :addr => "unix:/tmp/1", :expect_data => Proc.new{|data| data == 1}
|
193
193
|
end
|
194
|
+
|
195
|
+
process("3") do
|
196
|
+
pid_file "3.pid"
|
197
|
+
|
198
|
+
checks :socket, :addr => "unix:/tmp/3", :expect_data => /regexp/
|
199
|
+
end
|
200
|
+
|
201
|
+
process("2") do
|
202
|
+
pid_file "2.pid"
|
203
|
+
|
204
|
+
checks :socket, :addr => "unix:/tmp/2", :expect_data => Proc.new{|data| data == 1}
|
205
|
+
end
|
206
|
+
|
194
207
|
end
|
195
208
|
E
|
196
209
|
res = Eye::Dsl.parse_apps(conf)
|
197
210
|
proc = res['bla'][:groups]['__default__'][:processes]['1'][:checks][:socket][:expect_data]
|
198
211
|
proc[0].should == false
|
199
212
|
proc[1].should == true
|
213
|
+
|
214
|
+
proc = res['bla'][:groups]['__default__'][:processes]['2'][:checks][:socket][:expect_data]
|
215
|
+
proc[0].should == false
|
216
|
+
proc[1].should == true
|
200
217
|
end
|
201
218
|
|
202
219
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "Eye::Dsl notify" do
|
4
|
+
it "integration" do
|
5
|
+
conf = <<-E
|
6
|
+
Eye.config do
|
7
|
+
mail :host => "mx.some.host.ru", :port => 25
|
8
|
+
|
9
|
+
contact :vasya, :mail, "vasya@mail.ru"
|
10
|
+
contact :petya, :mail, "petya@mail.ru", :port => 1111
|
11
|
+
|
12
|
+
contact_group :idiots do
|
13
|
+
contact :idiot1, :mail, "idiot1@mail.ru"
|
14
|
+
contact :idiot2, :mail, "idiot1@mail.ru", :port => 1111
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Eye.application :bla do
|
19
|
+
notify :vasya
|
20
|
+
notify :idiots, :crit
|
21
|
+
|
22
|
+
group :gr1 do
|
23
|
+
notify :petya
|
24
|
+
notify :idiot1, :warn
|
25
|
+
end
|
26
|
+
end
|
27
|
+
E
|
28
|
+
res = Eye::Dsl.parse(conf)
|
29
|
+
|
30
|
+
res.should == {
|
31
|
+
:applications => {
|
32
|
+
"bla"=>{:name=>"bla",
|
33
|
+
:notify=>{"vasya"=>:crit, "idiots"=>:crit},
|
34
|
+
:groups=>{"gr1"=>{:name=>"gr1",
|
35
|
+
:notify=>{"vasya"=>:crit, "idiots"=>:crit, "petya"=>:crit, "idiot1"=>:warn}, :application=>"bla", :processes=>{}}}}},
|
36
|
+
:config => {
|
37
|
+
:mail=>{:host=>"mx.some.host.ru", :port => 25, :type => :mail},
|
38
|
+
:contacts=>{
|
39
|
+
"vasya"=>{:name=>"vasya", :type=>:mail, :contact=>"vasya@mail.ru", :opts=>{}},
|
40
|
+
"petya"=>{:name=>"petya", :type=>:mail, :contact=>"petya@mail.ru", :opts=>{:port=>1111}},
|
41
|
+
'idiots'=>[{:name=>"idiot1", :type=>:mail, :contact=>"idiot1@mail.ru", :opts=>{}}, {:name=>"idiot2", :type=>:mail, :contact=>"idiot1@mail.ru", :opts=>{:port=>1111}}],
|
42
|
+
"idiot1"=>{:name=>"idiot1", :type=>:mail, :contact=>"idiot1@mail.ru", :opts=>{}},
|
43
|
+
"idiot2"=>{:name=>"idiot2", :type=>:mail, :contact=>"idiot1@mail.ru", :opts=>{:port=>1111}}}}}
|
44
|
+
end
|
45
|
+
|
46
|
+
it "valid contact type" do
|
47
|
+
conf = <<-E
|
48
|
+
Eye.config do
|
49
|
+
contact :vasya, :mail, "vasya@mail.ru", :port => 25, :host => "localhost"
|
50
|
+
end
|
51
|
+
E
|
52
|
+
Eye::Dsl.parse(conf)[:config].should == {:contacts=>{
|
53
|
+
"vasya"=>{:name=>"vasya", :type=>:mail, :contact=>"vasya@mail.ru", :opts=>{:port => 25, :host => "localhost"}}}}
|
54
|
+
end
|
55
|
+
|
56
|
+
it "raise on unknown contact type" do
|
57
|
+
conf = <<-E
|
58
|
+
Eye.config do
|
59
|
+
contact :vasya, :dddd, "vasya@mail.ru"
|
60
|
+
end
|
61
|
+
E
|
62
|
+
expect{ Eye::Dsl.parse(conf) }.to raise_error(Eye::Dsl::Error)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "raise on unknown additional_options" do
|
66
|
+
conf = <<-E
|
67
|
+
Eye.config do
|
68
|
+
contact :vasya, :mail, "vasya@mail.ru", :bla => 1
|
69
|
+
end
|
70
|
+
E
|
71
|
+
expect{ Eye::Dsl.parse(conf) }.to raise_error(Eye::Checker::Validation::Error)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "set notify inherited" do
|
75
|
+
conf = <<-E
|
76
|
+
Eye.app :bla do
|
77
|
+
notify :vasya
|
78
|
+
|
79
|
+
group :bla do
|
80
|
+
end
|
81
|
+
end
|
82
|
+
E
|
83
|
+
Eye::Dsl.parse_apps(conf).should == {
|
84
|
+
"bla" => {:name=>"bla",
|
85
|
+
:notify=>{"vasya"=>:crit},
|
86
|
+
:groups=>{"bla"=>{:name=>"bla",
|
87
|
+
:notify=>{"vasya"=>:crit}, :application=>"bla", :processes=>{}}}}}
|
88
|
+
end
|
89
|
+
|
90
|
+
it "clear notify with nonotify" do
|
91
|
+
conf = <<-E
|
92
|
+
Eye.app :bla do
|
93
|
+
notify :vasya
|
94
|
+
|
95
|
+
group :bla do
|
96
|
+
nonotify :vasya
|
97
|
+
end
|
98
|
+
end
|
99
|
+
E
|
100
|
+
Eye::Dsl.parse_apps(conf).should == {
|
101
|
+
"bla" => {:name=>"bla",
|
102
|
+
:notify=>{"vasya"=>:crit},
|
103
|
+
:groups=>{"bla"=>{:name=>"bla", :notify=>{}, :application=>"bla", :processes=>{}}}}}
|
104
|
+
end
|
105
|
+
end
|