flapjack 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -71,12 +71,13 @@ module Flapjack
71
71
  end
72
72
 
73
73
  get '/' do
74
- self_stats
74
+ check_stats
75
+ entity_stats
75
76
  haml :index
76
77
  end
77
78
 
78
79
  get '/checks_all' do
79
- self_stats
80
+ check_stats
80
81
  @adjective = 'all'
81
82
 
82
83
  # TODO (?) recast as Entity.all do |e|; e.checks.do |ec|; ...
@@ -89,7 +90,7 @@ module Flapjack
89
90
  end
90
91
 
91
92
  get '/checks_failing' do
92
- self_stats
93
+ check_stats
93
94
  @adjective = 'failing'
94
95
 
95
96
  @states = redis.zrange('failed_checks', 0, -1).map {|key|
@@ -102,11 +103,15 @@ module Flapjack
102
103
 
103
104
  get '/self_stats' do
104
105
  self_stats
106
+ entity_stats
107
+ check_stats
105
108
  haml :self_stats
106
109
  end
107
110
 
108
111
  get '/self_stats.json' do
109
112
  self_stats
113
+ entity_stats
114
+ check_stats
110
115
  {
111
116
  'events_queued' => @events_queued,
112
117
  'all_entities' => @count_all_entities,
@@ -128,7 +133,7 @@ module Flapjack
128
133
  'average' => @event_rate_all,
129
134
  }
130
135
  },
131
- 'total_keys' => @keys.length,
136
+ 'total_keys' => @dbsize,
132
137
  'uptime' => @uptime_string,
133
138
  'boottime' => @boot_time,
134
139
  'current_time' => Time.now,
@@ -137,22 +142,22 @@ module Flapjack
137
142
  end
138
143
 
139
144
  get '/entities_all' do
140
- self_stats
145
+ entity_stats
141
146
  @adjective = 'all'
142
147
  @entities = Flapjack::Data::Entity.find_all_with_checks(:redis => redis)
143
148
  haml :entities
144
149
  end
145
150
 
146
151
  get '/entities_failing' do
147
- self_stats
152
+ entity_stats
148
153
  @adjective = 'failing'
149
154
  @entities = Flapjack::Data::Entity.find_all_with_failing_checks(:redis => redis)
150
155
  haml :entities
151
156
  end
152
157
 
153
158
  get '/entity/:entity' do
154
- self_stats
155
159
  @entity = params[:entity]
160
+ entity_stats
156
161
  @states = redis.keys("#{@entity}:*:states").map { |r|
157
162
  parts = r.split(':')[0..1]
158
163
  [parts[0], parts[1]] + entity_check_state(parts[0], parts[1])
@@ -167,7 +172,8 @@ module Flapjack
167
172
  entity_check = get_entity_check(@entity, @check)
168
173
  return 404 if entity_check.nil?
169
174
 
170
- self_stats
175
+ check_stats
176
+
171
177
  last_change = entity_check.last_change
172
178
 
173
179
  @check_state = entity_check.state
@@ -263,13 +269,13 @@ module Flapjack
263
269
  end
264
270
 
265
271
  get '/contacts' do
266
- self_stats
272
+ #self_stats
267
273
  @contacts = Flapjack::Data::Contact.all(:redis => redis)
268
274
  haml :contacts
269
275
  end
270
276
 
271
277
  get "/contacts/:contact" do
272
- self_stats
278
+ #self_stats
273
279
  contact_id = params[:contact]
274
280
 
275
281
  if contact_id
@@ -332,13 +338,8 @@ module Flapjack
332
338
  @fqdn = `/bin/hostname -f`.chomp
333
339
  @pid = Process.pid
334
340
  @instance_id = "#{@fqdn}:#{@pid}"
335
- @version = Flapjack::VERSION
336
341
 
337
- @keys = redis.keys '*'
338
- @count_all_checks = redis.keys('check:*:*').length
339
- @count_failing_checks = redis.zcard 'failed_checks'
340
- @count_all_entities = Flapjack::Data::Entity.find_all_with_checks(:redis => redis).length
341
- @count_failing_entities = Flapjack::Data::Entity.find_all_with_failing_checks(:redis => redis).length
342
+ @dbsize = redis.dbsize
342
343
  @executive_instances = redis.keys("executive_instance:*").map {|i|
343
344
  [ i.match(/executive_instance:(.*)/)[1], redis.hget(i, 'boot_time').to_i ]
344
345
  }.sort {|a, b| b[1] <=> a[1]}
@@ -352,6 +353,17 @@ module Flapjack
352
353
  @events_queued = redis.llen('events')
353
354
  end
354
355
 
356
+ def entity_stats
357
+ @count_all_entities = Flapjack::Data::Entity.find_all_with_checks(:redis => redis).length
358
+ @count_failing_entities = Flapjack::Data::Entity.find_all_with_failing_checks(:redis => redis).length
359
+ end
360
+
361
+ def check_stats
362
+ @count_all_checks = redis.keys('check:*:*').length
363
+ @count_failing_checks = redis.zcard 'failed_checks'
364
+ end
365
+
366
+
355
367
  end
356
368
 
357
369
  end
@@ -1,8 +1,8 @@
1
1
  %div{:class => "container"}
2
2
  %p{:class => "muted credit"}
3
- = "Flapjack version #{@version} | "
3
+ = "Flapjack version #{::Flapjack::VERSION} | "
4
4
  %a{:href => 'http://flapjack-project.com'}
5
5
  Flapjack Project
6
6
  = " | "
7
7
  %a{:href => 'https://github.com/flpjck/flapjack'}
8
- Flapjack on Github
8
+ Flapjack on Github
@@ -21,11 +21,17 @@
21
21
  %th Last Update
22
22
  %th Last Notification
23
23
  - @states.each do |entity, check, status, changed, updated, in_unscheduled_outage, in_scheduled_outage, notified_kind, notified|
24
- %tr
24
+ - colour_class = status
25
+ - case status
26
+ - when 'critical', 'unknown'
27
+ - colour_class = 'error'
28
+ - when 'ok', 'up'
29
+ - colour_class = 'success'
30
+ %tr{:class => colour_class}
25
31
  %td
26
32
  - link = "/check?entity=" + CGI.escape(entity) + "&check=" + CGI.escape(check)
27
33
  %a(title='check detail' href=link) #{check}
28
- %td{:class => status}
34
+ %td
29
35
  = status.upcase
30
36
  = " (Acknowledged)" if in_unscheduled_outage
31
37
  = " (Scheduled Maintenance)" if in_scheduled_outage
@@ -39,7 +39,7 @@
39
39
  %td #{@event_rate_all} events per second
40
40
  %tr
41
41
  %td Total keys in redis
42
- %td #{@keys.length}
42
+ %td #{@dbsize}
43
43
  %tr
44
44
  %td Uptime
45
45
  %td= @uptime_string
@@ -44,8 +44,27 @@ class Hash
44
44
  end
45
45
  end
46
46
 
47
- # we don't want to stop the entire EM reactor when we stop a web server
48
47
  module Thin
48
+
49
+ # see https://github.com/flpjck/flapjack/issues/169
50
+ class Request
51
+ class EqlTempfile < ::Tempfile
52
+ def eql?(obj)
53
+ obj.equal?(self) && (obj == self)
54
+ end
55
+ end
56
+
57
+ def move_body_to_tempfile
58
+ current_body = @body
59
+ current_body.rewind
60
+ @body = Thin::Request::EqlTempfile.new(BODY_TMPFILE)
61
+ @body.binmode
62
+ @body << current_body.read
63
+ @env[RACK_INPUT] = @body
64
+ end
65
+ end
66
+
67
+ # we don't want to stop the entire EM reactor when we stop a web server
49
68
  module Backends
50
69
  class Base
51
70
  def stop!
@@ -110,13 +110,7 @@ module Flapjack
110
110
 
111
111
  def start
112
112
  @fiber = Fiber.new {
113
- begin
114
- @pikelet.start
115
- rescue Exception => e
116
- trace = e.backtrace.join("\n")
117
- @logger.fatal "#{e.message}\n#{trace}"
118
- stop
119
- end
113
+ @pikelet.start
120
114
  }
121
115
  super
122
116
  @fiber.resume
@@ -172,13 +166,7 @@ module Flapjack
172
166
 
173
167
  def start
174
168
  @fiber = Fiber.new {
175
- begin
176
- @worker.work(0.1)
177
- rescue Exception => e
178
- trace = e.backtrace.join("\n")
179
- @logger.fatal "#{e.message}\n#{trace}"
180
- stop
181
- end
169
+ @worker.work(0.1)
182
170
  }
183
171
  super
184
172
  @klass.start if @klass.respond_to?(:start)
@@ -225,11 +213,14 @@ module Flapjack
225
213
  if @config
226
214
  @port = @config['port']
227
215
  @port = @port.nil? ? nil : @port.to_i
216
+ @timeout = @config['timeout']
217
+ @timeout = @timeout.nil? ? 300 : @timeout.to_i
228
218
  end
229
219
  @port = 3001 if (@port.nil? || @port <= 0 || @port > 65535)
230
220
 
231
221
  @server = ::Thin::Server.new('0.0.0.0', @port,
232
222
  @klass, :signals => false)
223
+ @server.timeout = @timeout
233
224
  end
234
225
 
235
226
  def start
@@ -52,5 +52,21 @@ module Flapjack
52
52
  return obj
53
53
  end
54
54
 
55
+ # The passed block will be provided each value from the args
56
+ # and must return array pairs [key, value] representing members of
57
+ # the hash this method returns. Keys should be unique -- if they're
58
+ # not, the earlier pair for that key will be overwritten.
59
+ def hashify(*args, &block)
60
+ key_value_pairs = args.map {|a| yield(a) }
61
+
62
+ # if using Ruby 1.9,
63
+ # Hash[ key_value_pairs ]
64
+ # is all that's needed, but for Ruby 1.8 compatability, these must
65
+ # be flattened and the resulting array unpacked. flatten(1) only
66
+ # flattens the arrays constructed in the block, it won't mess up
67
+ # any values (or keys) that are themselves arrays/hashes.
68
+ Hash[ *( key_value_pairs.flatten(1) )]
69
+ end
70
+
55
71
  end
56
72
  end
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  module Flapjack
4
- VERSION = "0.7.2"
4
+ VERSION = "0.7.3"
5
5
  end
@@ -54,6 +54,34 @@ describe Flapjack::Coordinator do
54
54
  fc.stop
55
55
  end
56
56
 
57
+ it "handles an exception raised by a pikelet and shuts down" do
58
+ setup_logger
59
+ logger.should_receive(:fatal)
60
+
61
+ cfg = {'executive' => {'enabled' => 'yes'}}
62
+ EM.should_receive(:synchrony).and_yield
63
+ config.should_receive(:for_redis).and_return({})
64
+ config.should_receive(:all).and_return(cfg)
65
+
66
+ executive = mock('executive')
67
+ executive.should_receive(:start).and_raise(RuntimeError)
68
+ executive.should_receive(:stop)
69
+ executive.should_receive(:update_status)
70
+ executive.should_receive(:status).exactly(3).times.and_return('stopped')
71
+
72
+ fc = Flapjack::Coordinator.new(config)
73
+ Flapjack::Pikelet.should_receive(:create).with('executive',
74
+ :config => cfg['executive'], :redis_config => {}).and_return(executive)
75
+
76
+ fiber.should_receive(:resume)
77
+ Fiber.should_receive(:new).and_yield.and_return(fiber)
78
+
79
+ EM.should_receive(:stop)
80
+
81
+ fc.start(:signals => false)
82
+ fc.stop
83
+ end
84
+
57
85
  it "traps system signals and shuts down" do
58
86
  setup_logger
59
87
 
@@ -143,6 +171,7 @@ describe Flapjack::Coordinator do
143
171
  new_config.should_receive(:all).and_return(new_cfg)
144
172
 
145
173
  executive = mock('executive')
174
+ executive.should_not_receive(:start)
146
175
  executive.should_receive(:type).exactly(3).times.and_return('executive')
147
176
  executive.should_receive(:reload).with(new_cfg['executive']).and_return(true)
148
177
  executive.should_not_receive(:stop)
@@ -9,13 +9,10 @@ require 'flapjack/data/notification_rule'
9
9
 
10
10
  describe Flapjack::Data::Contact, :redis => true do
11
11
 
12
- weekdays_8_18 = IceCube::Schedule.new(Time.local(2013,2,1,8,0,0), :duration => 60 * 60 * 10)
13
- weekdays_8_18.add_recurrence_rule(IceCube::Rule.weekly.day(:monday, :tuesday, :wednesday, :thursday, :friday))
14
-
15
12
  let(:notification_rule_data) {
16
13
  {:entity_tags => ["database","physical"],
17
14
  :entities => ["foo-app-01.example.com"],
18
- :time_restrictions => [ weekdays_8_18.to_hash ],
15
+ :time_restrictions => [],
19
16
  :warning_media => ["email"],
20
17
  :critical_media => ["sms", "email"],
21
18
  :warning_blackhole => false,
@@ -1,22 +1,115 @@
1
1
  require 'spec_helper'
2
2
  require 'flapjack/data/notification_rule'
3
3
 
4
- describe Flapjack::Data::NotificationRule do
4
+ describe Flapjack::Data::NotificationRule, :redis => true do
5
5
 
6
- it "checks that a notification rule exists"
6
+ let(:weekdays_8_18) {
7
+ wd = IceCube::Schedule.new(Time.local(2013,2,1,8,0,0), :duration => 60 * 60 * 10)
8
+ wd.add_recurrence_rule(IceCube::Rule.weekly.day(:monday, :tuesday, :wednesday, :thursday, :friday))
9
+ wd = wd.to_hash
10
+ wd[:start_time] = wd.delete(:start_date)
11
+ wd[:rrules].first[:rule_type] = wd[:rrules].first[:rule_type].sub(/\AIceCube::(\w+)Rule\z/, '\1')
12
+ wd
13
+ }
7
14
 
8
- it "returns a notification rule if it exists"
15
+ let(:rule_data) {
16
+ {:contact_id => '23',
17
+ :entity_tags => ["database","physical"],
18
+ :entities => ["foo-app-01.example.com"],
19
+ :time_restrictions => [ weekdays_8_18 ],
20
+ :warning_media => ["email"],
21
+ :critical_media => ["sms", "email"],
22
+ :warning_blackhole => false,
23
+ :critical_blackhole => false
24
+ }
25
+ }
9
26
 
10
- it "does not return a notification rule if it does not exist"
27
+ let(:rule_id) { 'ABC123' }
11
28
 
12
- it "updates a notification rule"
29
+ let(:timezone) { ActiveSupport::TimeZone.new("Europe/Moscow") }
13
30
 
14
- it "checks whether tag or entity names match"
31
+ let(:existing_rule) {
32
+ Flapjack::Data::NotificationRule.add(rule_data, :redis => @redis)
33
+ }
15
34
 
16
- it "checks whether times match"
35
+ it "checks that a notification rule exists" do
36
+ Flapjack::Data::NotificationRule.exists_with_id?(existing_rule.id, :redis => @redis).should be_true
37
+ Flapjack::Data::NotificationRule.exists_with_id?('not_there', :redis => @redis).should be_false
38
+ end
17
39
 
18
- it "checks if blackhole settings for a rule match a severity level"
40
+ it "returns a notification rule if it exists" do
41
+ rule = Flapjack::Data::NotificationRule.find_by_id(existing_rule.id, :redis => @redis)
42
+ rule.should_not be_nil
43
+ end
19
44
 
20
- it "returns the media settings for a rule's severity level"
45
+ it "does not return a notification rule if it does not exist" do
46
+ rule = Flapjack::Data::NotificationRule.find_by_id('not_there', :redis => @redis)
47
+ rule.should be_nil
48
+ end
49
+
50
+ it "updates a notification rule" do
51
+ rule = existing_rule
52
+
53
+ expect {
54
+ rule_data[:warning_blackhole] = true
55
+ success = rule.update(rule_data)
56
+ success.should be_true
57
+ }.to change { rule.warning_blackhole }.from(false).to(true)
58
+ end
59
+
60
+ it "converts time restriction data to an IceCube schedule" do
61
+ sched = Flapjack::Data::NotificationRule.
62
+ time_restriction_to_icecube_schedule(weekdays_8_18, timezone)
63
+ sched.should_not be_nil
64
+ end
65
+
66
+ it "generates a JSON string representing its data" do
67
+ rule = existing_rule
68
+ # bit of extra hackery for the inserted ID values
69
+ rule.to_json.should == {:id => rule.id}.merge(rule_data).to_json
70
+ end
71
+
72
+ it "checks whether entity names match" do
73
+ rule = existing_rule
74
+
75
+ rule.match_entity?('foo-app-01.example.com').should be_true
76
+ rule.match_entity?('foo-app-02.example.com').should be_false
77
+ end
78
+
79
+ pending "check whether entity tags match"
80
+
81
+ it "checks if blackhole settings for a rule match a severity level" do
82
+ rule_data[:warning_blackhole] = true
83
+ rule = Flapjack::Data::NotificationRule.add(rule_data, :redis => @redis)
84
+
85
+ rule.blackhole?('warning').should be_true
86
+ rule.blackhole?('critical').should be_false
87
+ end
88
+
89
+ it "returns the media settings for a rule's severity level" do
90
+ rule = existing_rule
91
+ rule.media_for_severity('warning').should == ['email']
92
+ rule.media_for_severity('critical').should =~ ['email', 'sms']
93
+ end
94
+
95
+ context 'validation' do
96
+
97
+ it "fails to add a notification rule with invalid data" do
98
+ rule_data[:entities] = []
99
+ rule_data[:entity_tags] = []
100
+ rule = Flapjack::Data::NotificationRule.add(rule_data, :redis => @redis)
101
+ rule.should be_nil
102
+ end
103
+
104
+ it "fails to update a notification rule with invalid data" do
105
+ rule = Flapjack::Data::NotificationRule.add(rule_data, :redis => @redis)
106
+ expect {
107
+ rule_data[:entities] = [57]
108
+ success = rule.update(rule_data)
109
+ success.should be_false
110
+ }.not_to change { rule.entities }
111
+ end
112
+
113
+ end
21
114
 
22
115
  end