flapjack 0.6.51 → 0.6.52

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.
@@ -152,3 +152,11 @@ Feature: events
152
152
  Given check 'abc' for entity 'def' is in a failure state
153
153
  When an acknowledgement event is received for check 'abc' on entity 'def'
154
154
  Then a notification should be generated for check 'abc' on entity 'def'
155
+
156
+ Scenario: Brief failure then OK
157
+ Given check 'abc' for entity 'def' is in an ok state
158
+ When a failure event is received for check 'abc' on entity 'def'
159
+ And 10 seconds passes
160
+ And an ok event is received for check 'abc' on entity 'def'
161
+ Then a notification should not be generated for check 'abc' on entity 'def'
162
+
@@ -161,4 +161,3 @@ end
161
161
  Then /^show me the notifications?$/ do
162
162
  puts @app.logger.messages.join("\n")
163
163
  end
164
-
@@ -5,6 +5,8 @@
5
5
 
6
6
  require 'set'
7
7
 
8
+ require 'flapjack/data/entity'
9
+
8
10
  module Flapjack
9
11
 
10
12
  module Data
@@ -30,12 +32,14 @@ module Flapjack
30
32
  def self.delete_all(options = {})
31
33
  raise "Redis connection not set" unless redis = options[:redis]
32
34
 
33
- redis.del( redis.keys("contact:*") +
34
- redis.keys("contact_media:*") +
35
- # FIXME: when we do source tagging we can properly
36
- # clean up contacts_for: keys
37
- # redis.keys('contacts_for:*') +
38
- redis.keys("contact_pagerduty:*") )
35
+ keys_to_delete = redis.keys("contact:*") +
36
+ redis.keys("contact_media:*") +
37
+ # FIXME: when we do source tagging we can properly
38
+ # clean up contacts_for: keys
39
+ # redis.keys('contacts_for:*') +
40
+ redis.keys("contact_pagerduty:*")
41
+
42
+ redis.del(keys_to_delete) unless keys_to_delete.length == 0
39
43
  end
40
44
 
41
45
  def self.find_by_id(contact_id, options = {})
@@ -110,7 +110,12 @@ module Flapjack
110
110
  end
111
111
 
112
112
  def tags
113
- @tags ||= load_tags
113
+ @tags ||= ::Set.new( @redis.keys("#{TAG_PREFIX}:*").inject([]) {|memo, entity_tag|
114
+ if Flapjack::Data::Tag.find(entity_tag, :redis => @redis).include?(@id.to_s)
115
+ memo << entity_tag.sub(/^#{TAG_PREFIX}:/, '')
116
+ end
117
+ memo
118
+ } )
114
119
  end
115
120
 
116
121
  def add_tags(*enum)
@@ -139,16 +144,6 @@ module Flapjack
139
144
  @logger = options[:logger]
140
145
  end
141
146
 
142
- def load_tags
143
- entity_tags = ::Set.new
144
- tag_data = @redis.keys("#{TAG_PREFIX}:*").inject([]) do |memo, entity_tag|
145
- tag = Flapjack::Data::Tag.find(entity_tag, :redis => @redis)
146
- memo << entity_tag.sub(/^#{TAG_PREFIX}:/, '') if tag.include?(@id.to_s)
147
- memo
148
- end
149
- entity_tags.merge(tag_data)
150
- end
151
-
152
147
  end
153
148
 
154
149
  end
@@ -5,6 +5,7 @@ require 'yajl/json_gem'
5
5
  require 'flapjack/patches'
6
6
 
7
7
  require 'flapjack/data/contact'
8
+ require 'flapjack/data/event'
8
9
  require 'flapjack/data/entity'
9
10
 
10
11
  # TODO might want to split the class methods out to a separate class, DAO pattern
@@ -153,6 +154,7 @@ module Flapjack
153
154
  end
154
155
 
155
156
  # change the end time of a scheduled maintenance (including when one is current)
157
+ # TODO allow to update summary as well
156
158
  def update_scheduled_maintenance(start_time, patches = {})
157
159
 
158
160
  # check if there is such a scheduled maintenance period
@@ -162,7 +164,7 @@ module Flapjack
162
164
 
163
165
  if patches[:end_time]
164
166
  end_time = patches[:end_time]
165
- raise ArgumentError unless end_time > start_time
167
+ raise ArgumentError, "end time must be after start time" unless end_time > start_time
166
168
  old_end_time = start_time + old_duration
167
169
  duration = end_time - start_time
168
170
  @redis.zadd("#{@key}:scheduled_maintenances", duration, start_time)
@@ -170,7 +172,6 @@ module Flapjack
170
172
 
171
173
  # scheduled maintenance periods have changed, revalidate
172
174
  update_current_scheduled_maintenance(:revalidate => true)
173
-
174
175
  end
175
176
 
176
177
  # delete a scheduled maintenance
@@ -404,17 +405,6 @@ module Flapjack
404
405
  }
405
406
  end
406
407
 
407
- # returns an array of pagerduty credentials. If more than one contact for this entity_check
408
- # has pagerduty credentials then there'll be one hash in the array for each set of
409
- # credentials.
410
- def pagerduty_credentials(options)
411
- self.contacts.inject([]) {|ret, contact|
412
- cred = contact.pagerduty_credentials
413
- ret << cred if cred
414
- ret
415
- }
416
- end
417
-
418
408
  # takes a check, looks up contacts that are interested in this check (or in the check's entity)
419
409
  # and returns an array of contact records
420
410
  def contacts
@@ -6,7 +6,7 @@ module Flapjack
6
6
  module Data
7
7
  class Event
8
8
 
9
- attr_accessor :previous_state
9
+ attr_accessor :previous_state, :previous_state_duration
10
10
 
11
11
  # Helper method for getting the next event.
12
12
  #
@@ -168,6 +168,7 @@ module Flapjack
168
168
  end
169
169
 
170
170
  event.previous_state = entity_check.state
171
+ event.previous_state_duration = Time.now.to_i - entity_check.last_change
171
172
  @logger.info("No previous state for event #{event.id}") if event.previous_state.nil?
172
173
 
173
174
  # If there is a state change, update record with: the time, the new state
@@ -6,6 +6,9 @@ module Flapjack
6
6
  module Filters
7
7
 
8
8
  # * If the service event’s state is ok and the previous state was ok, don’t alert
9
+ # * If the service event's state is ok and the previous notification was a recovery, don't alert
10
+ # * If the service event's state is ok and the previous state was not ok and for less than 30
11
+ # seconds, don't alert
9
12
  # * If the service event's state is ok and there is unscheduled downtime set, end the unscheduled
10
13
  # downtime
11
14
  class Ok
@@ -20,8 +23,23 @@ module Flapjack
20
23
  result = true
21
24
  end
22
25
 
23
- # end any unscheduled downtime
24
26
  entity_check = Flapjack::Data::EntityCheck.for_event_id(event.id, :redis => @persistence)
27
+
28
+ last_notification = entity_check.last_notification
29
+ @log.debug("Filter: Ok: last notification: #{last_notification.inspect}")
30
+ if last_notification[:type] == 'recovery'
31
+ @log.debug("Filter: Ok: last notification was a recovery, so blocking")
32
+ result = true
33
+ end
34
+
35
+ if event.previous_state != 'ok'
36
+ if event.previous_state_duration < 30
37
+ @log.debug("Filter: Ok: previous non ok state was for less than 30 seconds, so blocking")
38
+ result = true
39
+ end
40
+ end
41
+
42
+ # end any unscheduled downtime
25
43
  entity_check.end_unscheduled_maintenance
26
44
  end
27
45
 
@@ -48,7 +48,10 @@ module Flapjack
48
48
 
49
49
  def main
50
50
  logger.debug("pagerduty gateway - commencing main method")
51
- raise "Can't connect to the pagerduty API" unless test_pagerduty_connection
51
+ while not test_pagerduty_connection do
52
+ logger.error("Can't connect to the pagerduty API, retrying after 10 seconds")
53
+ EM::Synchrony.sleep(10)
54
+ end
52
55
 
53
56
  # TODO: only clear this if there isn't another pagerduty gateway instance running
54
57
  # or better, include an instance ID in the semaphore key name
@@ -160,16 +163,25 @@ module Flapjack
160
163
  @logger.debug "found unacknowledged failing checks as follows: " + unacknowledged_failing_checks.join(', ')
161
164
 
162
165
  unacknowledged_failing_checks.each do |entity_check|
163
- pagerduty_credentials = entity_check.pagerduty_credentials(:redis => @redis_timer)
166
+
167
+ # If more than one contact for this entity_check has pagerduty
168
+ # credentials then there'll be one hash in the array for each set of
169
+ # credentials.
170
+ ec_credentials = entity_check.contacts.inject([]) {|ret, contact|
171
+ cred = contact.pagerduty_credentials
172
+ ret << cred if cred
173
+ ret
174
+ }
175
+
164
176
  check = entity_check.check
165
177
 
166
- if pagerduty_credentials.empty?
178
+ if ec_credentials.empty?
167
179
  @logger.debug("No pagerduty credentials found for #{entity_check.entity_name}:#{check}, skipping")
168
180
  next
169
181
  end
170
182
 
171
183
  # FIXME: try each set of credentials until one works (may have stale contacts turning up)
172
- options = pagerduty_credentials.first.merge('check' => "#{entity_check.entity_name}:#{check}")
184
+ options = ec_credentials.first.merge('check' => "#{entity_check.entity_name}:#{check}")
173
185
 
174
186
  acknowledged = pagerduty_acknowledged?(options)
175
187
  if acknowledged.nil?
@@ -238,6 +238,12 @@ module Flapjack
238
238
  haml :contact
239
239
  end
240
240
 
241
+ protected
242
+
243
+ def render_haml(file, scope)
244
+ Haml::Engine.new(File.read(File.dirname(__FILE__) + '/web/views/' + file)).render(scope)
245
+ end
246
+
241
247
  private
242
248
 
243
249
  def get_entity_check(entity, check)
@@ -1,8 +1,7 @@
1
1
  !!! 5
2
- - nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
3
- - nav = nav_haml.render
4
- - css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
5
- - css = css_haml.render
2
+ - nav = render_haml('_nav.haml', self)
3
+ - css = render_haml('_css.haml', self)
4
+ - check_path_escaped = URI.escape(@entity, ' #&;/=?') << '/' << URI.escape(@check, ' #&;/=?')
6
5
  %html
7
6
  %head
8
7
  %title Flapjack - Check: #{@entity}:#{@check}
@@ -11,7 +10,7 @@
11
10
  %div#wrapper
12
11
  = nav
13
12
  %h1 #{@check} on #{@entity}
14
- %form{:action => "/acknowledgements/#{@entity}/#{@check}", :method => "post"}
13
+ %form{:action => "/acknowledgements/#{check_path_escaped}", :method => "post"}
15
14
  %h2
16
15
  State: #{@check_state ? @check_state.upcase : ''}
17
16
  - if (['warning', 'critical'].include?(@check_state) and !(@current_unscheduled_maintenance || @current_scheduled_maintenance))
@@ -26,7 +25,7 @@
26
25
  %input{:type => 'text', :name => 'duration', :value => ''}
27
26
  - if @current_unscheduled_maintenance
28
27
  %h3 (Acknowledged - #{@current_unscheduled_maintenance[:summary]})
29
- %form{:action => "/end_unscheduled_maintenance/#{@entity}/#{@check}", :method => "post"}
28
+ %form{:action => "/end_unscheduled_maintenance/#{check_path_escaped}", :method => "post"}
30
29
  %p
31
30
  %input{:type => 'submit', :value => 'End Unscheduled Maintenance (Unacknowledge)', :class => 'button'}
32
31
  - if @current_scheduled_maintenance
@@ -93,12 +92,12 @@
93
92
  %td
94
93
  - if end_time > Time.now.to_i
95
94
  - if start_time > Time.now.to_i
96
- %form{ :action => "/scheduled_maintenances/#{@entity}/#{@check}", :method => "post"}
95
+ %form{ :action => "/scheduled_maintenances/#{check_path_escaped}", :method => "post"}
97
96
  %input{:type => 'hidden', :name => '_method', :value => 'delete'}
98
97
  %input{:type => 'hidden', :name => 'start_time', :value => start_time}
99
98
  %input{:type => 'submit', :value => 'Delete', :class => 'button'}
100
99
  - else
101
- %form{ :action => "/scheduled_maintenances/#{@entity}/#{@check}", :method => "post"}
100
+ %form{ :action => "/scheduled_maintenances/#{check_path_escaped}", :method => "post"}
102
101
  %input{:type => 'hidden', :name => '_method', :value => 'patch'}
103
102
  %input{:type => 'hidden', :name => 'start_time', :value => start_time}
104
103
  %input{:type => 'hidden', :name => 'end_time', :value => 'now'}
@@ -106,7 +105,7 @@
106
105
  - else
107
106
  %p No maintenance is scheduled
108
107
  %h4 Add Scheduled Maintenance
109
- %form{:action => "/scheduled_maintenances/#{@entity}/#{@check}", :method => "post"}
108
+ %form{:action => "/scheduled_maintenances/#{check_path_escaped}", :method => "post"}
110
109
  %fieldset
111
110
  %table
112
111
  %tr
@@ -1,8 +1,6 @@
1
1
  !!! 5
2
- - nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
3
- - nav = nav_haml.render
4
- - css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
5
- - css = css_haml.render
2
+ - nav = render_haml('_nav.haml', self)
3
+ - css = render_haml('_css.haml', self)
6
4
  %html
7
5
  %head
8
6
  %title Flapjack - Contact: #{@contact.name}
@@ -39,7 +37,7 @@
39
37
  %p= entity.name
40
38
  %td
41
39
  - checks.each do |check|
42
- - link = "/check?entity=#{entity.name}&check=#{check}"
40
+ - link = "/check?entity=#{CGI.escape(entity.name)}&check=#{CGI.escape(check)}"
43
41
  %p
44
42
  %a(title='check status' href=link) #{check}
45
43
  - else
@@ -1,8 +1,6 @@
1
1
  !!! 5
2
- - nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
3
- - nav = nav_haml.render
4
- - css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
5
- - css = css_haml.render
2
+ - nav = render_haml('_nav.haml', self)
3
+ - css = render_haml('_css.haml', self)
6
4
  %html
7
5
  %head
8
6
  %title Flapjack - Contacts
@@ -1,8 +1,6 @@
1
1
  !!! 5
2
- - nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
3
- - nav = nav_haml.render
4
- - css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
5
- - css = css_haml.render
2
+ - nav = render_haml('_nav.haml', self)
3
+ - css = render_haml('_css.haml', self)
6
4
  %html
7
5
  %head
8
6
  %title Flapjack
@@ -36,7 +34,7 @@
36
34
  %tr
37
35
  %td= entity
38
36
  %td
39
- - link = "/check?entity=" + entity + "&check=" + check
37
+ - link = "/check?entity=" + CGI.escape(entity) + "&check=" + CGI.escape(check)
40
38
  %a(title='check detail' href=link) #{check}
41
39
  %td{:class => status}
42
40
  = status.upcase
@@ -49,7 +47,3 @@
49
47
  - else
50
48
  - last_notified = 'never'
51
49
  %td= last_notified
52
-
53
- -#%ul
54
- -#- @keys.each do |key|
55
- -#%li= key
@@ -1,8 +1,6 @@
1
1
  !!! 5
2
- - nav_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_nav.haml'))
3
- - nav = nav_haml.render
4
- - css_haml = Haml::Engine.new(File.read(File.dirname(__FILE__) + '/_css.haml'))
5
- - css = css_haml.render
2
+ - nav = render_haml('_nav.haml', self)
3
+ - css = render_haml('_css.haml', self)
6
4
  %html
7
5
  %head
8
6
  %title Flapjack - Self Stats
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  module Flapjack
4
- VERSION = "0.6.51"
4
+ VERSION = "0.6.52"
5
5
  end
@@ -1,15 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  require 'flapjack/data/contact'
4
+ require 'flapjack/data/entity_check'
4
5
 
5
6
  describe Flapjack::Data::Contact, :redis => true do
6
7
 
7
- def add_contacts
8
+ before(:each) do
8
9
  Flapjack::Data::Contact.add({'id' => '362',
9
10
  'first_name' => 'John',
10
11
  'last_name' => 'Johnson',
11
- 'email' => 'johnj@example.com' },
12
+ 'email' => 'johnj@example.com',
13
+ 'media' => {
14
+ 'pagerduty' => {
15
+ 'service_key' => '123456789012345678901234',
16
+ 'subdomain' => 'flpjck',
17
+ 'username' => 'flapjack',
18
+ 'password' => 'very_secure'
19
+ }
20
+ }},
12
21
  :redis => @redis)
22
+
13
23
  Flapjack::Data::Contact.add({'id' => '363',
14
24
  'first_name' => 'Jane',
15
25
  'last_name' => 'Janeley',
@@ -18,8 +28,6 @@ describe Flapjack::Data::Contact, :redis => true do
18
28
  end
19
29
 
20
30
  it "returns a list of all contacts" do
21
- add_contacts
22
-
23
31
  contacts = Flapjack::Data::Contact.all(:redis => @redis)
24
32
  contacts.should_not be_nil
25
33
  contacts.should be_an(Array)
@@ -29,18 +37,12 @@ describe Flapjack::Data::Contact, :redis => true do
29
37
  end
30
38
 
31
39
  it "finds a contact by id" do
32
- add_contacts
33
-
34
40
  contact = Flapjack::Data::Contact.find_by_id('362', :redis => @redis)
35
41
  contact.should_not be_nil
36
42
  contact.name.should == "John Johnson"
37
43
  end
38
44
 
39
- it "finds all contacts for a check on an entity"
40
-
41
45
  it "deletes all contacts" do
42
- add_contacts
43
-
44
46
  Flapjack::Data::Contact.delete_all(:redis => @redis)
45
47
  contact = Flapjack::Data::Contact.find_by_id('362', :redis => @redis)
46
48
  contact.should be_nil
@@ -48,8 +50,44 @@ describe Flapjack::Data::Contact, :redis => true do
48
50
  contact.should be_nil
49
51
  end
50
52
 
51
- it "returns pagerduty credentials for a contact"
53
+ it "returns a list of entities and their checks for a contact" do
54
+ entity_name = 'abc-123'
55
+
56
+ Flapjack::Data::Entity.add({'id' => '5000',
57
+ 'name' => entity_name,
58
+ 'contacts' => ['362']},
59
+ :redis => @redis)
60
+
61
+ ec = Flapjack::Data::EntityCheck.for_entity_name(entity_name, 'PING', :redis => @redis)
62
+ t = Time.now.to_i
63
+ ec.update_state('ok', :timestamp => t, :summary => 'a')
52
64
 
53
- it "returns a list of entities for a contact"
65
+ contact = Flapjack::Data::Contact.find_by_id('362', :redis => @redis)
66
+ eandcs = contact.entities_and_checks
67
+ eandcs.should_not be_nil
68
+ eandcs.should be_an(Array)
69
+ eandcs.should have(1).entity_and_checks
70
+
71
+ eandc = eandcs.first
72
+ eandc.should be_a(Hash)
73
+
74
+ entity = eandc[:entity]
75
+ entity.name.should == entity_name
76
+ checks = eandc[:checks]
77
+ checks.should be_a(Set)
78
+ checks.should have(1).check
79
+ checks.should include('PING')
80
+ end
81
+
82
+ it "returns pagerduty credentials for a contact" do
83
+ contact = Flapjack::Data::Contact.find_by_id('362', :redis => @redis)
84
+ credentials = contact.pagerduty_credentials
85
+ credentials.should_not be_nil
86
+ credentials.should be_a(Hash)
87
+ credentials.should == {'service_key' => '123456789012345678901234',
88
+ 'subdomain' => 'flpjck',
89
+ 'username' => 'flapjack',
90
+ 'password' => 'very_secure'}
91
+ end
54
92
 
55
93
  end
@@ -13,8 +13,15 @@ describe Flapjack::Data::EntityCheck, :redis => true do
13
13
  let(:half_an_hour) { 30 * 60 }
14
14
 
15
15
  before(:each) do
16
+ Flapjack::Data::Contact.add({'id' => '362',
17
+ 'first_name' => 'John',
18
+ 'last_name' => 'Johnson',
19
+ 'email' => 'johnj@example.com' },
20
+ :redis => @redis)
21
+
16
22
  Flapjack::Data::Entity.add({'id' => '5000',
17
- 'name' => name},
23
+ 'name' => name,
24
+ 'contacts' => ['362']},
18
25
  :redis => @redis)
19
26
  end
20
27
 
@@ -106,21 +113,21 @@ describe Flapjack::Data::EntityCheck, :redis => true do
106
113
  timestamp.should_not be_nil
107
114
  timestamp.should == t.to_s
108
115
 
109
- umps = @redis.zrange("#{name}:#{check}:unscheduled_maintenances", 0, -1, :with_scores => true)
116
+ umps = ec.maintenances(nil, nil, :scheduled => false)
110
117
  umps.should_not be_nil
111
118
  umps.should be_an(Array)
112
119
  umps.should have(1).unscheduled_maintenance_period
113
- umps[0].should be_an(Array)
120
+ umps[0].should be_a(Hash)
114
121
 
115
- start_time = umps[0][0]
122
+ start_time = umps[0][:start_time]
116
123
  start_time.should_not be_nil
117
- start_time.should be_a(String)
118
- start_time.should == t.to_s
124
+ start_time.should be_an(Integer)
125
+ start_time.should == t
119
126
 
120
- score = umps[0][1]
121
- score.should_not be_nil
122
- score.should be_a(Float)
123
- score.should == half_an_hour
127
+ duration = umps[0][:duration]
128
+ duration.should_not be_nil
129
+ duration.should be_a(Float)
130
+ duration.should == half_an_hour
124
131
 
125
132
  summary = @redis.get("#{name}:#{check}:#{t}:unscheduled_maintenance:summary")
126
133
  summary.should_not be_nil
@@ -133,32 +140,167 @@ describe Flapjack::Data::EntityCheck, :redis => true do
133
140
  ec.create_scheduled_maintenance(:start_time => t + (60 * 60),
134
141
  :duration => half_an_hour, :summary => "30 minutes")
135
142
 
136
- smps = @redis.zrange("#{name}:#{check}:scheduled_maintenances", 0, -1, :with_scores => true)
143
+ smps = ec.maintenances(nil, nil, :scheduled => true)
144
+ smps.should_not be_nil
145
+ smps.should be_an(Array)
146
+ smps.should have(1).scheduled_maintenance_period
147
+ smps[0].should be_a(Hash)
148
+
149
+ start_time = smps[0][:start_time]
150
+ start_time.should_not be_nil
151
+ start_time.should be_an(Integer)
152
+ start_time.should == (t + (60 * 60))
153
+
154
+ duration = smps[0][:duration]
155
+ duration.should_not be_nil
156
+ duration.should be_a(Float)
157
+ duration.should == half_an_hour
158
+ end
159
+
160
+ # TODO this should probably enforce that it starts in the future
161
+ it "creates a scheduled maintenance period covering the current time" do
162
+ t = Time.now.to_i
163
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
164
+ ec.create_scheduled_maintenance(:start_time => t - (60 * 60),
165
+ :duration => 2 * (60 * 60), :summary => "2 hours")
166
+
167
+ smps = ec.maintenances(nil, nil, :scheduled => true)
168
+ smps.should_not be_nil
169
+ smps.should be_an(Array)
170
+ smps.should have(1).scheduled_maintenance_period
171
+ smps[0].should be_a(Hash)
172
+
173
+ start_time = smps[0][:start_time]
174
+ start_time.should_not be_nil
175
+ start_time.should be_an(Integer)
176
+ start_time.should == (t - (60 * 60))
177
+
178
+ duration = smps[0][:duration]
179
+ duration.should_not be_nil
180
+ duration.should be_a(Float)
181
+ duration.should == 2 * (60 * 60)
182
+ end
183
+
184
+ it "updates a scheduled maintenance period for a future time" do
185
+ t = Time.now.to_i
186
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
187
+ ec.create_scheduled_maintenance(:start_time => t + (60 * 60),
188
+ :duration => 2 * (60 * 60), :summary => "2 hours")
189
+
190
+ ec.update_scheduled_maintenance(t + (60 * 60), :end_time => t + (4 * (60 * 60)))
191
+ smps = ec.maintenances(nil, nil, :scheduled => true)
192
+ smps.should_not be_nil
193
+ smps.should be_an(Array)
194
+ smps.should have(1).scheduled_maintenance_period
195
+ smps[0].should be_a(Hash)
196
+
197
+ start_time = smps[0][:start_time]
198
+ start_time.should_not be_nil
199
+ start_time.should be_an(Integer)
200
+ start_time.should == (t + (60 * 60))
201
+
202
+ duration = smps[0][:duration]
203
+ duration.should_not be_nil
204
+ duration.should be_a(Float)
205
+ duration.should == 3 * (60 * 60)
206
+ end
207
+
208
+ # TODO this should probably enforce that it starts in the future
209
+ it "updates a scheduled maintenance period covering the current time", :time => true do
210
+ t = Time.now.to_i
211
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
212
+ ec.create_scheduled_maintenance(:start_time => t + (60 * 60),
213
+ :duration => 2 * (60 * 60), :summary => "2 hours")
214
+
215
+ Delorean.time_travel_to( Time.at(t + (90 * 60)) )
216
+
217
+ ec.update_scheduled_maintenance(t + (60 * 60), :end_time => t + (4 * (60 * 60)))
218
+ smps = ec.maintenances(nil, nil, :scheduled => true)
219
+ smps.should_not be_nil
220
+ smps.should be_an(Array)
221
+ smps.should have(1).scheduled_maintenance_period
222
+ smps[0].should be_a(Hash)
223
+
224
+ start_time = smps[0][:start_time]
225
+ start_time.should_not be_nil
226
+ start_time.should be_an(Integer)
227
+ start_time.should == (t + (60 * 60))
228
+
229
+ duration = smps[0][:duration]
230
+ duration.should_not be_nil
231
+ duration.should be_a(Float)
232
+ duration.should == 3 * (60 * 60)
233
+ end
234
+
235
+ it "fails to update a scheduled maintenance period when not found" do
236
+ t = Time.now.to_i
237
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
238
+ lambda {
239
+ ec.update_scheduled_maintenance(t + (60 * 60), :end_time => t + (2 * (60 * 60)))
240
+ }.should raise_error(ArgumentError)
241
+
242
+ smps = ec.maintenances(nil, nil, :scheduled => true)
243
+ smps.should_not be_nil
244
+ smps.should be_an(Array)
245
+ smps.should be_empty
246
+ end
247
+
248
+ it "fails to update a scheduled maintenance period with invalid end time" do
249
+ t = Time.now.to_i
250
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
251
+ ec.create_scheduled_maintenance(:start_time => t + (60 * 60),
252
+ :duration => 2 * (60 * 60), :summary => "2 hours")
253
+
254
+ lambda {
255
+ ec.update_scheduled_maintenance(t + (60 * 60), :end_time => t - (4 * (60 * 60)))
256
+ }.should raise_error(ArgumentError)
257
+ smps = ec.maintenances(nil, nil, :scheduled => true)
137
258
  smps.should_not be_nil
138
259
  smps.should be_an(Array)
139
260
  smps.should have(1).scheduled_maintenance_period
140
- smps[0].should be_an(Array)
261
+ smps[0].should be_a(Hash)
141
262
 
142
- start_time = smps[0][0]
263
+ start_time = smps[0][:start_time]
143
264
  start_time.should_not be_nil
144
- start_time.should be_a(String)
145
- start_time.should == (t + (60 * 60)).to_s
265
+ start_time.should be_an(Integer)
266
+ start_time.should == (t + (60 * 60))
146
267
 
147
- score = smps[0][1]
148
- score.should_not be_nil
149
- score.should be_a(Float)
150
- score.should == half_an_hour
268
+ duration = smps[0][:duration]
269
+ duration.should_not be_nil
270
+ duration.should be_a(Float)
271
+ duration.should == 2 * (60 * 60)
151
272
  end
152
273
 
153
- it "creates a scheduled maintenance period covering the current time"
274
+ it "removes a scheduled maintenance period for a future time" do
275
+ t = Time.now.to_i
276
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
277
+ ec.create_scheduled_maintenance(:start_time => t + (60 * 60),
278
+ :duration => 2 * (60 * 60), :summary => "2 hours")
279
+
280
+ ec.delete_scheduled_maintenance(:start_time => t + (60 * 60))
154
281
 
155
- it "updates a scheduled maintenance period for a future time"
282
+ smps = ec.maintenances(nil, nil, :scheduled => true)
283
+ smps.should_not be_nil
284
+ smps.should be_an(Array)
285
+ smps.should be_empty
286
+ end
287
+
288
+ # TODO this should probably enforce that it starts in the future
289
+ it "removes a scheduled maintenance period covering a current time", :time => true do
290
+ t = Time.now.to_i
291
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
292
+ ec.create_scheduled_maintenance(:start_time => t + (60 * 60),
293
+ :duration => 2 * (60 * 60), :summary => "2 hours")
156
294
 
157
- it "updates a scheduled maintenance period covering the current time"
295
+ Delorean.time_travel_to( Time.at(t + (90 * 60)) )
158
296
 
159
- it "removes a scheduled maintenance period for a future time"
297
+ ec.delete_scheduled_maintenance(:start_time => t + (60 * 60))
160
298
 
161
- it "removes a scheduled maintenance period covering a current time"
299
+ smps = ec.maintenances(nil, nil, :scheduled => true)
300
+ smps.should_not be_nil
301
+ smps.should be_an(Array)
302
+ smps.should be_empty
303
+ end
162
304
 
163
305
  it "returns a list of scheduled maintenance periods" do
164
306
  t = Time.now.to_i
@@ -210,8 +352,6 @@ describe Flapjack::Data::EntityCheck, :redis => true do
210
352
  :summary => "second"}
211
353
  end
212
354
 
213
- it "updates scheduled maintenance periods"
214
-
215
355
  end
216
356
 
217
357
  it "creates an acknowledgement" do
@@ -300,20 +440,20 @@ describe Flapjack::Data::EntityCheck, :redis => true do
300
440
  ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
301
441
 
302
442
  t = Time.now.to_i
303
- # ec.update_state('ok', :timestamp => time_before(t, 5), :summary => 'a')
304
- # ec.update_state('critical', :timestamp => time_before(t, 4), :summary => 'b')
305
- # ec.update_state('ok', :timestamp => time_before(t, 3), :summary => 'c')
306
- # ec.update_state('critical', :timestamp => time_before(t, 2), :summary => 'd')
307
- # ec.update_state('ok', :timestamp => time_before(t, 1), :summary => 'e')
308
-
309
- # states = ec.historical_states(time_before(t, 4), t)
310
- # states.should_not be_nil
311
- # states.should be_an(Array)
312
- # states.should have(4).data_hashes
313
- # states[0][:summary].should == 'b'
314
- # states[1][:summary].should == 'c'
315
- # states[2][:summary].should == 'd'
316
- # states[3][:summary].should == 'e'
443
+ ec.update_state('ok', :timestamp => time_before(t, 5), :summary => 'a')
444
+ ec.update_state('critical', :timestamp => time_before(t, 4), :summary => 'b')
445
+ ec.update_state('ok', :timestamp => time_before(t, 3), :summary => 'c')
446
+ ec.update_state('critical', :timestamp => time_before(t, 2), :summary => 'd')
447
+ ec.update_state('ok', :timestamp => time_before(t, 1), :summary => 'e')
448
+
449
+ states = ec.historical_states(time_before(t, 4), t)
450
+ states.should_not be_nil
451
+ states.should be_an(Array)
452
+ states.should have(4).data_hashes
453
+ states[0][:summary].should == 'b'
454
+ states[1][:summary].should == 'c'
455
+ states[2][:summary].should == 'd'
456
+ states[3][:summary].should == 'e'
317
457
  end
318
458
 
319
459
  it "returns a list of historical scheduled maintenances for a time range" do
@@ -360,7 +500,18 @@ describe Flapjack::Data::EntityCheck, :redis => true do
360
500
  ec.should_not be_failed
361
501
  end
362
502
 
363
- it "returns a status summary"
503
+ it "returns a status summary" do
504
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
505
+
506
+ t = Time.now.to_i
507
+ ec.update_state('ok', :timestamp => time_before(t, 5), :summary => 'a')
508
+ ec.update_state('critical', :timestamp => time_before(t, 4), :summary => 'b')
509
+ ec.update_state('ok', :timestamp => time_before(t, 3), :summary => 'c')
510
+ ec.update_state('critical', :timestamp => time_before(t, 2), :summary => 'd')
511
+
512
+ summary = ec.summary
513
+ summary.should == 'd'
514
+ end
364
515
 
365
516
  it "returns timestamps for its last notifications" do
366
517
  t = Time.now.to_i
@@ -374,4 +525,12 @@ describe Flapjack::Data::EntityCheck, :redis => true do
374
525
  ec.last_recovery_notification.should == t
375
526
  end
376
527
 
528
+ it "finds all related contacts" do
529
+ ec = Flapjack::Data::EntityCheck.for_entity_name(name, check, :redis => @redis)
530
+ contacts = ec.contacts
531
+ contacts.should_not be_nil
532
+ contacts.should be_an(Array)
533
+ contacts.should have(1).contact
534
+ contacts.first.name.should == 'John Johnson'
535
+ end
377
536
  end
@@ -3,10 +3,34 @@ require 'flapjack/data/entity'
3
3
 
4
4
  describe Flapjack::Data::Tag, :redis => true do
5
5
 
6
- it "adds references to tags"
6
+ it "adds references to tags" do
7
+ tags = Flapjack::Data::Tag.create('special', ['apple', 'button', 'carbon'], :redis => @redis)
7
8
 
8
- it "deletes references from tags"
9
+ tags.should include('carbon')
10
+ tags.should_not include('chocolate')
9
11
 
10
- it "lists references contained in a tag"
12
+ tags.add('chocolate')
13
+ tags.should include('chocolate')
14
+ end
15
+
16
+ it "deletes references from tags" do
17
+ tags = Flapjack::Data::Tag.create('special', ['apple', 'button', 'carbon'], :redis => @redis)
18
+
19
+ tags.should include('apple')
20
+ tags.should include('button')
21
+
22
+ tags.delete('button')
23
+ tags.should include('apple')
24
+ tags.should_not include('button')
25
+ end
26
+
27
+ it "lists references contained in a tag" do
28
+ t1 = Flapjack::Data::Tag.create('special', ['apple', 'button', 'carbon'], :redis => @redis)
29
+
30
+ t2 = Flapjack::Data::Tag.find('special', :redis => @redis)
31
+ t2.should include('apple')
32
+ t2.should include('carbon')
33
+ t2.should_not include('chocolate')
34
+ end
11
35
 
12
36
  end
@@ -61,7 +61,24 @@ describe 'Flapjack::Gateways::API', :sinatra => true do
61
61
  last_response.status.should == 204
62
62
  end
63
63
 
64
- it "returns a list of scheduled maintenance periods within a time window for an entity"
64
+ it "returns a list of scheduled maintenance periods within a time window for an entity" do
65
+ start = Time.parse('1 Jan 2012')
66
+ finish = Time.parse('6 Jan 2012')
67
+
68
+ result = mock('result')
69
+ result_json = %q{"result"}
70
+ result.should_receive(:to_json).and_return(result_json)
71
+ entity_presenter.should_receive(:scheduled_maintenance).with(start.to_i, finish.to_i).and_return(result)
72
+ Flapjack::Gateways::API::EntityPresenter.should_receive(:new).
73
+ with(entity, :redis => redis).and_return(entity_presenter)
74
+ Flapjack::Data::Entity.should_receive(:find_by_name).
75
+ with(entity_name, :redis => redis).and_return(entity)
76
+
77
+ get "/scheduled_maintenances/#{entity_name_esc}?" +
78
+ "start_time=#{CGI.escape(start.iso8601)}&end_time=#{CGI.escape(finish.iso8601)}"
79
+ last_response.should be_ok
80
+ last_response.body.should == result_json
81
+ end
65
82
 
66
83
  it "returns a list of scheduled maintenance periods for a check on an entity" do
67
84
  result = mock('result')
@@ -112,8 +129,26 @@ describe 'Flapjack::Gateways::API', :sinatra => true do
112
129
  last_response.body.should == result_json
113
130
  end
114
131
 
132
+ it "returns a list of unscheduled maintenance periods within a time window for a check an entity" do
133
+ start = Time.parse('1 Jan 2012')
134
+ finish = Time.parse('6 Jan 2012')
115
135
 
116
- it "returns a list of unscheduled maintenance periods within a time window for a check an entity"
136
+ result = mock('result')
137
+ result_json = %q{"result"}
138
+ result.should_receive(:to_json).and_return(result_json)
139
+ entity_check_presenter.should_receive(:unscheduled_maintenance).with(start.to_i, finish.to_i).and_return(result)
140
+ Flapjack::Gateways::API::EntityCheckPresenter.should_receive(:new).
141
+ with(entity_check).and_return(entity_check_presenter)
142
+ Flapjack::Data::Entity.should_receive(:find_by_name).
143
+ with(entity_name, :redis => redis).and_return(entity)
144
+ Flapjack::Data::EntityCheck.should_receive(:for_entity).
145
+ with(entity, check, :redis => redis).and_return(entity_check)
146
+
147
+ get "/unscheduled_maintenances/#{entity_name_esc}/#{check}" +
148
+ "?start_time=#{CGI.escape(start.iso8601)}&end_time=#{CGI.escape(finish.iso8601)}"
149
+ last_response.should be_ok
150
+ last_response.body.should == result_json
151
+ end
117
152
 
118
153
  it "returns a list of outages for an entity" do
119
154
  result = mock('result')
@@ -93,14 +93,17 @@ describe Flapjack::Gateways::Pagerduty, :redis => true do
93
93
  Flapjack::RedisPool.should_receive(:new)
94
94
  fp.bootstrap(:config => config)
95
95
 
96
- entity_check = mock('entity_check')
97
- entity_check.should_receive(:check).and_return('PING')
98
- entity_check.should_receive(:pagerduty_credentials).and_return([{
96
+ contact = mock('contact')
97
+ contact.should_receive(:pagerduty_credentials).and_return({
99
98
  'service_key' => '12345678',
100
99
  'subdomain"' => 'flpjck',
101
100
  'username' => 'flapjack',
102
101
  'password' => 'password123'
103
- }])
102
+ })
103
+
104
+ entity_check = mock('entity_check')
105
+ entity_check.should_receive(:check).and_return('PING')
106
+ entity_check.should_receive(:contacts).and_return([contact])
104
107
  entity_check.should_receive(:entity_name).exactly(2).times.and_return('foo-app-01.bar.net:PING')
105
108
  entity_check.should_receive(:create_acknowledgement).with('summary' => 'Acknowledged on PagerDuty')
106
109
 
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'web/views/check.haml', :haml_view => true do
4
+
5
+ it "should escape unsafe check characters in URIs" do
6
+ @entity = 'abc-xyz-01'
7
+ @check = 'Disk / Utilisation'
8
+ @last_notifications = {}
9
+
10
+ page = render_haml('check.haml', self)
11
+ page.should match(%r{/abc-xyz-01/Disk%20%2F%20Utilisation})
12
+ end
13
+
14
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'web/views/contact.haml', :haml_view => true do
4
+
5
+ it "should escape unsafe check characters in URI parameters" do
6
+ @contact = mock('contact')
7
+ @contact.should_receive(:media)
8
+ @contact.should_receive(:name).twice.and_return('Aeschylus')
9
+
10
+ entity = mock('entity')
11
+ entity.should_receive(:name).twice.and_return('abc-xyz-01')
12
+
13
+ checks = ['Disk / Utilisation']
14
+
15
+ @entities_and_checks = [{:entity => entity, :checks => checks}]
16
+
17
+ page = render_haml('contact.haml', self)
18
+ page.should match(%r{\?entity=abc-xyz-01&amp;check=Disk\+%2F\+Utilisation})
19
+ end
20
+
21
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'web/views/index.haml', :haml_view => true do
4
+
5
+ it "should escape unsafe check characters in URI parameters" do
6
+ @states = [['abc-xyz-01', 'Disk / Utilisation', '-', '-', '-', false, false, nil, nil]]
7
+
8
+ page = render_haml('index.haml', self)
9
+ page.should match(%r{\?entity=abc-xyz-01&amp;check=Disk\+%2F\+Utilisation})
10
+ end
11
+
12
+ end
@@ -216,8 +216,17 @@ describe Flapjack::Gateways::Web, :sinatra => true, :redis => true do
216
216
  last_response.should be_ok
217
217
  end
218
218
 
219
- it "shows details of an individual contact found by email"
219
+ it "shows details of an individual contact found by id" do
220
+ contact = mock('contact')
221
+ contact.should_receive(:name).twice.and_return("Smithson Smith")
222
+ contact.should_receive(:media).exactly(3).times.and_return({})
223
+ contact.should_receive(:entities_and_checks).and_return([])
220
224
 
221
- it "shows details of an individual contact found by id"
225
+ Flapjack::Data::Contact.should_receive(:find_by_id).
226
+ with('0362', :redis => @redis).and_return(contact)
227
+
228
+ get "/contacts/0362"
229
+ last_response.should be_ok
230
+ end
222
231
 
223
232
  end
data/spec/spec_helper.rb CHANGED
@@ -45,5 +45,11 @@ RSpec.configure do |config|
45
45
  @redis.quit
46
46
  end
47
47
 
48
+ config.after(:each, :time => true) do
49
+ Delorean.back_to_the_present
50
+ end
51
+
52
+ config.include HamlViewHelper, :haml_view => true
53
+
48
54
  config.include Rack::Test::Methods, :sinatra => true
49
55
  end
@@ -0,0 +1,15 @@
1
+ require 'haml'
2
+ require 'flapjack/utility'
3
+
4
+ module HamlViewHelper
5
+
6
+ TEMPLATE_PATH = File.dirname(__FILE__) +
7
+ '/../../lib/flapjack/gateways/web/views/'
8
+
9
+ include Flapjack::Utility
10
+
11
+ def render_haml(file, scope)
12
+ Haml::Engine.new(File.read(TEMPLATE_PATH + file)).render(scope)
13
+ end
14
+
15
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flapjack
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.51
4
+ version: 0.6.52
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-11-29 00:00:00.000000000 Z
14
+ date: 2012-12-07 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: daemons
@@ -432,11 +432,15 @@ files:
432
432
  - spec/lib/flapjack/gateways/oobetet_spec.rb
433
433
  - spec/lib/flapjack/gateways/pagerduty_spec.rb
434
434
  - spec/lib/flapjack/gateways/sms_messagenet.spec.rb
435
+ - spec/lib/flapjack/gateways/web/views/check.haml_spec.rb
436
+ - spec/lib/flapjack/gateways/web/views/contact.haml_spec.rb
437
+ - spec/lib/flapjack/gateways/web/views/index.haml_spec.rb
435
438
  - spec/lib/flapjack/gateways/web_spec.rb
436
439
  - spec/lib/flapjack/pikelet_spec.rb
437
440
  - spec/lib/flapjack/redis_pool_spec.rb
438
441
  - spec/lib/flapjack/utility_spec.rb
439
442
  - spec/spec_helper.rb
443
+ - spec/support/haml_view_helper.rb
440
444
  - spec/support/profile_all_formatter.rb
441
445
  - spec/support/uncolored_doc_formatter.rb
442
446
  - tasks/events.rake
@@ -512,10 +516,14 @@ test_files:
512
516
  - spec/lib/flapjack/gateways/oobetet_spec.rb
513
517
  - spec/lib/flapjack/gateways/pagerduty_spec.rb
514
518
  - spec/lib/flapjack/gateways/sms_messagenet.spec.rb
519
+ - spec/lib/flapjack/gateways/web/views/check.haml_spec.rb
520
+ - spec/lib/flapjack/gateways/web/views/contact.haml_spec.rb
521
+ - spec/lib/flapjack/gateways/web/views/index.haml_spec.rb
515
522
  - spec/lib/flapjack/gateways/web_spec.rb
516
523
  - spec/lib/flapjack/pikelet_spec.rb
517
524
  - spec/lib/flapjack/redis_pool_spec.rb
518
525
  - spec/lib/flapjack/utility_spec.rb
519
526
  - spec/spec_helper.rb
527
+ - spec/support/haml_view_helper.rb
520
528
  - spec/support/profile_all_formatter.rb
521
529
  - spec/support/uncolored_doc_formatter.rb