flapjack 1.0.0rc2 → 1.0.0rc3

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.
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