flapjack 0.4.12 → 0.5.1
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.
- data/README.md +77 -50
- data/Rakefile +78 -26
- data/TODO.md +15 -32
- data/bin/flapjack-benchmark +50 -0
- data/bin/flapjack-notifier +11 -36
- data/bin/flapjack-notifier-manager +1 -3
- data/bin/flapjack-worker +5 -19
- data/doc/PACKAGING.md +25 -0
- data/etc/flapjack/flapjack-notifier.conf.example +34 -0
- data/etc/flapjack/recipients.conf.example +14 -0
- data/features/flapjack-notifier-manager.feature +19 -0
- data/features/flapjack-worker-manager.feature +25 -0
- data/features/packaging-lintian.feature +15 -0
- data/features/persistence/couch.feature +105 -0
- data/features/persistence/sqlite3.feature +105 -0
- data/features/persistence/steps/couch_steps.rb +25 -0
- data/features/persistence/steps/generic_steps.rb +102 -0
- data/features/persistence/steps/sqlite3_steps.rb +13 -0
- data/features/steps/flapjack-notifier-manager_steps.rb +24 -0
- data/features/steps/flapjack-worker-manager_steps.rb +50 -0
- data/features/steps/packaging-lintian_steps.rb +13 -0
- data/features/support/env.rb +22 -0
- data/features/support/silent_system.rb +4 -0
- data/flapjack.gemspec +7 -11
- data/lib/flapjack/applications/notifier.rb +222 -0
- data/lib/flapjack/applications/worker.rb +99 -0
- data/lib/flapjack/checks/ping +10 -0
- data/lib/flapjack/cli/notifier.rb +80 -218
- data/lib/flapjack/cli/worker.rb +1 -86
- data/lib/flapjack/filters/any_parents_failed.rb +14 -0
- data/lib/flapjack/filters/ok.rb +13 -0
- data/lib/flapjack/inifile.rb +44 -0
- data/lib/flapjack/{notifier.rb → notifier_engine.rb} +13 -9
- data/lib/flapjack/notifiers/mailer/mailer.rb +12 -13
- data/lib/flapjack/notifiers/xmpp/xmpp.rb +2 -2
- data/lib/flapjack/patches.rb +25 -0
- data/lib/flapjack/persistence/couch.rb +5 -0
- data/lib/flapjack/persistence/couch/connection.rb +66 -0
- data/lib/flapjack/persistence/couch/couch.rb +63 -0
- data/lib/flapjack/persistence/data_mapper.rb +3 -0
- data/lib/flapjack/persistence/data_mapper/data_mapper.rb +67 -0
- data/lib/flapjack/{models → persistence/data_mapper/models}/check.rb +3 -7
- data/lib/flapjack/{models → persistence/data_mapper/models}/check_template.rb +0 -0
- data/lib/flapjack/persistence/data_mapper/models/event.rb +17 -0
- data/lib/flapjack/{models → persistence/data_mapper/models}/node.rb +0 -0
- data/lib/flapjack/{models → persistence/data_mapper/models}/related_check.rb +0 -0
- data/lib/flapjack/persistence/sqlite3.rb +3 -0
- data/lib/flapjack/persistence/sqlite3/sqlite3.rb +166 -0
- data/lib/flapjack/transports/beanstalkd.rb +33 -0
- data/lib/flapjack/transports/result.rb +58 -0
- metadata +46 -56
- data/etc/flapjack/flapjack-notifier.yaml.example +0 -8
- data/etc/flapjack/recipients.yaml.example +0 -10
- data/lib/flapjack/database.rb +0 -10
- data/lib/flapjack/result.rb +0 -47
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Nothing Flapjack-specific here - feel free to reuse.
|
4
|
+
|
5
|
+
module Flapjack
|
6
|
+
class Inifile
|
7
|
+
|
8
|
+
def initialize(string)
|
9
|
+
@data = {}
|
10
|
+
|
11
|
+
string.split("\n").each do |line|
|
12
|
+
# sections
|
13
|
+
if line =~ /^\s*\[(.+)\]\s*(;.+)*$/
|
14
|
+
@current_section = $1
|
15
|
+
@data[@current_section] ||= {}
|
16
|
+
end
|
17
|
+
# parameters
|
18
|
+
if line =~ /^\s*(.+)\s*=\s*([^;]+)\s*(;.+)*$/ # after = captures up to ; then groups everything after ;
|
19
|
+
key = $1.strip
|
20
|
+
value = $2.strip
|
21
|
+
@data[@current_section][key] = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](key)
|
27
|
+
@data[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def keys
|
31
|
+
@data.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.read(filename)
|
35
|
+
self.new(File.read(filename))
|
36
|
+
end
|
37
|
+
|
38
|
+
def all
|
39
|
+
@data
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -3,14 +3,13 @@
|
|
3
3
|
require 'ostruct'
|
4
4
|
|
5
5
|
module Flapjack
|
6
|
-
class
|
6
|
+
class NotifierEngine
|
7
7
|
|
8
|
-
attr_reader :
|
8
|
+
attr_reader :log, :notifiers
|
9
9
|
|
10
10
|
def initialize(opts={})
|
11
|
-
@log = opts[:
|
11
|
+
@log = opts[:log]
|
12
12
|
raise "you have to specify a logger" unless @log
|
13
|
-
@recipients = (opts[:recipients] || [])
|
14
13
|
|
15
14
|
@notifiers = []
|
16
15
|
if opts[:notifiers]
|
@@ -19,15 +18,20 @@ module Flapjack
|
|
19
18
|
@log.info("using the #{n.class.to_s.split("::").last} notifier")
|
20
19
|
end
|
21
20
|
else
|
22
|
-
@log.warning("
|
21
|
+
@log.warning("There are no notifiers! flapjack-notifier won't be useful.")
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
|
25
|
+
def notify!(options={})
|
26
|
+
result = options[:result]
|
27
|
+
event = options[:event]
|
28
|
+
recipients = options[:recipients]
|
29
|
+
|
30
|
+
raise ArgumentError, "A result + event were not passed!" unless result && event
|
31
|
+
|
28
32
|
@notifiers.each do |n|
|
29
|
-
|
30
|
-
@log.info("Notifying #{recipient.name} via #{n.class} about check #{result.
|
33
|
+
recipients.each do |recipient|
|
34
|
+
@log.info("Notifying #{recipient.name} via #{n.class} about check #{result.check_id}")
|
31
35
|
n.notify(:result => result, :who => recipient, :event => event)
|
32
36
|
end
|
33
37
|
end
|
@@ -3,40 +3,39 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'net/smtp'
|
5
5
|
require 'tmail'
|
6
|
-
require 'log4r'
|
7
6
|
|
8
7
|
module Flapjack
|
9
8
|
module Notifiers
|
10
9
|
|
11
10
|
class Mailer
|
12
11
|
|
12
|
+
attr_accessor :log, :from_address
|
13
|
+
|
13
14
|
def initialize(opts={})
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
@website_uri = opts[:website_uri] ? opts[:website_uri].gsub(/\/$/, '') : "http://#{`hostname`}"
|
20
|
-
@log = opts[:logger]
|
21
|
-
@log ||= ::Log4r::Logger.new("notifier")
|
15
|
+
@log = opts[:log]
|
16
|
+
@from_address = opts[:from_address]
|
17
|
+
@website_uri = opts[:website_uri]
|
18
|
+
|
19
|
+
raise ArgumentError, "from address must be provided" unless @from_address
|
22
20
|
end
|
23
21
|
|
24
22
|
def notify(opts={})
|
25
23
|
raise ArgumentError, "a recipient was not specified" unless opts[:who]
|
26
24
|
raise ArgumentError, "a result was not specified" unless opts[:result]
|
27
|
-
|
25
|
+
|
26
|
+
# potential FIXME: refactor TMail out entirely?
|
28
27
|
mail = TMail::Mail.new
|
29
28
|
mail.to = opts[:who].email
|
30
29
|
mail.from = @from_address
|
31
|
-
mail.subject = "Check: #{opts[:result].
|
30
|
+
mail.subject = "Check: #{opts[:result].check_id}, Status: #{opts[:result].status}"
|
32
31
|
mail.body = <<-DESC
|
33
|
-
Check #{opts[:result].
|
32
|
+
Check #{opts[:result].check_id} returned the status "#{opts[:result].status}".
|
34
33
|
|
35
34
|
Here was the output:
|
36
35
|
#{opts[:result].output}
|
37
36
|
|
38
37
|
You can respond to this issue at:
|
39
|
-
#{@website_uri}/issue/#{opts[:result].
|
38
|
+
#{@website_uri}/issue/#{opts[:result].check_id}
|
40
39
|
DESC
|
41
40
|
|
42
41
|
begin
|
@@ -31,8 +31,8 @@ module Flapjack
|
|
31
31
|
raise ArgumentError, "a result was not specified" unless opts[:result]
|
32
32
|
|
33
33
|
text = <<-DESC
|
34
|
-
Check #{opts[:result].
|
35
|
-
http://localhost:4000/checks/#{opts[:result].
|
34
|
+
Check #{opts[:result].check_id} returned the status "#{opts[:result].status}".
|
35
|
+
http://localhost:4000/checks/#{opts[:result].check_id}
|
36
36
|
DESC
|
37
37
|
|
38
38
|
message = Jabber::Message.new(opts[:who].jid, text)
|
data/lib/flapjack/patches.rb
CHANGED
@@ -22,5 +22,30 @@ module Log4r
|
|
22
22
|
def error(args)
|
23
23
|
err(args)
|
24
24
|
end
|
25
|
+
|
26
|
+
def warning(args)
|
27
|
+
warn(args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# extracted from Extlib.
|
33
|
+
# FIXME: what's the licensing here?
|
34
|
+
class String
|
35
|
+
def camel_case
|
36
|
+
return self if self !~ /_/ && self =~ /[A-Z]+.*/
|
37
|
+
split('_').map{|e| e.capitalize}.join
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# http://gist.github.com/151324
|
42
|
+
class Hash
|
43
|
+
def symbolize_keys
|
44
|
+
inject({}) do |acc, (k,v)|
|
45
|
+
key = String === k ? k.to_sym : k
|
46
|
+
value = Hash === v ? v.symbolize_keys : v
|
47
|
+
acc[key] = value
|
48
|
+
acc
|
49
|
+
end
|
25
50
|
end
|
26
51
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
module Flapjack
|
4
|
+
module Persistence
|
5
|
+
class Couch
|
6
|
+
class Connection
|
7
|
+
class << self
|
8
|
+
attr_accessor :host, :port
|
9
|
+
def setup(options={})
|
10
|
+
@host = options[:host]
|
11
|
+
@port = options[:port]
|
12
|
+
@database = options[:database]
|
13
|
+
end
|
14
|
+
|
15
|
+
def get(id)
|
16
|
+
uri = "/#{@database}/#{id}"
|
17
|
+
req = ::Net::HTTP::Get.new(uri)
|
18
|
+
request(req)
|
19
|
+
end
|
20
|
+
|
21
|
+
def post(options={})
|
22
|
+
document = options[:document]
|
23
|
+
uri = "/#{@database}/"
|
24
|
+
|
25
|
+
req = ::Net::HTTP::Post.new(uri)
|
26
|
+
req["content-type"] = "application/json"
|
27
|
+
req.body = document.to_json
|
28
|
+
|
29
|
+
request(req)
|
30
|
+
end
|
31
|
+
|
32
|
+
def put(options={})
|
33
|
+
document = options[:document]
|
34
|
+
uri = "/#{@database}/#{(options[:document]["id"] || options[:document]["_id"])}"
|
35
|
+
|
36
|
+
req = ::Net::HTTP::Put.new(uri)
|
37
|
+
req["content-type"] = "application/json"
|
38
|
+
req.body = document.to_json
|
39
|
+
|
40
|
+
request(req)
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete(options={})
|
44
|
+
document = options[:document]
|
45
|
+
|
46
|
+
uri = "/#{@database}/#{(options[:document]["id"] || options[:document]["_id"])}"
|
47
|
+
|
48
|
+
req = ::Net::HTTP::Put.new(uri)
|
49
|
+
req["content-type"] = "application/json"
|
50
|
+
req.body = document.to_json
|
51
|
+
|
52
|
+
request(req)
|
53
|
+
end
|
54
|
+
|
55
|
+
def request(request)
|
56
|
+
response = Net::HTTP.start(@host, @port) {|http| http.request(request)}
|
57
|
+
|
58
|
+
@parser = Yajl::Parser.new
|
59
|
+
hash = @parser.parse(response.body)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end # class Couch
|
64
|
+
end # module Persistence
|
65
|
+
end # module Flapjack
|
66
|
+
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'yajl/json_gem'
|
5
|
+
|
6
|
+
module Flapjack
|
7
|
+
module Persistence
|
8
|
+
class Couch
|
9
|
+
|
10
|
+
attr_accessor :config
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
@options = options
|
14
|
+
@config = OpenStruct.new(options)
|
15
|
+
@log = @config.log
|
16
|
+
|
17
|
+
Flapjack::Persistence::Couch::Connection.setup(@options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def any_parents_failed?(id)
|
21
|
+
id == "1" ? false : true
|
22
|
+
end
|
23
|
+
|
24
|
+
def save_check(attrs)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_check(id)
|
29
|
+
id == "4" ? nil : true
|
30
|
+
end
|
31
|
+
|
32
|
+
def delete_check(id)
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
def all_checks
|
37
|
+
[true, true, true]
|
38
|
+
end
|
39
|
+
|
40
|
+
def save_check_relationship(attrs)
|
41
|
+
true
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_event(result)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def all_events_for(id)
|
49
|
+
[true, true, true]
|
50
|
+
end
|
51
|
+
|
52
|
+
def all_events
|
53
|
+
[true, true, true]
|
54
|
+
end
|
55
|
+
|
56
|
+
def all_check_relationships
|
57
|
+
[true, true, true]
|
58
|
+
end
|
59
|
+
|
60
|
+
end # class Couch
|
61
|
+
end # module Persistence
|
62
|
+
end # module Flapjack
|
63
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'dm-core'
|
4
|
+
require 'dm-validations'
|
5
|
+
require 'dm-types'
|
6
|
+
require 'dm-timestamps'
|
7
|
+
|
8
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'models', '*.rb')).each do |model|
|
9
|
+
require model
|
10
|
+
end
|
11
|
+
|
12
|
+
module Flapjack
|
13
|
+
module Persistence
|
14
|
+
class DataMapper
|
15
|
+
def initialize(options={})
|
16
|
+
@options = options
|
17
|
+
@config = OpenStruct.new(options)
|
18
|
+
@log = @config.log
|
19
|
+
connect
|
20
|
+
end
|
21
|
+
|
22
|
+
def any_parents_failed?(result)
|
23
|
+
check = Check.get(result.check_id)
|
24
|
+
if check
|
25
|
+
check.worst_parent_status != 0
|
26
|
+
else
|
27
|
+
@log.warning("Check with id #{result.check_id} doesn't exist!")
|
28
|
+
false # this will notify
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def save(result)
|
33
|
+
check = Check.get(result.check_id)
|
34
|
+
if check
|
35
|
+
check.status = result.status
|
36
|
+
check.save
|
37
|
+
else
|
38
|
+
@log.warning("Check with id #{result.check_id} doesn't exist!")
|
39
|
+
true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def create_event(result)
|
44
|
+
event = Event.new(:check_id => result.check_id)
|
45
|
+
event.save
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
def connect
|
50
|
+
raise ArgumentError, "Database URI wasn't specified" unless @config.uri
|
51
|
+
::DataMapper.setup(:default, @config.uri)
|
52
|
+
validate_structure
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate_structure
|
56
|
+
begin
|
57
|
+
::DataMapper.repository(:default).adapter.execute("SELECT 'id' FROM 'checks';")
|
58
|
+
rescue Sqlite3Error => e
|
59
|
+
@log.warning("The specified database doesn't appear to have any structure!")
|
60
|
+
@log.warning("Exiting.")
|
61
|
+
raise
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
File without changes
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Event
|
2
|
+
include DataMapper::Resource
|
3
|
+
|
4
|
+
timestamps :at
|
5
|
+
|
6
|
+
belongs_to :check
|
7
|
+
|
8
|
+
property :id, Serial, :key => true
|
9
|
+
property :check_id, Integer, :nullable => false
|
10
|
+
#property :check_output, Text, :nullable => false
|
11
|
+
|
12
|
+
# dm-timestamps
|
13
|
+
property :created_at, DateTime
|
14
|
+
property :updated_at, DateTime
|
15
|
+
property :deleted_at, ParanoidDateTime
|
16
|
+
|
17
|
+
end
|