flapjack 1.0.0rc1 → 1.0.0rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/CHANGELOG.md +15 -0
- data/README.md +4 -0
- data/bin/flapjack +15 -2
- data/build.sh +22 -0
- data/dist/etc/init.d/flapjack +1 -1
- data/dist/etc/init.d/flapjack-nagios-receiver +3 -3
- data/dist/etc/init.d/flapper +3 -3
- data/etc/flapjack_config.yaml.example +35 -17
- data/lib/flapjack/cli/receiver.rb +25 -1
- data/lib/flapjack/cli/simulate.rb +16 -9
- data/lib/flapjack/data/entity.rb +17 -21
- data/lib/flapjack/data/entity_check.rb +10 -0
- data/lib/flapjack/gateways/jabber.rb +36 -12
- data/lib/flapjack/gateways/jsonapi/check_methods.rb +32 -0
- data/lib/flapjack/gateways/jsonapi/report_methods.rb +1 -5
- data/lib/flapjack/gateways/web/views/check.html.erb +1 -1
- data/lib/flapjack/gateways/web/views/checks.html.erb +2 -2
- data/lib/flapjack/gateways/web/views/contact.html.erb +2 -2
- data/lib/flapjack/gateways/web/views/entity.html.erb +1 -1
- data/lib/flapjack/patches.rb +70 -0
- data/lib/flapjack/version.rb +1 -1
- data/libexec/httpbroker.go +161 -0
- data/libexec/oneoff.go +75 -0
- data/spec/lib/flapjack/data/entity_spec.rb +1 -1
- data/spec/lib/flapjack/gateways/jabber_spec.rb +15 -17
- data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +57 -0
- data/src/flapjack/event.go +25 -0
- data/src/flapjack/event_test.go +26 -0
- data/src/flapjack/transport.go +49 -0
- data/src/flapjack/transport_test.go +15 -0
- metadata +9 -2
data/libexec/oneoff.go
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
package main
|
2
|
+
|
3
|
+
import (
|
4
|
+
"os"
|
5
|
+
"fmt"
|
6
|
+
"time"
|
7
|
+
"strings"
|
8
|
+
"flapjack"
|
9
|
+
"gopkg.in/alecthomas/kingpin.v1"
|
10
|
+
"encoding/json"
|
11
|
+
)
|
12
|
+
|
13
|
+
var (
|
14
|
+
entity = kingpin.Arg("entity", "Entity name").Required().String()
|
15
|
+
check = kingpin.Arg("check", "Check name").Required().String()
|
16
|
+
state = kingpin.Arg("state", "Current state").Required().String()
|
17
|
+
summary = kingpin.Arg("summary", "Summary of event").Required().String()
|
18
|
+
debug = kingpin.Flag("debug", "Enable verbose output (default false)").Bool()
|
19
|
+
server = kingpin.Flag("server", "Redis server to connect to (default localhost:6380)").Default("localhost:6380").String()
|
20
|
+
database = kingpin.Flag("database", "Redis database to connect to (default 0)").Int()
|
21
|
+
)
|
22
|
+
|
23
|
+
func main() {
|
24
|
+
kingpin.Version("0.0.1")
|
25
|
+
kingpin.Parse()
|
26
|
+
|
27
|
+
if *debug {
|
28
|
+
fmt.Println("Entity:", *entity)
|
29
|
+
fmt.Println("Check:", *check)
|
30
|
+
fmt.Println("State:", *state)
|
31
|
+
fmt.Println("Summary:", *summary)
|
32
|
+
fmt.Println("Debug:", *debug)
|
33
|
+
fmt.Println("Server:", *server)
|
34
|
+
fmt.Println("Database:", *database)
|
35
|
+
}
|
36
|
+
|
37
|
+
addr := strings.Split(*server, ":")
|
38
|
+
if len(addr) != 2 {
|
39
|
+
fmt.Println("Error: invalid address specified:", *server)
|
40
|
+
fmt.Println("Should be in format `addr:port` (e.g. 127.0.0.1:6380)")
|
41
|
+
os.Exit(1)
|
42
|
+
}
|
43
|
+
|
44
|
+
event := flapjack.Event{
|
45
|
+
Entity: *entity,
|
46
|
+
Check: *check,
|
47
|
+
Type: "service",
|
48
|
+
State: *state,
|
49
|
+
Summary: *summary,
|
50
|
+
Time: time.Now().Unix(),
|
51
|
+
}
|
52
|
+
|
53
|
+
if *debug {
|
54
|
+
data, _ := json.Marshal(event)
|
55
|
+
fmt.Printf("Event data: %s\n", data)
|
56
|
+
}
|
57
|
+
|
58
|
+
transport, err := flapjack.Dial(*server, *database)
|
59
|
+
if *debug {
|
60
|
+
fmt.Printf("Transport: %+v\n", transport)
|
61
|
+
}
|
62
|
+
if err != nil {
|
63
|
+
fmt.Println("Error: couldn't connect to Redis:", err)
|
64
|
+
os.Exit(1)
|
65
|
+
}
|
66
|
+
|
67
|
+
reply, err := transport.Send(event)
|
68
|
+
if *debug {
|
69
|
+
fmt.Println("Reply from Redis:", reply)
|
70
|
+
}
|
71
|
+
if err != nil {
|
72
|
+
fmt.Println("Error: couldn't send event:", err)
|
73
|
+
os.Exit(1)
|
74
|
+
}
|
75
|
+
}
|
@@ -11,7 +11,7 @@ describe Flapjack::Data::Entity, :redis => true do
|
|
11
11
|
|
12
12
|
expect(entity).not_to be_nil
|
13
13
|
expect(entity.name).to eq(name)
|
14
|
-
expect(@redis.get("entity_id:#{name}")).to
|
14
|
+
expect(@redis.get("entity_id:#{name}")).to match(/^\S+$/)
|
15
15
|
end
|
16
16
|
|
17
17
|
it "adds a registered contact with an entity" do
|
@@ -3,13 +3,14 @@ require 'flapjack/gateways/jabber'
|
|
3
3
|
|
4
4
|
describe Flapjack::Gateways::Jabber, :logger => true do
|
5
5
|
|
6
|
-
let(:config) { {'queue'
|
7
|
-
'server'
|
8
|
-
'port'
|
9
|
-
'jabberid'
|
10
|
-
'password'
|
11
|
-
'alias'
|
12
|
-
'
|
6
|
+
let(:config) { {'queue' => 'jabber_notifications',
|
7
|
+
'server' => 'example.com',
|
8
|
+
'port' => '5222',
|
9
|
+
'jabberid' => 'flapjack@example.com',
|
10
|
+
'password' => 'password',
|
11
|
+
'alias' => 'flapjack',
|
12
|
+
'identifiers' => ['@flapjack'],
|
13
|
+
'rooms' => ['flapjacktest@conference.example.com']
|
13
14
|
}
|
14
15
|
}
|
15
16
|
|
@@ -26,10 +27,11 @@ describe Flapjack::Gateways::Jabber, :logger => true do
|
|
26
27
|
expect(fj).to receive(:register_handler).with(:ready).and_yield(stanza)
|
27
28
|
expect(fj).to receive(:on_ready).with(stanza)
|
28
29
|
|
29
|
-
|
30
|
+
body_matchers = [{:body => /^@flapjack[:\s]/}, {:body => /^flapjack[:\s]/}]
|
31
|
+
expect(fj).to receive(:register_handler).with(:message, :groupchat?, body_matchers).and_yield(stanza)
|
30
32
|
expect(fj).to receive(:on_groupchat).with(stanza)
|
31
33
|
|
32
|
-
expect(fj).to receive(:register_handler).with(:message, :chat
|
34
|
+
expect(fj).to receive(:register_handler).with(:message, :chat?, :body).and_yield(stanza)
|
33
35
|
expect(fj).to receive(:on_chat).with(stanza)
|
34
36
|
|
35
37
|
expect(fj).to receive(:register_handler).with(:disconnected).and_yield(stanza)
|
@@ -52,9 +54,8 @@ describe Flapjack::Gateways::Jabber, :logger => true do
|
|
52
54
|
end
|
53
55
|
|
54
56
|
it "receives an acknowledgement message" do
|
55
|
-
expect(stanza).to receive(:body).and_return('flapjack: ACKID 1f8ac10f fixing now duration: 90m')
|
57
|
+
expect(stanza).to receive(:body).twice.and_return('flapjack: ACKID 1f8ac10f fixing now duration: 90m')
|
56
58
|
from = double('from')
|
57
|
-
expect(from).to receive(:resource).and_return('sender')
|
58
59
|
expect(from).to receive(:stripped).and_return('sender')
|
59
60
|
expect(stanza).to receive(:from).and_return(from)
|
60
61
|
|
@@ -85,12 +86,11 @@ describe Flapjack::Gateways::Jabber, :logger => true do
|
|
85
86
|
end
|
86
87
|
|
87
88
|
it "strips XML tags from the received message" do
|
88
|
-
expect(stanza).to receive(:body).
|
89
|
+
expect(stanza).to receive(:body).twice.
|
89
90
|
and_return('flapjack: tell me about <span style="text-decoration: underline;">' +
|
90
91
|
'<a href="http://example.org/">example.org</a></span>')
|
91
92
|
|
92
93
|
from = double('from')
|
93
|
-
expect(from).to receive(:resource).and_return('sender')
|
94
94
|
expect(from).to receive(:stripped).and_return('sender')
|
95
95
|
expect(stanza).to receive(:from).and_return(from)
|
96
96
|
|
@@ -119,11 +119,10 @@ describe Flapjack::Gateways::Jabber, :logger => true do
|
|
119
119
|
end
|
120
120
|
|
121
121
|
it "handles a message with a newline in it" do
|
122
|
-
expect(stanza).to receive(:body).
|
122
|
+
expect(stanza).to receive(:body).twice.
|
123
123
|
and_return("flapjack: tell me about \nexample.com")
|
124
124
|
|
125
125
|
from = double('from')
|
126
|
-
expect(from).to receive(:resource).and_return('sender')
|
127
126
|
expect(from).to receive(:stripped).and_return('sender')
|
128
127
|
expect(stanza).to receive(:from).and_return(from)
|
129
128
|
|
@@ -152,9 +151,8 @@ describe Flapjack::Gateways::Jabber, :logger => true do
|
|
152
151
|
end
|
153
152
|
|
154
153
|
it "receives a message it doesn't understand" do
|
155
|
-
expect(stanza).to receive(:body).
|
154
|
+
expect(stanza).to receive(:body).twice.and_return('flapjack: hello!')
|
156
155
|
from = double('from')
|
157
|
-
expect(from).to receive(:resource).and_return('sender')
|
158
156
|
expect(from).to receive(:stripped).and_return('sender')
|
159
157
|
expect(stanza).to receive(:from).and_return(from)
|
160
158
|
|
@@ -14,6 +14,63 @@ describe 'Flapjack::Gateways::JSONAPI::CheckMethods', :sinatra => true, :logger
|
|
14
14
|
|
15
15
|
let(:check_presenter) { double(Flapjack::Gateways::JSONAPI::CheckPresenter) }
|
16
16
|
|
17
|
+
let(:check_data) { {'entity_name' => 'www.example.com', 'name' => 'PING' } }
|
18
|
+
|
19
|
+
it "retrieves all checks" do
|
20
|
+
expect(entity).to receive(:id).and_return('23')
|
21
|
+
|
22
|
+
expect(entity_check).to receive(:entity).and_return(entity)
|
23
|
+
expect(entity_check).to receive(:key).twice.and_return('PING')
|
24
|
+
expect(entity_check).to receive(:to_jsonapi).and_return(check_data.to_json)
|
25
|
+
expect(Flapjack::Data::EntityCheck).to receive(:find_all).with(:redis => redis).
|
26
|
+
and_return([entity_check])
|
27
|
+
|
28
|
+
aget '/checks'
|
29
|
+
expect(last_response).to be_ok
|
30
|
+
expect(last_response.body).to eq({:checks => [check_data]}.to_json)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "retrieves one check" do
|
34
|
+
expect(entity).to receive(:id).and_return('23')
|
35
|
+
|
36
|
+
expect(entity_check).to receive(:entity).and_return(entity)
|
37
|
+
expect(entity_check).to receive(:key).twice.and_return('PING')
|
38
|
+
expect(entity_check).to receive(:to_jsonapi).and_return(check_data.to_json)
|
39
|
+
expect(Flapjack::Data::EntityCheck).to receive(:for_event_id).
|
40
|
+
with('www.example.com:PING', :logger => @logger, :redis => redis).
|
41
|
+
and_return(entity_check)
|
42
|
+
|
43
|
+
aget '/checks/www.example.com:PING'
|
44
|
+
expect(last_response).to be_ok
|
45
|
+
expect(last_response.body).to eq({:checks => [check_data]}.to_json)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "retrieves several entities" do
|
49
|
+
entity_check_2 = double(Flapjack::Data::EntityCheck)
|
50
|
+
check_data_2 = {'name' => 'SSH',
|
51
|
+
'entity_name' => 'www.example.com'}
|
52
|
+
|
53
|
+
expect(entity).to receive(:id).twice.and_return('23')
|
54
|
+
|
55
|
+
expect(entity_check).to receive(:entity).and_return(entity)
|
56
|
+
expect(entity_check).to receive(:key).twice.and_return('PING')
|
57
|
+
expect(entity_check).to receive(:to_jsonapi).and_return(check_data.to_json)
|
58
|
+
expect(Flapjack::Data::EntityCheck).to receive(:for_event_id).
|
59
|
+
with('www.example.com:PING', :logger => @logger, :redis => redis).
|
60
|
+
and_return(entity_check)
|
61
|
+
|
62
|
+
expect(entity_check_2).to receive(:entity).and_return(entity)
|
63
|
+
expect(entity_check_2).to receive(:key).twice.and_return('SSH')
|
64
|
+
expect(entity_check_2).to receive(:to_jsonapi).and_return(check_data_2.to_json)
|
65
|
+
expect(Flapjack::Data::EntityCheck).to receive(:for_event_id).
|
66
|
+
with('www.example.com:SSH', :logger => @logger, :redis => redis).
|
67
|
+
and_return(entity_check_2)
|
68
|
+
|
69
|
+
aget '/checks/www.example.com:PING,www.example.com:SSH'
|
70
|
+
expect(last_response).to be_ok
|
71
|
+
expect(last_response.body).to eq({:checks => [check_data, check_data_2]}.to_json)
|
72
|
+
end
|
73
|
+
|
17
74
|
it 'disables a check' do
|
18
75
|
expect(Flapjack::Data::Entity).to receive(:find_by_name).
|
19
76
|
with(entity_name, :redis => redis).and_return(entity)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
package flapjack
|
2
|
+
|
3
|
+
import "errors"
|
4
|
+
|
5
|
+
// Event is a basic representation of a Flapjack event.
|
6
|
+
// Find more at https://github.com/flapjack/flapjack/wiki/DATA_STRUCTURES
|
7
|
+
type Event struct {
|
8
|
+
Entity string `json:"entity"`
|
9
|
+
Check string `json:"check"`
|
10
|
+
Type string `json:"type"`
|
11
|
+
State string `json:"state"`
|
12
|
+
Summary string `json:"summary"`
|
13
|
+
Time int64 `json:"time"`
|
14
|
+
}
|
15
|
+
|
16
|
+
// IsValid performs basic validations on the event data.
|
17
|
+
func (e Event) IsValid() (error) {
|
18
|
+
// FIXME(auxesis): provide validation errors for each failure
|
19
|
+
if len(e.Entity) == 0 { return errors.New("no entity") }
|
20
|
+
if len(e.Check) == 0 { return errors.New("no check") }
|
21
|
+
if len(e.State) == 0 { return errors.New("no state") }
|
22
|
+
if len(e.Summary) == 0 { return errors.New("no summary") }
|
23
|
+
return nil
|
24
|
+
}
|
25
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
package flapjack
|
2
|
+
|
3
|
+
import "testing"
|
4
|
+
|
5
|
+
func TestValidationFails(t *testing.T) {
|
6
|
+
event := Event{}
|
7
|
+
err := event.IsValid()
|
8
|
+
|
9
|
+
if err == nil {
|
10
|
+
t.Error("Expected validation to fail.")
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
func TestValidationPasses(t *testing.T) {
|
15
|
+
event := Event{
|
16
|
+
Entity: "hello",
|
17
|
+
Check: "world",
|
18
|
+
State: "ok",
|
19
|
+
Summary: "hello world",
|
20
|
+
}
|
21
|
+
err := event.IsValid()
|
22
|
+
|
23
|
+
if err != nil {
|
24
|
+
t.Error("Expected validation to pass, got:", err)
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
package flapjack
|
2
|
+
|
3
|
+
import (
|
4
|
+
"encoding/json"
|
5
|
+
"github.com/garyburd/redigo/redis"
|
6
|
+
)
|
7
|
+
|
8
|
+
// Transport is a representation of a Redis connection.
|
9
|
+
type Transport struct {
|
10
|
+
Address string
|
11
|
+
Database int
|
12
|
+
Connection redis.Conn
|
13
|
+
}
|
14
|
+
|
15
|
+
// Dial establishes a connection to Redis, wrapped in a Transport.
|
16
|
+
func Dial(address string, database int) (Transport,error) {
|
17
|
+
// Connect to Redis
|
18
|
+
conn, err := redis.Dial("tcp", address)
|
19
|
+
if err != nil {
|
20
|
+
return Transport{}, err
|
21
|
+
}
|
22
|
+
|
23
|
+
// Switch database
|
24
|
+
conn.Do("SELECT", database)
|
25
|
+
|
26
|
+
transport := Transport{
|
27
|
+
Address: address,
|
28
|
+
Database: database,
|
29
|
+
Connection: conn,
|
30
|
+
}
|
31
|
+
return transport, nil
|
32
|
+
}
|
33
|
+
|
34
|
+
// Send takes an event and sends it over a transport.
|
35
|
+
func (t Transport) Send(event Event) (interface{}, error) {
|
36
|
+
err := event.IsValid()
|
37
|
+
if err == nil {
|
38
|
+
data, _ := json.Marshal(event)
|
39
|
+
reply, err := t.Connection.Do("LPUSH", "events", data)
|
40
|
+
if err != nil {
|
41
|
+
return nil, err
|
42
|
+
}
|
43
|
+
|
44
|
+
return reply, nil
|
45
|
+
} else {
|
46
|
+
return nil, err
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
package flapjack
|
2
|
+
|
3
|
+
import "testing"
|
4
|
+
|
5
|
+
func TestDialFails(t *testing.T) {
|
6
|
+
address := "localhost:55555" // non-existent Redis server
|
7
|
+
database := 0
|
8
|
+
_, err := Dial(address, database)
|
9
|
+
|
10
|
+
if err == nil {
|
11
|
+
t.Error("Dial should fail")
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
// TODO(auxesis): add test for sending and receiving Events
|
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.
|
4
|
+
version: 1.0.0rc2
|
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-
|
13
|
+
date: 2014-07-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: dante
|
@@ -356,6 +356,7 @@ files:
|
|
356
356
|
- README.md
|
357
357
|
- Rakefile
|
358
358
|
- bin/flapjack
|
359
|
+
- build.sh
|
359
360
|
- dist/etc/init.d/flapjack
|
360
361
|
- dist/etc/init.d/flapjack-nagios-receiver
|
361
362
|
- dist/etc/init.d/flapper
|
@@ -501,6 +502,8 @@ files:
|
|
501
502
|
- lib/flapjack/redis_pool.rb
|
502
503
|
- lib/flapjack/utility.rb
|
503
504
|
- lib/flapjack/version.rb
|
505
|
+
- libexec/httpbroker.go
|
506
|
+
- libexec/oneoff.go
|
504
507
|
- log/.gitkeep
|
505
508
|
- spec/lib/flapjack/coordinator_spec.rb
|
506
509
|
- spec/lib/flapjack/data/contact_spec.rb
|
@@ -546,6 +549,10 @@ files:
|
|
546
549
|
- spec/support/jsonapi_helper.rb
|
547
550
|
- spec/support/profile_all_formatter.rb
|
548
551
|
- spec/support/uncolored_doc_formatter.rb
|
552
|
+
- src/flapjack/event.go
|
553
|
+
- src/flapjack/event_test.go
|
554
|
+
- src/flapjack/transport.go
|
555
|
+
- src/flapjack/transport_test.go
|
549
556
|
- tasks/benchmarks.rake
|
550
557
|
- tasks/events.rake
|
551
558
|
- tasks/profile.rake
|