flapjack 1.0.0rc2 → 1.0.0rc3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: dcd6cb806d57b78108cc67f2064b1b435a73f79e
4
- data.tar.gz: 753950c0c1decff7c6d5fc954eede8a0e686d0ec
3
+ metadata.gz: d91c5e561fafc64a7698f898d1c02b6f00386c97
4
+ data.tar.gz: c7c4e4ba3e84c4a59ff09964fc67278c12b2fb04
5
5
  SHA512:
6
- metadata.gz: 08641c26fb0307d9d9df4d97cc28876d962e883ea5094e719a2837eaad608f8f03d5c936a495cb42aba8d67c651c89dfc1a70da2a22e4aaf5caa70d5414c61b3
7
- data.tar.gz: 7011a9ad02bf9bebed297f288496f7e1dc5af7a91e9fffa1d85e78a040514fc069e9cbe021917533f32b3f224b3f1b7c63f5f5d981749c0420afcbd04f52a1dc
6
+ metadata.gz: 6efd501448115a5a97a23df6ece95b2f53bf2b4a01d4fe268c1ae851bdb850aaa5d242497f2c5e4871f5b945076151081ae7df33b6eb15fe6e01f06d99a8cda0
7
+ data.tar.gz: d11a32ab75ad2cbcf7b9f9d432cd6f5dfd2da498b127a2c653291f55cf08b7f7eafbc9da9894510b94bbde7d9edcc5ac9ede703761c612563d5e32b2851dabad
@@ -14,11 +14,15 @@ before_install:
14
14
  - gem install bundler
15
15
  script: bundle exec rspec spec && bundle exec cucumber features
16
16
  notifications:
17
- irc: 'irc.freenode.net#flapjack'
17
+ irc:
18
+ channels:
19
+ - 'irc.freenode.net#flapjack'
20
+ template:
21
+ - '%{message} %{repository}#%{build_number} (%{branch} - %{commit} : %{author})'
18
22
  hipchat:
19
23
  template:
20
- - '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}
21
- (<a href="%{build_url}">Details</a>/<a href="%{compare_url}">Change view</a>)'
24
+ - '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}
25
+ (<a href="%{build_url}">Details</a>/<a href="%{compare_url}">Change view</a>)'
22
26
  format: html
23
27
  rooms:
24
28
  secure: ajMolTKDuprYJ9Fadcjb3evh80MyJSgjW4hl4OWnEHyrjQLnsO0hbAvSrKRFUzorMoi58L8XZXd01gMgRqRxMvwqfoHLv4njw8px4X9+F/hySPZj3aMAFM1HuoTmHqeP+Rl+1Ssg+Kss6N4JkgNS81s+tkRnnoHG1n/EhfH6PkE=
@@ -1,5 +1,10 @@
1
1
  ## Flapjack Changelog
2
2
 
3
+ # 1.0.0rc3 - 2014-07-24
4
+ - Bug: fix jsonapi methods for report checks/methods, maintenance reports, outage reports (@ali-graham)
5
+ - Bug: improve tests around notification rules and rollup, tweak rollup behaviour on recovery (@ali-graham)
6
+ - Bug: fix handling of base_url config warnings in web gateway #560 (@jessereynolds)
7
+
3
8
  # 1.0.0rc2 - 2014-07-17
4
9
  - Feature: jsonapi GET /checks badab0d (@ali-graham)
5
10
  - Feature: Add HTTP broker, one-off event submitter, general Go support #553 (@auxesis)
@@ -96,7 +96,6 @@ Feature: Notification rules on a per contact basis
96
96
  When a critical event is received
97
97
  Then 3 email alerts should be queued for malak@example.com
98
98
  When the time is February 1 2013 18:01
99
- Then all alert dropping keys for user c1 should have expired
100
99
  When a critical event is received
101
100
  Then 3 email alerts should be queued for malak@example.com
102
101
 
@@ -210,7 +209,7 @@ Feature: Notification rules on a per contact basis
210
209
  When 10 minutes passes
211
210
  And a critical event is received
212
211
  Then 1 email alert should be queued for malak@example.com
213
- When 5 minutes passes
212
+ When 6 minutes passes
214
213
  And a critical event is received
215
214
  Then 2 email alerts should be queued for malak@example.com
216
215
 
@@ -226,7 +225,7 @@ Feature: Notification rules on a per contact basis
226
225
  When 10 minutes passes
227
226
  And an unknown event is received
228
227
  Then 1 email alert should be queued for malak@example.com
229
- When 5 minutes passes
228
+ When 6 minutes passes
230
229
  And an unknown event is received
231
230
  Then 2 email alerts should be queued for malak@example.com
232
231
 
@@ -214,3 +214,21 @@ Feature: Rollup on a per contact, per media basis
214
214
  Then 2 sms alerts of type problem and rollup none should be queued for +61400000001
215
215
  And 3 sms alerts should be queued for +61400000001
216
216
 
217
+ @time
218
+ Scenario: Multiple notifications should not occur when in rollup
219
+ Given check 'ping' for entity 'foo' is in an ok state
220
+ And check 'ping' for entity 'baz' is in an ok state
221
+ When a critical event is received for check 'ping' on entity 'foo'
222
+ And a critical event is received for check 'ping' on entity 'baz'
223
+ And 1 minute passes
224
+ And a critical event is received for check 'ping' on entity 'foo'
225
+ And a critical event is received for check 'ping' on entity 'baz'
226
+ And 1 minute passes
227
+ And a critical event is received for check 'ping' on entity 'foo'
228
+ Then 1 email alert of type problem and rollup problem should be queued for malak@example.com
229
+ And 19 minutes passes
230
+ And a critical event is received for check 'ping' on entity 'baz'
231
+ Then 2 email alerts of type problem and rollup problem should be queued for malak@example.com
232
+ And 1 minute passes
233
+ And a critical event is received for check 'ping' on entity 'baz'
234
+ Then 2 email alerts of type problem and rollup problem should be queued for malak@example.com
@@ -395,7 +395,7 @@ Given /^user (\S+) has the following notification rules:$/ do |contact_id, rules
395
395
  end
396
396
 
397
397
  Then /^all alert dropping keys for user (\S+) should have expired$/ do |contact_id|
398
- expect(@redis.keys("drop_alerts_for_contact:#{contact_id}*")).to be_empty
398
+ expect(@redis.keys("drop_alerts_for_contact:#{contact_id}:*")).to be_empty
399
399
  end
400
400
 
401
401
  Then /^(\w+) (\w+) alert(?:s)?(?: of)?(?: type (\w+))?(?: and)?(?: rollup (\w+))? should be queued for (.*)$/ do |num_queued, media, notification_type, rollup, address|
@@ -408,13 +408,13 @@ Then /^(\w+) (\w+) alert(?:s)?(?: of)?(?: type (\w+))?(?: and)?(?: rollup (\w+))
408
408
  queue = Resque.peek("#{media}_notifications", 0, 30)
409
409
  queued_length = queue.find_all {|n|
410
410
  type_ok = notification_type ? ( n['args'].first['notification_type'] == notification_type ) : true
411
- rollup_ok = true
412
- if rollup
413
- if rollup == 'none'
414
- rollup_ok = n['args'].first['rollup'].nil?
415
- else
416
- rollup_ok = n['args'].first['rollup'] == rollup
417
- end
411
+ rollup_ok = case rollup
412
+ when 'none'
413
+ n['args'].first['rollup'].nil?
414
+ when nil, n['args'].first['rollup']
415
+ true
416
+ else
417
+ false
418
418
  end
419
419
  type_ok && rollup_ok && ( n['args'].first['address'] == address )
420
420
  }.length
@@ -68,27 +68,31 @@ end
68
68
 
69
69
  class RedisDelorean
70
70
 
71
- ExpireAsIfAtScript = <<EXPIRE_AS_IF_AT
71
+ ShiftTTLsScript = <<SHIFT_TTLS
72
72
  local keys = redis.call('keys', KEYS[1])
73
- local current_time = tonumber(ARGV[1])
74
- local expire_as_if_at = tonumber(ARGV[2])
75
- local counter = 0
73
+ local ttl_decrease = tonumber(ARGV[1])
74
+ local deleted = 0
76
75
 
77
76
  for i,k in ipairs(keys) do
78
77
  local key_ttl = redis.call('ttl', k)
79
78
 
80
79
  -- key does not have timeout if < 0
81
- if ( (key_ttl >= 0) and ((current_time + key_ttl) <= expire_as_if_at) ) then
82
- redis.call('del', k)
83
- counter = counter + 1
80
+ if key_ttl >= 0 then
81
+ local diff = key_ttl - ttl_decrease
82
+ if diff <= 0 then
83
+ redis.call('del', k)
84
+ deleted = deleted + 1
85
+ else
86
+ redis.call('expire', k, diff)
87
+ end
84
88
  end
85
89
  end
86
- return counter
87
- EXPIRE_AS_IF_AT
90
+ return deleted
91
+ SHIFT_TTLS
88
92
 
89
93
  def self.before_all(options = {})
90
94
  redis = options[:redis]
91
- @expire_as_if_at_sha = redis.script(:load, ExpireAsIfAtScript)
95
+ @shift_ttls_sha = redis.script(:load, ShiftTTLsScript)
92
96
  end
93
97
 
94
98
  def self.before_each(options = {})
@@ -96,28 +100,20 @@ EXPIRE_AS_IF_AT
96
100
  end
97
101
 
98
102
  def self.time_travel_to(dest_time)
99
- #puts "travelling to #{Time.now.in_time_zone}, real time is #{Time.now_without_delorean.in_time_zone}"
100
103
  old_maybe_fake_time = Time.now.in_time_zone
101
-
102
104
  Delorean.time_travel_to(dest_time)
103
- #puts "travelled to #{Time.now.in_time_zone}, real time is #{Time.now_without_delorean.in_time_zone}"
104
- return if dest_time < old_maybe_fake_time
105
-
106
- # dumps the first offset -- we're not interested in time difference from
107
- # real, only with context to the fake frame of reference...
108
- # This may mean all scenarios using this code should have an initial
109
- # Given it is *datetime*
110
- # step
111
- offsets = Delorean.send(:time_travel_offsets).dup
112
- offsets.shift
113
- delta = -offsets.inject(0){ |sum, val| sum + val }.floor
114
-
115
- real_time = Time.now_without_delorean.to_i
116
- #puts "delta #{delta}, expire before real time #{Time.at(real_time + delta)}"
117
-
118
- result = @redis.evalsha(@expire_as_if_at_sha, ['*'],
119
- [real_time, real_time + delta])
120
- #puts "Expired #{result} key#{(result == 1) ? '' : 's'}"
105
+ # puts "real time is #{Time.now_without_delorean.in_time_zone}"
106
+ # puts "old assumed time is #{old_maybe_fake_time}"
107
+ # puts "new assumed time is #{Time.now.in_time_zone}"
108
+
109
+ # keys_prior = @redis.keys('*')
110
+
111
+ del = @redis.evalsha(@shift_ttls_sha, ['*'],
112
+ [(dest_time - old_maybe_fake_time).to_i])
113
+
114
+ # keys_after = @redis.keys('*')
115
+
116
+ # puts "Expired #{del} key#{(del == 1) ? '' : 's'}, #{(keys_prior - keys_after).inspect}"
121
117
  end
122
118
 
123
119
  end
@@ -224,6 +224,7 @@ module Flapjack
224
224
  unless ['pagerduty'].include?(media)
225
225
  alerting_checks = contact.count_alerting_checks_for_media(media)
226
226
  rollup_threshold = contact.rollup_threshold_for_media(media)
227
+
227
228
  case
228
229
  when rollup_threshold.nil?
229
230
  # back away slowly
@@ -231,8 +232,9 @@ module Flapjack
231
232
  next ret if contact.drop_rollup_notifications_for_media?(media)
232
233
  contact.update_sent_rollup_alert_keys_for_media(media, :delete => ok?)
233
234
  rollup_type = 'problem'
234
- when (alerting_checks + cleaned >= rollup_threshold)
235
+ when (alerting_checks + cleaned) >= rollup_threshold
235
236
  # alerting checks was just cleaned such that it is now below the rollup threshold
237
+ contact.update_sent_rollup_alert_keys_for_media(media, :delete => true)
236
238
  rollup_type = 'recovery'
237
239
  end
238
240
  logger.debug "rollup decisions: #{@event_id} #{@state} #{media} #{address} rollup_type: #{rollup_type}"
@@ -81,7 +81,7 @@ module Flapjack
81
81
  }
82
82
  end
83
83
 
84
- result
84
+ {:outages => result}
85
85
  end
86
86
 
87
87
  def unscheduled_maintenance(start_time, end_time)
@@ -96,7 +96,7 @@ module Flapjack
96
96
  pu[:end_time] >= start_time
97
97
  }
98
98
 
99
- start_in_unsched + unsched_maintenance
99
+ {:unscheduled_maintenances => (start_in_unsched + unsched_maintenance)}
100
100
  end
101
101
 
102
102
  def scheduled_maintenance(start_time, end_time)
@@ -111,7 +111,7 @@ module Flapjack
111
111
  ps[:end_time] >= start_time
112
112
  }
113
113
 
114
- start_in_sched + sched_maintenance
114
+ {:scheduled_maintenances => (start_in_sched + sched_maintenance)}
115
115
  end
116
116
 
117
117
  # TODO test whether the below overlapping logic is prone to off-by-one
@@ -120,9 +120,7 @@ module Flapjack
120
120
  #
121
121
  # TODO test performance with larger data sets
122
122
  def downtime(start_time, end_time)
123
- sched_maintenances = scheduled_maintenance(start_time, end_time)
124
-
125
- outs = outage(start_time, end_time)
123
+ outs = outage(start_time, end_time)[:outages]
126
124
 
127
125
  total_secs = {}
128
126
  percentages = {}
@@ -139,6 +137,8 @@ module Flapjack
139
137
  # We then create two new outage periods to cover the time around
140
138
  # the scheduled maintenance period, and remove the original.
141
139
 
140
+ sched_maintenances = scheduled_maintenance(start_time, end_time)[:scheduled_maintenances]
141
+
142
142
  sched_maintenances.each do |sm|
143
143
 
144
144
  split_outs = []
@@ -27,7 +27,9 @@ module Flapjack
27
27
  end
28
28
 
29
29
  checks = if event_ids.nil?
30
- Flapjack::Data::EntityCheck.find_all(:redis => redis)
30
+ Flapjack::Data::EntityCheck.find_all(:redis => redis).collect {|check_name|
31
+ find_entity_check_by_name(*check_name.split(':', 2))
32
+ }
31
33
  elsif !event_ids.empty?
32
34
  event_ids.collect {|event_id| find_entity_check_by_name(*event_id.split(':', 2)) }
33
35
  else
@@ -68,12 +68,14 @@ module Flapjack
68
68
  end
69
69
 
70
70
  @base_url = @config['base_url']
71
- if @base_url
72
- @base_url = $1 if @base_url.match(/^(.+\/)$/)
73
- else
74
- dummy_url = "/"
75
- @logger.error "base_url must contain trailing '/', setting it to safe default (#{dummy_url})"
76
- @base_url = dummy_url
71
+ unless @base_url
72
+ @logger.info "base_url is not configured, setting to '/'"
73
+ @base_url = '/'
74
+ end
75
+
76
+ unless @base_url.match(/^.*\/$/)
77
+ @logger.warn "base_url must end with a trailing '/', setting to '#{@base_url}/'"
78
+ @base_url = "#{@base_url}/"
77
79
  end
78
80
 
79
81
  # constants won't be exposed to eRb scope
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  module Flapjack
4
- VERSION = "1.0.0rc2"
4
+ VERSION = "1.0.0rc3"
5
5
  end
6
6
 
@@ -47,8 +47,10 @@ describe 'Flapjack::Gateways::JSONAPI::CheckPresenter' do
47
47
  ecp = Flapjack::Gateways::JSONAPI::CheckPresenter.new(entity_check)
48
48
  outages = ecp.outage(time - (5 * 60 * 60), time - (2 * 60 * 60))
49
49
  expect(outages).not_to be_nil
50
- expect(outages).to be_an(Array)
51
- expect(outages.size).to eq(4)
50
+ expect(outages).to be_a(Hash)
51
+ expect(outages).to have_key(:outages)
52
+ expect(outages[:outages]).to be_an(Array)
53
+ expect(outages[:outages].size).to eq(4)
52
54
 
53
55
  # TODO check the data in those hashes
54
56
  end
@@ -63,8 +65,10 @@ describe 'Flapjack::Gateways::JSONAPI::CheckPresenter' do
63
65
  ecp = Flapjack::Gateways::JSONAPI::CheckPresenter.new(entity_check)
64
66
  outages = ecp.outage(nil, nil)
65
67
  expect(outages).not_to be_nil
66
- expect(outages).to be_an(Array)
67
- expect(outages.size).to eq(4)
68
+ expect(outages).to be_a(Hash)
69
+ expect(outages).to have_key(:outages)
70
+ expect(outages[:outages]).to be_an(Array)
71
+ expect(outages[:outages].size).to eq(4)
68
72
 
69
73
  # TODO check the data in those hashes
70
74
  end
@@ -82,8 +86,10 @@ describe 'Flapjack::Gateways::JSONAPI::CheckPresenter' do
82
86
  ecp = Flapjack::Gateways::JSONAPI::CheckPresenter.new(entity_check)
83
87
  outages = ecp.outage(nil, nil)
84
88
  expect(outages).not_to be_nil
85
- expect(outages).to be_an(Array)
86
- expect(outages.size).to eq(3)
89
+ expect(outages).to be_a(Hash)
90
+ expect(outages).to have_key(:outages)
91
+ expect(outages[:outages]).to be_an(Array)
92
+ expect(outages[:outages].size).to eq(3)
87
93
  end
88
94
 
89
95
  it "returns a (small) outage hash for a single state change" do
@@ -95,8 +101,10 @@ describe 'Flapjack::Gateways::JSONAPI::CheckPresenter' do
95
101
  ecp = Flapjack::Gateways::JSONAPI::CheckPresenter.new(entity_check)
96
102
  outages = ecp.outage(nil, nil)
97
103
  expect(outages).not_to be_nil
98
- expect(outages).to be_an(Array)
99
- expect(outages.size).to eq(1)
104
+ expect(outages).to be_a(Hash)
105
+ expect(outages).to have_key(:outages)
106
+ expect(outages[:outages]).to be_an(Array)
107
+ expect(outages[:outages].size).to eq(1)
100
108
  end
101
109
 
102
110
  it "a list of unscheduled maintenances for an entity check" do
@@ -109,8 +117,10 @@ describe 'Flapjack::Gateways::JSONAPI::CheckPresenter' do
109
117
  ecp = Flapjack::Gateways::JSONAPI::CheckPresenter.new(entity_check)
110
118
  unsched_maint = ecp.unscheduled_maintenance(time - (12 * 60 * 60), time)
111
119
 
112
- expect(unsched_maint).to be_an(Array)
113
- expect(unsched_maint.size).to eq(4)
120
+ expect(unsched_maint).to be_a(Hash)
121
+ expect(unsched_maint).to have_key(:unscheduled_maintenances)
122
+ expect(unsched_maint[:unscheduled_maintenances]).to be_an(Array)
123
+ expect(unsched_maint[:unscheduled_maintenances].size).to eq(4)
114
124
 
115
125
  # TODO check the data in those hashes
116
126
  end
@@ -125,8 +135,10 @@ describe 'Flapjack::Gateways::JSONAPI::CheckPresenter' do
125
135
  ecp = Flapjack::Gateways::JSONAPI::CheckPresenter.new(entity_check)
126
136
  sched_maint = ecp.scheduled_maintenance(time - (12 * 60 * 60), time)
127
137
 
128
- expect(sched_maint).to be_an(Array)
129
- expect(sched_maint.size).to eq(4)
138
+ expect(sched_maint).to be_a(Hash)
139
+ expect(sched_maint).to have_key(:scheduled_maintenances)
140
+ expect(sched_maint[:scheduled_maintenances]).to be_an(Array)
141
+ expect(sched_maint[:scheduled_maintenances].size).to eq(4)
130
142
 
131
143
  # TODO check the data in those hashes
132
144
  end
@@ -1,9 +1,12 @@
1
1
  if ENV['COVERAGE']
2
2
  require 'simplecov'
3
-
4
3
  SimpleCov.start do
5
4
  add_filter '/spec/'
6
5
  end
6
+ SimpleCov.at_exit do
7
+ Oj.default_options = { :mode => :compat }
8
+ SimpleCov.result.format!
9
+ end
7
10
  end
8
11
 
9
12
  $testing = true
@@ -16,16 +19,17 @@ ENV['RACK_ENV'] = ENV["FLAPJACK_ENV"]
16
19
  require 'bundler'
17
20
  Bundler.require(:default, :test)
18
21
 
22
+ require 'oj'
23
+ Oj.default_options = { :indent => 0, :mode => :strict }
24
+ Oj.mimic_JSON
25
+ require 'active_support/json'
26
+
19
27
  require 'webmock/rspec'
20
28
  WebMock.disable_net_connect!
21
29
 
22
30
  $:.unshift(File.dirname(__FILE__) + '/../lib')
23
31
  require 'flapjack/configuration'
24
32
 
25
- require 'oj'
26
- Oj.mimic_JSON
27
- Oj.default_options = { :indent => 0, :mode => :strict }
28
- require 'active_support/json'
29
33
 
30
34
  # Requires supporting files with custom matchers and macros, etc,
31
35
  # in ./support/ and its subdirectories.
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: 1.0.0rc2
4
+ version: 1.0.0rc3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lindsay Holmwood
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-07-17 00:00:00.000000000 Z
13
+ date: 2014-07-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: dante