flapjack 0.8.1 → 0.8.2
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 +4 -4
- data/.travis.yml +1 -0
- data/CHANGELOG.md +17 -0
- data/bin/flapjack +1 -1
- data/features/cli.feature +2 -1
- data/features/notification_rules.feature +22 -7
- data/features/steps/events_steps.rb +6 -12
- data/lib/flapjack/coordinator.rb +55 -60
- data/lib/flapjack/data/event.rb +103 -12
- data/lib/flapjack/data/notification.rb +7 -1
- data/lib/flapjack/data/notification_rule.rb +55 -53
- data/lib/flapjack/gateways/web/views/checks.html.erb +9 -9
- data/lib/flapjack/gateways/web/views/contact.html.erb +1 -1
- data/lib/flapjack/gateways/web/views/entity.html.erb +4 -4
- data/lib/flapjack/gateways/web/views/layout.erb +1 -1
- data/lib/flapjack/gateways/web/views/self_stats.html.erb +1 -1
- data/lib/flapjack/processor.rb +2 -6
- data/lib/flapjack/version.rb +1 -1
- data/spec/lib/flapjack/coordinator_spec.rb +29 -45
- data/spec/lib/flapjack/data/event_spec.rb +136 -9
- data/tasks/benchmarks.rake +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9f395ed95f7e409a6dccf833a149abce9ba2c3c
|
4
|
+
data.tar.gz: 02c26390a79ff90130c1c9c3a6fe1618c1c0341e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: caba9206c27111faae12acef641840b0fbd44cd7e8227462d0ba30bedf5a21cd71f54070ffc33015478eda857f06a28666a3772127a554f0b262545022bcd84f
|
7
|
+
data.tar.gz: c2af3088fc0154cc99f3520a254fa57e49ec110d1e0ccd0649fed0a21dc5462497eded455565131dc99e63e71d3725adf57ba725ae0442ea9b65197c78daac34
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
## Flapjack Changelog
|
2
2
|
|
3
|
+
# 0.8.2 - 2014-01-16
|
4
|
+
- Bug: no HTML parsing in summary of scheduled maintenance (thanks @marek-knappe) gh-413 (@ali-graham)
|
5
|
+
- Bug: if tags and entities are present in a notification rule, both should match the event (thanks @jswoods) gh-417 (@jessereynolds)
|
6
|
+
- Bug: improperly structured events crash the processor gh-411 (thanks @someword) (@ali-graham)
|
7
|
+
- Bug: Critical events are no longer showing up as red in the flapjack UI gh-419 (thanks @jswoods) (@jessereynolds)
|
8
|
+
|
9
|
+
# 0.8.1 - 2014-01-06
|
10
|
+
- Bug: JSONAPI: 500 on GET /entities with no entities in the db gh-409 (@jessereynolds)
|
11
|
+
|
12
|
+
# 0.8.0 - 2014-01-06
|
13
|
+
- Feature: Start rewriting API as per jsonapi.org gh-253 (@ali-graham, @jessereynolds)
|
14
|
+
- Feature: API consuming webapp beginnings gh-253 (@ali-graham, @auxesis, @jessereynolds)
|
15
|
+
- Feature: New styles for navigation and so on (@auxesis)
|
16
|
+
- Chore: Upgrade Rspec to v3 gh-389 (@ali-graham)
|
17
|
+
- Chore: Make README clearer gh-406 (@auxesis)
|
18
|
+
- Bug: Listing notification rules for a non existent contact gives 500, should be 404 gh-392 (@ali-graham)
|
19
|
+
|
3
20
|
# 0.7.35 - 2013-12-10
|
4
21
|
- Feature: allow flapper to flap with an arbitrary interval gh-383 (@jessereynolds)
|
5
22
|
- Feature: Expose statistics for currency of all checks gh-386 (@jessereynolds)
|
data/bin/flapjack
CHANGED
data/features/cli.feature
CHANGED
@@ -69,6 +69,7 @@ test:
|
|
69
69
|
|
70
70
|
Scenario: Reloading flapjack configuration
|
71
71
|
When I start flapjack with `flapjack start --config tmp/cucumber_cli/flapjack_cfg.yaml`
|
72
|
+
Then flapjack should start within 15 seconds
|
72
73
|
When I run `mv tmp/cucumber_cli/flapjack_cfg.yaml tmp/cucumber_cli/flapjack_cfg.yaml.bak`
|
73
74
|
Given a file named "flapjack_cfg.yaml" with:
|
74
75
|
"""
|
@@ -80,7 +81,7 @@ test:
|
|
80
81
|
"""
|
81
82
|
When I send a SIGHUP to the flapjack process
|
82
83
|
# TODO how to test for config file change?
|
83
|
-
When I
|
84
|
+
When I stop flapjack with `flapjack stop --config tmp/cucumber_cli/flapjack_cfg_d.yaml`
|
84
85
|
Then flapjack should stop within 15 seconds
|
85
86
|
|
86
87
|
|
@@ -3,12 +3,13 @@ Feature: Notification rules on a per contact basis
|
|
3
3
|
|
4
4
|
Background:
|
5
5
|
Given the following users exist:
|
6
|
-
| id | first_name | last_name | email | sms | timezone
|
7
|
-
| c1 | Malak | Al-Musawi | malak@example.com | +61400000001 | Asia/Baghdad
|
8
|
-
| c2 | Imani | Farooq | imani@example.com | +61400000002 | Europe/Moscow
|
9
|
-
| c3 | Vera | Дурейко | vera@example.com | +61400000003 | Europe/Paris
|
10
|
-
| c4 | Lucia | Moretti | lucia@example.com | +61400000004 | Europe/Rome
|
11
|
-
| c5 | Wang Fang | Wong | fang@example.com | +61400000005 | Asia/Shanghai
|
6
|
+
| id | first_name | last_name | email | sms | timezone |
|
7
|
+
| c1 | Malak | Al-Musawi | malak@example.com | +61400000001 | Asia/Baghdad |
|
8
|
+
| c2 | Imani | Farooq | imani@example.com | +61400000002 | Europe/Moscow |
|
9
|
+
| c3 | Vera | Дурейко | vera@example.com | +61400000003 | Europe/Paris |
|
10
|
+
| c4 | Lucia | Moretti | lucia@example.com | +61400000004 | Europe/Rome |
|
11
|
+
| c5 | Wang Fang | Wong | fang@example.com | +61400000005 | Asia/Shanghai |
|
12
|
+
| c6 | Jive | Smith | jive@example.com | +61400000006 | America/Los_Angeles |
|
12
13
|
|
13
14
|
And the following entities exist:
|
14
15
|
| id | name | contacts |
|
@@ -16,7 +17,7 @@ Feature: Notification rules on a per contact basis
|
|
16
17
|
| 2 | bar | c1,c2,c3 |
|
17
18
|
| 3 | baz | c1,c3 |
|
18
19
|
| 4 | buf | c1,c2,c3 |
|
19
|
-
| 5 | foo-app-01.xyz | c4
|
20
|
+
| 5 | foo-app-01.xyz | c4,c6 |
|
20
21
|
|
21
22
|
And user c1 has the following notification intervals:
|
22
23
|
| email | sms |
|
@@ -66,6 +67,11 @@ Feature: Notification rules on a per contact basis
|
|
66
67
|
| unknown_media | critical_media |
|
67
68
|
| email | email, sms |
|
68
69
|
|
70
|
+
And user c6 has the following notification rules:
|
71
|
+
| entities | tags | warning_media | critical_media |
|
72
|
+
| | | | |
|
73
|
+
| foo-app-01.xyz | check_disk | email | email |
|
74
|
+
|
69
75
|
@time_restrictions @time
|
70
76
|
Scenario: Alerts only during specified time restrictions
|
71
77
|
Given the timezone is Asia/Baghdad
|
@@ -380,6 +386,15 @@ Feature: Notification rules on a per contact basis
|
|
380
386
|
And a critical event is received
|
381
387
|
Then no sms alerts should be queued for +61400000004
|
382
388
|
|
389
|
+
@time
|
390
|
+
Scenario: Don't notify when tags in a rule don't match, but the entity does match
|
391
|
+
Given the check is check 'Memory Util' on entity 'foo-app-01.xyz'
|
392
|
+
And the check is in an ok state
|
393
|
+
When a critical event is received
|
394
|
+
And 1 minute passes
|
395
|
+
And a critical event is received
|
396
|
+
Then no email alerts should be queued for jive@example.com
|
397
|
+
|
383
398
|
@time
|
384
399
|
Scenario: Only notify during specified time periods in tag matched rules
|
385
400
|
Given the timezone is Europe/Rome
|
@@ -83,8 +83,7 @@ def submit_ok(entity, check)
|
|
83
83
|
'state' => 'ok',
|
84
84
|
'summary' => '0% packet loss',
|
85
85
|
'entity' => entity,
|
86
|
-
'check' => check
|
87
|
-
'client' => 'clientx'
|
86
|
+
'check' => check
|
88
87
|
}
|
89
88
|
submit_event(event)
|
90
89
|
end
|
@@ -95,8 +94,7 @@ def submit_warning(entity, check)
|
|
95
94
|
'state' => 'warning',
|
96
95
|
'summary' => '25% packet loss',
|
97
96
|
'entity' => entity,
|
98
|
-
'check' => check
|
99
|
-
'client' => 'clientx'
|
97
|
+
'check' => check
|
100
98
|
}
|
101
99
|
submit_event(event)
|
102
100
|
end
|
@@ -107,8 +105,7 @@ def submit_critical(entity, check)
|
|
107
105
|
'state' => 'critical',
|
108
106
|
'summary' => '100% packet loss',
|
109
107
|
'entity' => entity,
|
110
|
-
'check' => check
|
111
|
-
'client' => 'clientx'
|
108
|
+
'check' => check
|
112
109
|
}
|
113
110
|
submit_event(event)
|
114
111
|
end
|
@@ -119,8 +116,7 @@ def submit_unknown(entity, check)
|
|
119
116
|
'state' => 'unknown',
|
120
117
|
'summary' => 'check execution error',
|
121
118
|
'entity' => entity,
|
122
|
-
'check' => check
|
123
|
-
'client' => 'clientx'
|
119
|
+
'check' => check
|
124
120
|
}
|
125
121
|
submit_event(event)
|
126
122
|
end
|
@@ -131,8 +127,7 @@ def submit_acknowledgement(entity, check)
|
|
131
127
|
'state' => 'acknowledgement',
|
132
128
|
'summary' => "I'll have this fixed in a jiffy, saw the same thing yesterday",
|
133
129
|
'entity' => entity,
|
134
|
-
'check' => check
|
135
|
-
'client' => 'clientx',
|
130
|
+
'check' => check
|
136
131
|
}
|
137
132
|
submit_event(event)
|
138
133
|
end
|
@@ -143,8 +138,7 @@ def submit_test(entity, check)
|
|
143
138
|
'state' => 'test_notifications',
|
144
139
|
'summary' => "test notification for all contacts interested in #{entity}",
|
145
140
|
'entity' => entity,
|
146
|
-
'check' => check
|
147
|
-
'client' => 'clientx',
|
141
|
+
'check' => check
|
148
142
|
}
|
149
143
|
submit_event(event)
|
150
144
|
end
|
data/lib/flapjack/coordinator.rb
CHANGED
@@ -21,6 +21,8 @@ module Flapjack
|
|
21
21
|
@redis_options = config.for_redis
|
22
22
|
@pikelets = []
|
23
23
|
|
24
|
+
@received_signals = []
|
25
|
+
|
24
26
|
@logger = Flapjack::Logger.new("flapjack-coordinator", @config.all['logger'])
|
25
27
|
end
|
26
28
|
|
@@ -29,22 +31,39 @@ module Flapjack
|
|
29
31
|
|
30
32
|
EM.synchrony do
|
31
33
|
setup_signals if options[:signals]
|
32
|
-
|
34
|
+
|
35
|
+
begin
|
36
|
+
add_pikelets(pikelets(@config.all))
|
37
|
+
loop do
|
38
|
+
while sig = @received_signals.shift do
|
39
|
+
case sig
|
40
|
+
when 'INT', 'TERM', 'QUIT'
|
41
|
+
@exit_value = Signal.list[sig] + 128
|
42
|
+
raise Interrupt
|
43
|
+
when 'HUP'
|
44
|
+
reload
|
45
|
+
end
|
46
|
+
end
|
47
|
+
EM::Synchrony.sleep 0.25
|
48
|
+
end
|
49
|
+
rescue Exception => e
|
50
|
+
unless e.is_a?(Interrupt)
|
51
|
+
trace = e.backtrace.join("\n")
|
52
|
+
@logger.fatal "#{e.class.name}\n#{e.message}\n#{trace}"
|
53
|
+
@exit_value = 1
|
54
|
+
end
|
55
|
+
remove_pikelets(@pikelets)
|
56
|
+
EM.stop
|
57
|
+
end
|
33
58
|
end
|
34
59
|
|
60
|
+
Syslog.close if Syslog.opened?
|
61
|
+
|
35
62
|
@exit_value
|
36
63
|
end
|
37
64
|
|
38
|
-
|
39
|
-
return unless @exit_value.nil?
|
40
|
-
@exit_value = value
|
41
|
-
remove_pikelets(@pikelets, :shutdown => true)
|
42
|
-
# Syslog.close if Syslog.opened? # TODO revisit in threading branch
|
43
|
-
end
|
65
|
+
private
|
44
66
|
|
45
|
-
# NB: global config options (e.g. daemonize, pidfile,
|
46
|
-
# logfile, redis options) won't be checked on reload.
|
47
|
-
# should we do a full restart if some of these change?
|
48
67
|
def reload
|
49
68
|
prev_pikelet_cfg = pikelets(@config.all)
|
50
69
|
|
@@ -85,85 +104,61 @@ module Flapjack
|
|
85
104
|
added << pik.type
|
86
105
|
end
|
87
106
|
|
88
|
-
# puts "removed"
|
89
|
-
# p removed
|
90
|
-
|
91
|
-
# puts "added"
|
92
|
-
# p added
|
93
|
-
|
94
107
|
removed_pikelets = @pikelets.select {|pik| removed.include?(pik.type) }
|
95
108
|
|
96
|
-
# puts "removed pikelets"
|
97
|
-
# p removed_pikelets
|
98
|
-
|
99
109
|
remove_pikelets(removed_pikelets)
|
100
110
|
|
101
111
|
# is there a nicer way to only keep the parts of the hash with matching keys?
|
102
112
|
added_pikelets = enabled_pikelet_cfg.select {|k, v| added.include?(k) }
|
103
113
|
|
104
|
-
# puts "added pikelet configs"
|
105
|
-
# p added_pikelets
|
106
|
-
|
107
114
|
add_pikelets(added_pikelets)
|
108
115
|
end
|
109
116
|
|
110
|
-
private
|
111
|
-
|
112
117
|
# the global nature of this seems at odds with it calling stop
|
113
118
|
# within a single coordinator instance. Coordinator is essentially
|
114
119
|
# a singleton anyway...
|
115
120
|
def setup_signals
|
116
|
-
Kernel.trap('INT')
|
117
|
-
Kernel.trap('TERM')
|
121
|
+
Kernel.trap('INT') { @received_signals << 'INT' unless @received_signals.include?('INT') }
|
122
|
+
Kernel.trap('TERM') { @received_signals << 'TERM' unless @received_signals.include?('TERM') }
|
118
123
|
unless RbConfig::CONFIG['host_os'] =~ /mswin|windows|cygwin/i
|
119
|
-
Kernel.trap('QUIT') {
|
120
|
-
Kernel.trap('HUP') {
|
124
|
+
Kernel.trap('QUIT') { @received_signals << 'QUIT' unless @received_signals.include?('QUIT') }
|
125
|
+
Kernel.trap('HUP') { @received_signals << 'HUP' unless @received_signals.include?('HUP') }
|
121
126
|
end
|
122
127
|
end
|
123
128
|
|
124
129
|
# passed a hash with {PIKELET_TYPE => PIKELET_CFG, ...}
|
125
130
|
def add_pikelets(pikelets_data = {})
|
126
|
-
start_piks = []
|
127
131
|
pikelets_data.each_pair do |type, cfg|
|
128
132
|
next unless pikelet = Flapjack::Pikelet.create(type,
|
129
|
-
:config => cfg, :redis_config => @redis_options,
|
130
|
-
|
133
|
+
:config => cfg, :redis_config => @redis_options,
|
134
|
+
:boot_time => @boot_time)
|
135
|
+
|
131
136
|
@pikelets << pikelet
|
132
|
-
|
133
|
-
begin
|
134
|
-
start_piks.each {|pik| pik.start }
|
135
|
-
rescue Exception => e
|
136
|
-
trace = e.backtrace.join("\n")
|
137
|
-
@logger.fatal "#{e.class.name}\n#{e.message}\n#{trace}"
|
138
|
-
stop
|
137
|
+
pikelet.start
|
139
138
|
end
|
140
139
|
end
|
141
140
|
|
142
141
|
def remove_pikelets(piks, opts = {})
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
# @logger.info "#{pik.type}: #{old_status} -> #{status}"
|
156
|
-
end
|
142
|
+
piks.map(&:stop)
|
143
|
+
|
144
|
+
loop do
|
145
|
+
# only prints state changes, otherwise pikelets not closing promptly can
|
146
|
+
# cause everything else to be spammy
|
147
|
+
piks.each do |pik|
|
148
|
+
old_status = pik.status
|
149
|
+
pik.update_status
|
150
|
+
status = pik.status
|
151
|
+
next if old_status.eql?(status)
|
152
|
+
@logger.info "#{pik.type}: #{old_status} -> #{status}"
|
153
|
+
end
|
157
154
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
break
|
164
|
-
end
|
155
|
+
if piks.any? {|p| p.status == 'stopping' }
|
156
|
+
EM::Synchrony.sleep 0.25
|
157
|
+
else
|
158
|
+
@pikelets -= piks
|
159
|
+
break
|
165
160
|
end
|
166
|
-
|
161
|
+
end
|
167
162
|
end
|
168
163
|
|
169
164
|
def pikelets(config_env)
|
data/lib/flapjack/data/event.rb
CHANGED
@@ -10,6 +10,44 @@ module Flapjack
|
|
10
10
|
|
11
11
|
attr_reader :check, :summary, :details, :acknowledgement_id
|
12
12
|
|
13
|
+
REQUIRED_KEYS = ['type', 'state', 'entity', 'check', 'summary']
|
14
|
+
OPTIONAL_KEYS = ['time', 'details', 'acknowledgement_id', 'duration']
|
15
|
+
|
16
|
+
VALIDATIONS = {
|
17
|
+
proc {|e| ['service', 'action'].include?(e['type']) } =>
|
18
|
+
"type must be either 'service' or 'action'",
|
19
|
+
|
20
|
+
proc {|e| ['ok', 'warning', 'critical', 'unknown', 'acknowledgement', 'test_notifications'].include?(e['state']) } =>
|
21
|
+
"state must be one of 'ok', 'warning', 'critical', 'unknown', 'acknowledgement' or 'test_notifications'",
|
22
|
+
|
23
|
+
proc {|e| e['entity'].is_a?(String) } =>
|
24
|
+
"entity must be a string",
|
25
|
+
|
26
|
+
proc {|e| e['check'].is_a?(String) } =>
|
27
|
+
"check must be a string",
|
28
|
+
|
29
|
+
proc {|e| e['summary'].is_a?(String) } =>
|
30
|
+
"summary must be a string",
|
31
|
+
|
32
|
+
proc {|e| e['time'].nil? ||
|
33
|
+
e['time'].is_a?(Integer) ||
|
34
|
+
(e['time'].is_a?(String) && !!(parsed_duration =~ /^\d+$/)) } =>
|
35
|
+
"time must be a positive integer, or a string castable to one",
|
36
|
+
|
37
|
+
proc {|e| e['details'].nil? || e['details'].is_a?(String) } =>
|
38
|
+
"details must be a string",
|
39
|
+
|
40
|
+
proc {|e| e['acknowledgement_id'].nil? ||
|
41
|
+
e['acknowledgement_id'].is_a?(String) ||
|
42
|
+
e['acknowledgement_id'].is_a?(Integer) } =>
|
43
|
+
"acknowledgement_id must be a string or an integer",
|
44
|
+
|
45
|
+
proc {|e| e['duration'].nil? ||
|
46
|
+
e['duration'].is_a?(Integer) ||
|
47
|
+
(e['duration'].is_a?(String) && !!(parsed_duration =~ /^\d+$/)) } =>
|
48
|
+
"duration must be a positive integer, or a string castable to one",
|
49
|
+
}
|
50
|
+
|
13
51
|
# Helper method for getting the next event.
|
14
52
|
#
|
15
53
|
# Has a blocking and non-blocking method signature.
|
@@ -19,7 +57,6 @@ module Flapjack
|
|
19
57
|
#
|
20
58
|
# Calling next with :block => false, will return a nil if there are no
|
21
59
|
# events on the queue.
|
22
|
-
#
|
23
60
|
def self.next(queue, opts = {})
|
24
61
|
raise "Redis connection not set" unless redis = opts[:redis]
|
25
62
|
|
@@ -28,15 +65,17 @@ module Flapjack
|
|
28
65
|
:events_archive_maxage => (3 * 60 * 60) }
|
29
66
|
options = defaults.merge(opts)
|
30
67
|
|
68
|
+
archive_dest = nil
|
69
|
+
base_time_str = Time.now.utc.strftime("%Y%m%d%H")
|
70
|
+
|
31
71
|
if options[:archive_events]
|
32
|
-
|
72
|
+
archive_dest = "events_archive:#{base_time_str}"
|
33
73
|
if options[:block]
|
34
|
-
raw = redis.brpoplpush(queue,
|
74
|
+
raw = redis.brpoplpush(queue, archive_dest, 0)
|
35
75
|
else
|
36
|
-
raw = redis.rpoplpush(queue,
|
76
|
+
raw = redis.rpoplpush(queue, archive_dest)
|
37
77
|
return unless raw
|
38
78
|
end
|
39
|
-
redis.expire(dest, options[:events_archive_maxage])
|
40
79
|
else
|
41
80
|
if options[:block]
|
42
81
|
raw = redis.brpop(queue, 0)[1]
|
@@ -45,15 +84,67 @@ module Flapjack
|
|
45
84
|
return unless raw
|
46
85
|
end
|
47
86
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
87
|
+
parsed = parse_and_validate(raw, :logger => options[:logger])
|
88
|
+
if parsed.nil?
|
89
|
+
# either bad json or invalid data -- in either case we'll
|
90
|
+
# store the raw data in a rejected list
|
91
|
+
rejected_dest = "events_rejected:#{base_time_str}"
|
92
|
+
if options[:archive_events]
|
93
|
+
redis.multi
|
94
|
+
redis.lrem(archive_dest, 1, raw)
|
95
|
+
redis.lpush(rejected_dest, raw)
|
96
|
+
redis.exec
|
97
|
+
redis.expire(archive_dest, options[:events_archive_maxage])
|
98
|
+
else
|
99
|
+
redis.lpush(rejected_dest, raw)
|
100
|
+
end
|
101
|
+
return
|
102
|
+
elsif options[:archive_events]
|
103
|
+
redis.expire(archive_dest, options[:events_archive_maxage])
|
104
|
+
end
|
105
|
+
self.new(parsed)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.parse_and_validate(raw, opts = {})
|
109
|
+
errors = []
|
110
|
+
if parsed = ::Oj.load(raw)
|
111
|
+
|
112
|
+
if parsed.is_a?(Hash)
|
113
|
+
missing_keys = REQUIRED_KEYS.select {|k|
|
114
|
+
!parsed.has_key?(k) || parsed[k].nil? || parsed[k].empty?
|
115
|
+
}
|
116
|
+
unless missing_keys.empty?
|
117
|
+
errors << "Event hash has missing keys '#{missing_keys.join('\', \'')}'"
|
118
|
+
end
|
119
|
+
|
120
|
+
unknown_keys = parsed.keys - (REQUIRED_KEYS + OPTIONAL_KEYS)
|
121
|
+
unless unknown_keys.empty?
|
122
|
+
errors << "Event hash has unknown key(s) '#{unknown_keys.join('\', \'')}'"
|
123
|
+
end
|
124
|
+
else
|
125
|
+
errors << "Event must be a JSON hash, see https://github.com/flpjck/flapjack/wiki/DATA_STRUCTURES#event-queue"
|
53
126
|
end
|
54
|
-
|
127
|
+
|
128
|
+
if errors.empty?
|
129
|
+
errors += VALIDATIONS.keys.inject([]) {|ret,vk|
|
130
|
+
ret << "Event #{VALIDATIONS[vk]}" unless vk.call(parsed)
|
131
|
+
ret
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
return parsed if errors.empty?
|
136
|
+
end
|
137
|
+
|
138
|
+
if opts[:logger]
|
139
|
+
error_str = errors.nil? ? '' : errors.join(', ')
|
140
|
+
opts[:logger].error("Invalid event data received #{error_str}#{parsed.inspect}")
|
141
|
+
end
|
142
|
+
nil
|
143
|
+
rescue Oj::Error => e
|
144
|
+
if opts[:logger]
|
145
|
+
opts[:logger].error("Error deserialising event json: #{e}, raw json: #{raw.inspect}")
|
55
146
|
end
|
56
|
-
|
147
|
+
nil
|
57
148
|
end
|
58
149
|
|
59
150
|
# creates, or modifies, an event object and adds it to the events list in redis
|
@@ -142,7 +142,13 @@ module Flapjack
|
|
142
142
|
# for time, entity and tags
|
143
143
|
matchers = rules.select do |rule|
|
144
144
|
logger.debug("considering rule with entities: #{rule.entities} and tags: #{rule.tags.to_json}")
|
145
|
-
|
145
|
+
rule_has_tags = rule.tags ? (rule.tags.length > 0) : false
|
146
|
+
rule_has_entities = rule.entities ? (rule.entities.length > 0) : false
|
147
|
+
|
148
|
+
matches_tags = rule_has_tags ? rule.match_tags?(@tags) : true
|
149
|
+
matches_entity = rule_has_entities ? rule.match_entity?(@event_id) : true
|
150
|
+
|
151
|
+
((matches_entity && matches_tags) || ! rule.is_specific?) &&
|
146
152
|
rule_occurring_now?(rule, :contact => contact, :default_timezone => default_timezone)
|
147
153
|
end
|
148
154
|
|
@@ -233,66 +233,68 @@ module Flapjack
|
|
233
233
|
tr
|
234
234
|
end
|
235
235
|
|
236
|
+
VALIDATION_PROCS = {
|
237
|
+
proc {|d| !d.has_key?(:entities) ||
|
238
|
+
( d[:entities].nil? ||
|
239
|
+
d[:entities].is_a?(Array) &&
|
240
|
+
d[:entities].all? {|e| e.is_a?(String)} ) } =>
|
241
|
+
"entities must be a list of strings",
|
242
|
+
|
243
|
+
proc {|d| !d.has_key?(:tags) ||
|
244
|
+
( d[:tags].nil? ||
|
245
|
+
d[:tags].is_a?(Flapjack::Data::TagSet) &&
|
246
|
+
d[:tags].all? {|et| et.is_a?(String)} ) } =>
|
247
|
+
"tags must be a tag_set of strings",
|
248
|
+
|
249
|
+
proc {|d| !d.has_key?(:time_restrictions) ||
|
250
|
+
( d[:time_restrictions].nil? ||
|
251
|
+
d[:time_restrictions].all? {|tr|
|
252
|
+
!!prepare_time_restriction(symbolize(tr))
|
253
|
+
} )
|
254
|
+
} =>
|
255
|
+
"time restrictions are invalid",
|
256
|
+
|
257
|
+
# TODO should the media types be checked against a whitelist?
|
258
|
+
proc {|d| !d.has_key?(:unknown_media) ||
|
259
|
+
( d[:unknown_media].nil? ||
|
260
|
+
d[:unknown_media].is_a?(Array) &&
|
261
|
+
d[:unknown_media].all? {|et| et.is_a?(String)} ) } =>
|
262
|
+
"unknown_media must be a list of strings",
|
263
|
+
|
264
|
+
proc {|d| !d.has_key?(:warning_media) ||
|
265
|
+
( d[:warning_media].nil? ||
|
266
|
+
d[:warning_media].is_a?(Array) &&
|
267
|
+
d[:warning_media].all? {|et| et.is_a?(String)} ) } =>
|
268
|
+
"warning_media must be a list of strings",
|
269
|
+
|
270
|
+
proc {|d| !d.has_key?(:critical_media) ||
|
271
|
+
( d[:critical_media].nil? ||
|
272
|
+
d[:critical_media].is_a?(Array) &&
|
273
|
+
d[:critical_media].all? {|et| et.is_a?(String)} ) } =>
|
274
|
+
"critical_media must be a list of strings",
|
275
|
+
|
276
|
+
proc {|d| !d.has_key?(:unknown_blackhole) ||
|
277
|
+
[TrueClass, FalseClass].include?(d[:unknown_blackhole].class) } =>
|
278
|
+
"unknown_blackhole must be true or false",
|
279
|
+
|
280
|
+
proc {|d| !d.has_key?(:warning_blackhole) ||
|
281
|
+
[TrueClass, FalseClass].include?(d[:warning_blackhole].class) } =>
|
282
|
+
"warning_blackhole must be true or false",
|
283
|
+
|
284
|
+
proc {|d| !d.has_key?(:critical_blackhole) ||
|
285
|
+
[TrueClass, FalseClass].include?(d[:critical_blackhole].class) } =>
|
286
|
+
"critical_blackhole must be true or false",
|
287
|
+
}
|
288
|
+
|
236
289
|
def self.validate_data(d, options = {})
|
237
290
|
id_not_required = !!options[:id_not_required]
|
238
291
|
# hash with validation => error_message
|
239
292
|
validations = {}
|
240
293
|
validations.merge!({ proc { d.has_key?(:id) } => "id not set"}) unless id_not_required
|
241
|
-
validations.merge!(
|
242
|
-
proc { !d.has_key?(:entities) ||
|
243
|
-
( d[:entities].nil? ||
|
244
|
-
d[:entities].is_a?(Array) &&
|
245
|
-
d[:entities].all? {|e| e.is_a?(String)} ) } =>
|
246
|
-
"entities must be a list of strings",
|
247
|
-
|
248
|
-
proc { !d.has_key?(:tags) ||
|
249
|
-
( d[:tags].nil? ||
|
250
|
-
d[:tags].is_a?(Flapjack::Data::TagSet) &&
|
251
|
-
d[:tags].all? {|et| et.is_a?(String)} ) } =>
|
252
|
-
"tags must be a tag_set of strings",
|
253
|
-
|
254
|
-
proc { !d.has_key?(:time_restrictions) ||
|
255
|
-
( d[:time_restrictions].nil? ||
|
256
|
-
d[:time_restrictions].all? {|tr|
|
257
|
-
!!prepare_time_restriction(symbolize(tr))
|
258
|
-
} )
|
259
|
-
} =>
|
260
|
-
"time restrictions are invalid",
|
261
|
-
|
262
|
-
# TODO should the media types be checked against a whitelist?
|
263
|
-
proc { !d.has_key?(:unknown_media) ||
|
264
|
-
( d[:unknown_media].nil? ||
|
265
|
-
d[:unknown_media].is_a?(Array) &&
|
266
|
-
d[:unknown_media].all? {|et| et.is_a?(String)} ) } =>
|
267
|
-
"unknown_media must be a list of strings",
|
268
|
-
|
269
|
-
proc { !d.has_key?(:warning_media) ||
|
270
|
-
( d[:warning_media].nil? ||
|
271
|
-
d[:warning_media].is_a?(Array) &&
|
272
|
-
d[:warning_media].all? {|et| et.is_a?(String)} ) } =>
|
273
|
-
"warning_media must be a list of strings",
|
274
|
-
|
275
|
-
proc { !d.has_key?(:critical_media) ||
|
276
|
-
( d[:critical_media].nil? ||
|
277
|
-
d[:critical_media].is_a?(Array) &&
|
278
|
-
d[:critical_media].all? {|et| et.is_a?(String)} ) } =>
|
279
|
-
"critical_media must be a list of strings",
|
280
|
-
|
281
|
-
proc { !d.has_key?(:unknown_blackhole) ||
|
282
|
-
[TrueClass, FalseClass].include?(d[:unknown_blackhole].class) } =>
|
283
|
-
"unknown_blackhole must be true or false",
|
284
|
-
|
285
|
-
proc { !d.has_key?(:warning_blackhole) ||
|
286
|
-
[TrueClass, FalseClass].include?(d[:warning_blackhole].class) } =>
|
287
|
-
"warning_blackhole must be true or false",
|
288
|
-
|
289
|
-
proc { !d.has_key?(:critical_blackhole) ||
|
290
|
-
[TrueClass, FalseClass].include?(d[:critical_blackhole].class) } =>
|
291
|
-
"critical_blackhole must be true or false",
|
292
|
-
})
|
294
|
+
validations.merge!(VALIDATION_PROCS)
|
293
295
|
|
294
296
|
errors = validations.keys.inject([]) {|ret,vk|
|
295
|
-
ret << "Rule #{validations[vk]}" unless vk.call
|
297
|
+
ret << "Rule #{validations[vk]}" unless vk.call(d)
|
296
298
|
ret
|
297
299
|
}
|
298
300
|
|
@@ -23,7 +23,7 @@
|
|
23
23
|
<%
|
24
24
|
row_colour = case status
|
25
25
|
when 'critical', 'unknown'
|
26
|
-
'
|
26
|
+
'danger'
|
27
27
|
when 'ok', 'up'
|
28
28
|
'success'
|
29
29
|
else
|
@@ -33,23 +33,23 @@
|
|
33
33
|
check_link = "/check?entity=" << u(entity) << "&check=" << u(check)
|
34
34
|
|
35
35
|
%>
|
36
|
-
<tr
|
36
|
+
<tr>
|
37
37
|
<% unless row_entity && entity == row_entity %>
|
38
|
-
<td rowspan
|
38
|
+
<td rowspan="<%= @states[entity].length %>">
|
39
39
|
<a href="<%= entity_link %>"><%= h entity %></a>
|
40
40
|
</td>
|
41
41
|
<% row_entity = entity %>
|
42
42
|
<% end %>
|
43
|
-
<td><a href="<%= check_link %>" title="check detail"><%= h check %></a></td>
|
44
|
-
<td class="<%=
|
43
|
+
<td class="<%= row_colour %>"><a href="<%= check_link %>" title="check detail"><%= h check %></a></td>
|
44
|
+
<td class="<%= row_colour %>">
|
45
45
|
<%= h status.upcase %>
|
46
46
|
<% if in_unscheduled_outage%> (Ack'd)<% end %>
|
47
47
|
<% if in_scheduled_outage %> (Sched)<% end %>
|
48
48
|
</td>
|
49
|
-
<td><%= h summary %></td>
|
50
|
-
<td><%= h changed %></td>
|
51
|
-
<td><%= h updated %></td>
|
52
|
-
<td><%= h notified %></td>
|
49
|
+
<td class="<%= row_colour %>"><%= h summary %></td>
|
50
|
+
<td class="<%= row_colour %>"><%= h changed %></td>
|
51
|
+
<td class="<%= row_colour %>"><%= h updated %></td>
|
52
|
+
<td class="<%= row_colour %>"><%= h notified %></td>
|
53
53
|
</tr>
|
54
54
|
|
55
55
|
<% end %>
|
@@ -143,7 +143,7 @@
|
|
143
143
|
<td><a href="/entity/<%= u(entity.name) %>" title="entity status"><%= h entity.name %></a></td>
|
144
144
|
<td>
|
145
145
|
<% checks.each do |check| %>
|
146
|
-
<%=
|
146
|
+
<%= "<a href=\"/check?entity=#{u(entity.name)}&check=#{u(check)}\" title=\"check status\">#{ h check }</a>" %>
|
147
147
|
<% end %>
|
148
148
|
</td>
|
149
149
|
</tr>
|
@@ -38,10 +38,10 @@
|
|
38
38
|
<% if in_unscheduled_outage%> (Ack'd)<% end %>
|
39
39
|
<% if in_scheduled_outage %> (Sched)<% end %>
|
40
40
|
</td>
|
41
|
-
<td><%= summary %></td>
|
42
|
-
<td><%= changed %></td>
|
43
|
-
<td><%= updated %></td>
|
44
|
-
<td><%= notified %></td>
|
41
|
+
<td><%= h summary %></td>
|
42
|
+
<td><%= h changed %></td>
|
43
|
+
<td><%= h updated %></td>
|
44
|
+
<td><%= h notified %></td>
|
45
45
|
</tr>
|
46
46
|
<% end %>
|
47
47
|
</table>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
|
-
<title><%= include_page_title %></title>
|
4
|
+
<title><%= h include_page_title %></title>
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
6
|
<link rel="stylesheet" href="/css/bootstrap.min.css" media="screen">
|
7
7
|
<link rel="stylesheet" href="/css/bootstrap-responsive.min.css" media="screen">
|
data/lib/flapjack/processor.rb
CHANGED
@@ -25,7 +25,6 @@ module Flapjack
|
|
25
25
|
@config = opts[:config]
|
26
26
|
@redis_config = opts[:redis_config] || {}
|
27
27
|
@logger = opts[:logger]
|
28
|
-
@coordinator = opts[:coordinator]
|
29
28
|
|
30
29
|
@redis = Flapjack::RedisPool.new(:config => @redis_config, :size => 2)
|
31
30
|
|
@@ -102,11 +101,8 @@ module Flapjack
|
|
102
101
|
if @exit_on_queue_empty && event.nil? && Flapjack::Data::Event.pending_count(@queue, :redis => @redis)
|
103
102
|
# SHUT IT ALL DOWN!!!
|
104
103
|
@logger.warn "Shutting down as exit_on_queue_empty is true, and the queue is empty"
|
105
|
-
|
106
|
-
|
107
|
-
# FIXME: seems the above call doesn't block until the remove_pikelets fiber exits...
|
108
|
-
EM::Synchrony.sleep(1)
|
109
|
-
exit
|
104
|
+
Process.kill('INT', Process.pid)
|
105
|
+
break
|
110
106
|
end
|
111
107
|
|
112
108
|
process_event(event) unless event.nil?
|
data/lib/flapjack/version.rb
CHANGED
@@ -29,19 +29,18 @@ describe Flapjack::Coordinator do
|
|
29
29
|
|
30
30
|
fc = Flapjack::Coordinator.new(config)
|
31
31
|
expect(Flapjack::Pikelet).to receive(:create).with('processor',
|
32
|
-
:config => cfg['processor'], :redis_config => {}, :boot_time => time
|
32
|
+
:config => cfg['processor'], :redis_config => {}, :boot_time => time).
|
33
33
|
and_return(processor)
|
34
34
|
|
35
|
-
expect(fiber).to receive(:resume)
|
36
|
-
expect(Fiber).to receive(:new).and_yield.and_return(fiber)
|
37
|
-
|
38
35
|
expect(EM).to receive(:stop)
|
36
|
+
expect(EM::Synchrony).to receive(:sleep).and_return {
|
37
|
+
fc.instance_variable_set('@received_signals', ['INT'])
|
38
|
+
}
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
expect(Syslog).to receive(:opened?).and_return(true)
|
41
|
+
expect(Syslog).to receive(:close)
|
42
42
|
|
43
43
|
fc.start(:signals => false)
|
44
|
-
fc.stop
|
45
44
|
end
|
46
45
|
|
47
46
|
it "handles an exception raised by a pikelet and shuts down" do
|
@@ -63,19 +62,15 @@ describe Flapjack::Coordinator do
|
|
63
62
|
|
64
63
|
fc = Flapjack::Coordinator.new(config)
|
65
64
|
expect(Flapjack::Pikelet).to receive(:create).with('processor',
|
66
|
-
:config => cfg['processor'], :redis_config => {}, :boot_time => time
|
65
|
+
:config => cfg['processor'], :redis_config => {}, :boot_time => time)
|
67
66
|
.and_return(processor)
|
68
67
|
|
69
|
-
expect(fiber).to receive(:resume)
|
70
|
-
expect(Fiber).to receive(:new).and_yield.and_return(fiber)
|
71
|
-
|
72
68
|
expect(EM).to receive(:stop)
|
73
69
|
|
74
|
-
|
75
|
-
|
70
|
+
expect(Syslog).to receive(:opened?).and_return(true)
|
71
|
+
expect(Syslog).to receive(:close)
|
76
72
|
|
77
73
|
fc.start(:signals => false)
|
78
|
-
fc.stop
|
79
74
|
end
|
80
75
|
|
81
76
|
it "loads an old executive pikelet config block with no new data" do
|
@@ -100,22 +95,21 @@ describe Flapjack::Coordinator do
|
|
100
95
|
|
101
96
|
fc = Flapjack::Coordinator.new(config)
|
102
97
|
expect(Flapjack::Pikelet).to receive(:create).with('processor',
|
103
|
-
:config => cfg['executive'], :redis_config => {}, :boot_time => time
|
98
|
+
:config => cfg['executive'], :redis_config => {}, :boot_time => time).
|
104
99
|
and_return(processor)
|
105
100
|
expect(Flapjack::Pikelet).to receive(:create).with('notifier',
|
106
|
-
:config => cfg['executive'], :redis_config => {}, :boot_time => time
|
101
|
+
:config => cfg['executive'], :redis_config => {}, :boot_time => time).
|
107
102
|
and_return(notifier)
|
108
103
|
|
109
|
-
expect(fiber).to receive(:resume)
|
110
|
-
expect(Fiber).to receive(:new).and_yield.and_return(fiber)
|
111
|
-
|
112
104
|
expect(EM).to receive(:stop)
|
105
|
+
expect(EM::Synchrony).to receive(:sleep).and_return {
|
106
|
+
fc.instance_variable_set('@received_signals', ['INT'])
|
107
|
+
}
|
113
108
|
|
114
|
-
|
115
|
-
|
109
|
+
expect(Syslog).to receive(:opened?).and_return(true)
|
110
|
+
expect(Syslog).to receive(:close)
|
116
111
|
|
117
112
|
fc.start(:signals => false)
|
118
|
-
fc.stop
|
119
113
|
end
|
120
114
|
|
121
115
|
it "loads an old executive pikelet config block with some new data" do
|
@@ -138,19 +132,18 @@ describe Flapjack::Coordinator do
|
|
138
132
|
fc = Flapjack::Coordinator.new(config)
|
139
133
|
expect(Flapjack::Pikelet).to receive(:create).with('processor',
|
140
134
|
:config => cfg['executive'].merge(cfg['processor']),
|
141
|
-
:redis_config => {}, :boot_time => time
|
135
|
+
:redis_config => {}, :boot_time => time).
|
142
136
|
and_return(processor)
|
143
137
|
|
144
|
-
expect(fiber).to receive(:resume)
|
145
|
-
expect(Fiber).to receive(:new).and_yield.and_return(fiber)
|
146
|
-
|
147
138
|
expect(EM).to receive(:stop)
|
139
|
+
expect(EM::Synchrony).to receive(:sleep).and_return {
|
140
|
+
fc.instance_variable_set('@received_signals', ['INT'])
|
141
|
+
}
|
148
142
|
|
149
|
-
|
150
|
-
|
143
|
+
expect(Syslog).to receive(:opened?).and_return(true)
|
144
|
+
expect(Syslog).to receive(:close)
|
151
145
|
|
152
146
|
fc.start(:signals => false)
|
153
|
-
fc.stop
|
154
147
|
end
|
155
148
|
|
156
149
|
it "traps system signals and shuts down" do
|
@@ -166,10 +159,9 @@ describe Flapjack::Coordinator do
|
|
166
159
|
expect(config).to receive(:all).and_return({})
|
167
160
|
expect(config).to receive(:for_redis).and_return({})
|
168
161
|
fc = Flapjack::Coordinator.new(config)
|
169
|
-
expect(fc).to receive(:stop).exactly(3).times
|
170
|
-
expect(fc).to receive(:reload)
|
171
162
|
|
172
163
|
fc.send(:setup_signals)
|
164
|
+
expect(fc.instance_variable_get('@received_signals')).to eq(['INT', 'TERM', 'QUIT', 'HUP'])
|
173
165
|
end
|
174
166
|
|
175
167
|
it "only traps two system signals on Windows" do
|
@@ -185,9 +177,9 @@ describe Flapjack::Coordinator do
|
|
185
177
|
expect(config).to receive(:all).and_return({})
|
186
178
|
expect(config).to receive(:for_redis).and_return({})
|
187
179
|
fc = Flapjack::Coordinator.new(config)
|
188
|
-
expect(fc).to receive(:stop).twice
|
189
180
|
|
190
181
|
fc.send(:setup_signals)
|
182
|
+
expect(fc.instance_variable_get('@received_signals')).to eq(['INT', 'TERM'])
|
191
183
|
end
|
192
184
|
|
193
185
|
it "stops one pikelet and starts another on reload" do
|
@@ -218,19 +210,14 @@ describe Flapjack::Coordinator do
|
|
218
210
|
jabber = double('jabber')
|
219
211
|
expect(Flapjack::Pikelet).to receive(:create).
|
220
212
|
with('jabber', :config => {"enabled" => true}, :redis_config => {},
|
221
|
-
:boot_time => time
|
213
|
+
:boot_time => time).
|
222
214
|
and_return(jabber)
|
223
215
|
expect(jabber).to receive(:start)
|
224
216
|
|
225
|
-
expect(fiber).to receive(:resume)
|
226
|
-
expect(Fiber).to receive(:new).and_yield.and_return(fiber)
|
227
|
-
|
228
217
|
fc.instance_variable_set('@boot_time', time)
|
229
218
|
fc.instance_variable_set('@pikelets', [processor])
|
230
|
-
fc.reload
|
219
|
+
fc.send(:reload)
|
231
220
|
expect(fc.instance_variable_get('@pikelets')).to eq([jabber])
|
232
|
-
|
233
|
-
|
234
221
|
end
|
235
222
|
|
236
223
|
it "reloads a pikelet config without restarting it" do
|
@@ -259,7 +246,7 @@ describe Flapjack::Coordinator do
|
|
259
246
|
fc = Flapjack::Coordinator.new(config)
|
260
247
|
fc.instance_variable_set('@boot_time', time)
|
261
248
|
fc.instance_variable_set('@pikelets', [processor])
|
262
|
-
fc.reload
|
249
|
+
fc.send(:reload)
|
263
250
|
expect(fc.instance_variable_get('@pikelets')).to eq([processor])
|
264
251
|
end
|
265
252
|
|
@@ -286,9 +273,6 @@ describe Flapjack::Coordinator do
|
|
286
273
|
expect(processor).to receive(:update_status)
|
287
274
|
expect(processor).to receive(:status).exactly(3).times.and_return('stopped')
|
288
275
|
|
289
|
-
expect(fiber).to receive(:resume)
|
290
|
-
expect(Fiber).to receive(:new).and_yield.and_return(fiber)
|
291
|
-
|
292
276
|
new_exec = double('new_executive')
|
293
277
|
expect(new_exec).to receive(:start)
|
294
278
|
|
@@ -297,12 +281,12 @@ describe Flapjack::Coordinator do
|
|
297
281
|
|
298
282
|
expect(Flapjack::Pikelet).to receive(:create).
|
299
283
|
with('processor', :config => new_cfg['processor'], :redis_config => {},
|
300
|
-
:boot_time => time
|
284
|
+
:boot_time => time).
|
301
285
|
and_return(new_exec)
|
302
286
|
|
303
287
|
fc.instance_variable_set('@boot_time', time)
|
304
288
|
fc.instance_variable_set('@pikelets', [processor])
|
305
|
-
fc.reload
|
289
|
+
fc.send(:reload)
|
306
290
|
expect(fc.instance_variable_get('@pikelets')).to eq([new_exec])
|
307
291
|
end
|
308
292
|
|
@@ -23,36 +23,163 @@ describe Flapjack::Data::Event do
|
|
23
23
|
context 'class' do
|
24
24
|
|
25
25
|
it "returns the next event (blocking, archiving)" do
|
26
|
-
expect(mock_redis).to receive(:brpoplpush).
|
26
|
+
expect(mock_redis).to receive(:brpoplpush).
|
27
|
+
with('events', /^events_archive:/, 0).and_return(event_data.to_json)
|
27
28
|
expect(mock_redis).to receive(:expire)
|
28
29
|
|
29
|
-
result = Flapjack::Data::Event.next('events', :block => true,
|
30
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
31
|
+
:archive_events => true, :redis => mock_redis)
|
30
32
|
expect(result).to be_an_instance_of(Flapjack::Data::Event)
|
31
33
|
end
|
32
34
|
|
33
35
|
it "returns the next event (blocking, not archiving)" do
|
34
|
-
expect(mock_redis).to receive(:brpop).with('events', 0).
|
36
|
+
expect(mock_redis).to receive(:brpop).with('events', 0).
|
37
|
+
and_return(['events', event_data.to_json])
|
35
38
|
|
36
|
-
result = Flapjack::Data::Event.next('events'
|
39
|
+
result = Flapjack::Data::Event.next('events',:block => true,
|
40
|
+
:archive_events => false, :redis => mock_redis)
|
37
41
|
expect(result).to be_an_instance_of(Flapjack::Data::Event)
|
38
42
|
end
|
39
43
|
|
40
44
|
it "returns the next event (non-blocking, archiving)" do
|
41
|
-
expect(mock_redis).to receive(:rpoplpush).
|
45
|
+
expect(mock_redis).to receive(:rpoplpush).
|
46
|
+
with('events', /^events_archive:/).and_return(event_data.to_json)
|
42
47
|
expect(mock_redis).to receive(:expire)
|
43
48
|
|
44
|
-
result = Flapjack::Data::Event.next('events', :block => false,
|
49
|
+
result = Flapjack::Data::Event.next('events', :block => false,
|
50
|
+
:archive_events => true, :redis => mock_redis)
|
45
51
|
expect(result).to be_an_instance_of(Flapjack::Data::Event)
|
46
52
|
end
|
47
53
|
|
48
54
|
it "returns the next event (non-blocking, not archiving)" do
|
49
|
-
expect(mock_redis).to receive(:rpop).with('events').
|
55
|
+
expect(mock_redis).to receive(:rpop).with('events').
|
56
|
+
and_return(event_data.to_json)
|
50
57
|
|
51
|
-
result = Flapjack::Data::Event.next('events', :block => false,
|
58
|
+
result = Flapjack::Data::Event.next('events', :block => false,
|
59
|
+
:archive_events => false, :redis => mock_redis)
|
52
60
|
expect(result).to be_an_instance_of(Flapjack::Data::Event)
|
53
61
|
end
|
54
62
|
|
55
|
-
it "
|
63
|
+
it "rejects invalid event JSON (archiving)" do
|
64
|
+
bad_event_json = '{{{'
|
65
|
+
expect(mock_redis).to receive(:brpoplpush).
|
66
|
+
with('events', /^events_archive:/, 0).and_return(bad_event_json)
|
67
|
+
expect(mock_redis).to receive(:multi)
|
68
|
+
expect(mock_redis).to receive(:lrem).with(/^events_archive:/, 1, bad_event_json)
|
69
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
70
|
+
expect(mock_redis).to receive(:exec)
|
71
|
+
expect(mock_redis).to receive(:expire)
|
72
|
+
|
73
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
74
|
+
:archive_events => true, :redis => mock_redis)
|
75
|
+
expect(result).to be_nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it "rejects invalid event JSON (not archiving)" do
|
79
|
+
bad_event_json = '{{{'
|
80
|
+
expect(mock_redis).to receive(:brpop).with('events', 0).
|
81
|
+
and_return(['events', bad_event_json])
|
82
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
83
|
+
|
84
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
85
|
+
:archive_events => false, :redis => mock_redis)
|
86
|
+
expect(result).to be_nil
|
87
|
+
end
|
88
|
+
|
89
|
+
['type', 'state', 'entity', 'check', 'summary'].each do |required_key|
|
90
|
+
|
91
|
+
it "rejects an event with missing '#{required_key}' key (archiving)" do
|
92
|
+
bad_event_data = event_data.clone
|
93
|
+
bad_event_data.delete(required_key)
|
94
|
+
bad_event_json = bad_event_data.to_json
|
95
|
+
expect(mock_redis).to receive(:brpoplpush).
|
96
|
+
with('events', /^events_archive:/, 0).and_return(bad_event_json)
|
97
|
+
expect(mock_redis).to receive(:multi)
|
98
|
+
expect(mock_redis).to receive(:lrem).with(/^events_archive:/, 1, bad_event_json)
|
99
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
100
|
+
expect(mock_redis).to receive(:exec)
|
101
|
+
expect(mock_redis).to receive(:expire)
|
102
|
+
|
103
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
104
|
+
:archive_events => true, :redis => mock_redis)
|
105
|
+
expect(result).to be_nil
|
106
|
+
end
|
107
|
+
|
108
|
+
it "rejects an event with missing '#{required_key}' key (not archiving)" do
|
109
|
+
bad_event_data = event_data.clone
|
110
|
+
bad_event_data.delete(required_key)
|
111
|
+
bad_event_json = bad_event_data.to_json
|
112
|
+
expect(mock_redis).to receive(:brpop).with('events', 0).
|
113
|
+
and_return(['events', bad_event_json])
|
114
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
115
|
+
|
116
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
117
|
+
:archive_events => false, :redis => mock_redis)
|
118
|
+
expect(result).to be_nil
|
119
|
+
end
|
120
|
+
|
121
|
+
it "rejects an event with invalid '#{required_key}' key (archiving)" do
|
122
|
+
bad_event_data = event_data.clone
|
123
|
+
bad_event_data[required_key] = {'hello' => 'there'}
|
124
|
+
bad_event_json = bad_event_data.to_json
|
125
|
+
expect(mock_redis).to receive(:brpoplpush).
|
126
|
+
with('events', /^events_archive:/, 0).and_return(bad_event_json)
|
127
|
+
expect(mock_redis).to receive(:multi)
|
128
|
+
expect(mock_redis).to receive(:lrem).with(/^events_archive:/, 1, bad_event_json)
|
129
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
130
|
+
expect(mock_redis).to receive(:exec)
|
131
|
+
expect(mock_redis).to receive(:expire)
|
132
|
+
|
133
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
134
|
+
:archive_events => true, :redis => mock_redis)
|
135
|
+
expect(result).to be_nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it "rejects an event with invalid '#{required_key}' key (not archiving)" do
|
139
|
+
bad_event_data = event_data.clone
|
140
|
+
bad_event_data[required_key] = {'hello' => 'there'}
|
141
|
+
bad_event_json = bad_event_data.to_json
|
142
|
+
expect(mock_redis).to receive(:brpop).with('events', 0).
|
143
|
+
and_return(['events', bad_event_json])
|
144
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
145
|
+
|
146
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
147
|
+
:archive_events => false, :redis => mock_redis)
|
148
|
+
expect(result).to be_nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
['time', 'details', 'acknowledgement_id', 'duration'].each do |optional_key|
|
153
|
+
it "rejects an event with invalid '#{optional_key}' key (archiving)" do
|
154
|
+
bad_event_data = event_data.clone
|
155
|
+
bad_event_data[optional_key] = {'hello' => 'there'}
|
156
|
+
bad_event_json = bad_event_data.to_json
|
157
|
+
expect(mock_redis).to receive(:brpoplpush).
|
158
|
+
with('events', /^events_archive:/, 0).and_return(bad_event_json)
|
159
|
+
expect(mock_redis).to receive(:multi)
|
160
|
+
expect(mock_redis).to receive(:lrem).with(/^events_archive:/, 1, bad_event_json)
|
161
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
162
|
+
expect(mock_redis).to receive(:exec)
|
163
|
+
expect(mock_redis).to receive(:expire)
|
164
|
+
|
165
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
166
|
+
:archive_events => true, :redis => mock_redis)
|
167
|
+
expect(result).to be_nil
|
168
|
+
end
|
169
|
+
|
170
|
+
it "rejects an event with invalid '#{optional_key}' key (archiving)" do
|
171
|
+
bad_event_data = event_data.clone
|
172
|
+
bad_event_data[optional_key] = {'hello' => 'there'}
|
173
|
+
bad_event_json = bad_event_data.to_json
|
174
|
+
expect(mock_redis).to receive(:brpop).with('events', 0).
|
175
|
+
and_return(['events', bad_event_json])
|
176
|
+
expect(mock_redis).to receive(:lpush).with(/^events_rejected:/, bad_event_json)
|
177
|
+
|
178
|
+
result = Flapjack::Data::Event.next('events', :block => true,
|
179
|
+
:archive_events => false, :redis => mock_redis)
|
180
|
+
expect(result).to be_nil
|
181
|
+
end
|
182
|
+
end
|
56
183
|
|
57
184
|
it "returns a count of pending events" do
|
58
185
|
events_len = 23
|
data/tasks/benchmarks.rake
CHANGED
@@ -54,10 +54,12 @@ namespace :benchmarks do
|
|
54
54
|
end
|
55
55
|
time_flapjack_start = Time.now.to_f
|
56
56
|
puts "Starting flapjack..."
|
57
|
-
|
57
|
+
result = system({"FLAPJACK_ENV" => FLAPJACK_ENV,
|
58
58
|
"CPUPROFILE" => "artifacts/flapjack-perftools-cpuprofile",
|
59
59
|
"RUBYOPT" => "-r#{perftools}"},
|
60
60
|
"bin/flapjack start --no-daemonize --config tasks/support/flapjack_config_benchmark.yaml")
|
61
|
+
status = $?
|
62
|
+
if status.exited? && (Signal.list['INT'] + 128).eql?($?.exitstatus)
|
61
63
|
puts "Flapjack run completed successfully"
|
62
64
|
else
|
63
65
|
raise "Problem starting flapjack: #{$?}"
|
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.8.
|
4
|
+
version: 0.8.2
|
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-01-
|
13
|
+
date: 2014-01-16 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: dante
|