kingkong 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -0
- data/Guardfile +1 -1
- data/README.md +15 -18
- data/bin/kingkong-munin +4 -1
- data/lib/kingkong.rb +2 -0
- data/lib/kingkong/aggregator.rb +38 -23
- data/lib/kingkong/ping.rb +51 -20
- data/lib/kingkong/pinger.rb +15 -10
- data/lib/kingkong/reporting.rb +37 -0
- data/lib/kingkong/runner.rb +130 -0
- data/lib/kingkong/version.rb +2 -2
- data/spec/lib/kingkong/aggregator_spec.rb +32 -0
- data/spec/lib/kingkong/ping_spec.rb +142 -0
- data/spec/lib/kingkong/pinger_spec.rb +20 -10
- data/spec/lib/kingkong/runner_spec.rb +70 -0
- data/spec/spec_helper.rb +4 -0
- metadata +15 -5
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
guard 'rspec', :cli => '--color --format nested', :version => 2 do
|
1
|
+
guard 'rspec', :cli => '--color --format nested --debug', :version => 2 do
|
2
2
|
watch(%r{^spec/.+_spec\.rb$})
|
3
3
|
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
4
4
|
watch('spec/spec_helper.rb') { "spec/" }
|
data/README.md
CHANGED
@@ -8,8 +8,8 @@
|
|
8
8
|
| /.----/ O O \----.\ |
|
9
9
|
\/ | | \/
|
10
10
|
| |
|
11
|
-
| |
|
12
|
-
| |
|
11
|
+
| | Full stack health checks for your networked apps!
|
12
|
+
| | (Kinda like Pingdom, but way deeper and free)
|
13
13
|
_\ -.,_____,.- /_
|
14
14
|
,.-" "-.,_________,.-" "-.,
|
15
15
|
/ | | \
|
@@ -28,26 +28,23 @@
|
|
28
28
|
| | | |
|
29
29
|
\__|__|__|__/ \__|__|__|__/ \_|__|__/
|
30
30
|
|
31
|
-
KingKong makes it easy to build full-stack ping-pong checks. You might need this to check and graph out the response time on your website, Twitter application, SMS gateway, or whatever else you'd connect to a network.
|
31
|
+
KingKong makes it easy to build full-stack ping-pong health checks so you can keep an eye on crucial input/outputs and make sure things stay nice and fast. You might need this to check and graph out the response time on your website, Twitter application, SMS gateway, or whatever else you'd connect to a network.
|
32
32
|
|
33
33
|
When its done, it will look something like this:
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
|
35
|
+
require 'kingkong'
|
36
|
+
require 'em-http-request'
|
37
|
+
|
38
|
+
KingKing::Runner.start {
|
39
|
+
socket '/tmp/king_kong.socket' # Check this socket with Munin and make a graph!
|
40
|
+
|
38
41
|
ping(:google).every(3).seconds do |ping|
|
39
|
-
google =
|
40
|
-
|
41
|
-
ping.
|
42
|
-
|
43
|
-
}
|
44
|
-
|
45
|
-
ping.start_time # Start the clock!
|
46
|
-
google.callback { # This triggers the request
|
47
|
-
ping.end_time # And this stops the clock!
|
48
|
-
}
|
42
|
+
google = EventMachine::HttpRequest.new('http://google.com/')
|
43
|
+
google.callback { ping.stop }
|
44
|
+
google.errback { ping.fail }
|
45
|
+
ping.start and google.get
|
49
46
|
end
|
50
|
-
|
47
|
+
|
51
48
|
ping(:twitter).every(10).seconds do |ping|
|
52
49
|
# Wire up your own thing in here that tweets
|
53
50
|
# .. and when you pick that up, end the pong!
|
data/bin/kingkong-munin
CHANGED
@@ -4,7 +4,7 @@ require 'socket'
|
|
4
4
|
require 'yaml'
|
5
5
|
|
6
6
|
puts case ARGV.first
|
7
|
-
when /config/
|
7
|
+
when /config/
|
8
8
|
%(graph_title Result latency
|
9
9
|
graph_category App
|
10
10
|
graph_vlabel load
|
@@ -13,3 +13,6 @@ else
|
|
13
13
|
stats = YAML.load UNIXSocket.new("/tmp/kingkong.socket").gets("\r\n")
|
14
14
|
%(load.value #{stats[:avg]})
|
15
15
|
end
|
16
|
+
|
17
|
+
# TODO - Message throughput
|
18
|
+
# TODO - Message latency (min/max/avg)
|
data/lib/kingkong.rb
CHANGED
@@ -3,9 +3,11 @@ require "kingkong/version"
|
|
3
3
|
module KingKong
|
4
4
|
autoload :Ping, 'kingkong/ping'
|
5
5
|
autoload :Pinger, 'kingkong/pinger'
|
6
|
+
autoload :Runner, 'kingkong/runner'
|
6
7
|
autoload :Server, 'kingkong/server'
|
7
8
|
autoload :Logging, 'kingkong/logging'
|
8
9
|
autoload :Aggregator, 'kingkong/aggregator'
|
10
|
+
autoload :Reporting, 'kingkong/reporting'
|
9
11
|
|
10
12
|
# Default logger for KingKong.
|
11
13
|
def self.logger
|
data/lib/kingkong/aggregator.rb
CHANGED
@@ -1,55 +1,70 @@
|
|
1
1
|
module KingKong
|
2
|
-
# Processes and aggregates
|
2
|
+
# Processes and aggregates samples for reporting. These stats will probably
|
3
3
|
# be accessed throug the KingKong::Server UNIX socket.
|
4
4
|
class Aggregator
|
5
|
-
attr_reader :sum, :
|
5
|
+
attr_reader :sum, :timed_out_count, :count, :started_at, :min, :max
|
6
6
|
|
7
|
-
def initialize
|
8
|
-
@max_count = max_count
|
7
|
+
def initialize
|
9
8
|
reset
|
10
9
|
end
|
11
10
|
|
12
11
|
# Accept a ping message, keep a running total, and make sure we stay within the
|
13
12
|
# size of the number of samples that we want to keep around.
|
14
|
-
def process(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
13
|
+
def process(*pings)
|
14
|
+
pings.each do |ping|
|
15
|
+
@count+=1
|
16
|
+
case ping.status
|
17
|
+
when :timed_out
|
18
|
+
@timed_out_count+=1
|
19
|
+
else
|
20
|
+
# This happens on our first ping before we have a min/max
|
21
|
+
@min = @max = ping.latency if @min.nil? and @max.nil?
|
22
|
+
# Cool! Lets do some math
|
23
|
+
@min = ping.latency if ping.latency < @min
|
24
|
+
@max = ping.latency if ping.latency > @max
|
25
|
+
# We need to sum latency to calculate avgs
|
26
|
+
@sum += ping.latency
|
27
|
+
end
|
22
28
|
end
|
23
29
|
end
|
24
30
|
|
25
|
-
#
|
31
|
+
# avg latency of samples
|
26
32
|
def avg
|
27
33
|
count.zero? ? 0 : sum / count
|
28
34
|
end
|
29
35
|
|
30
|
-
#
|
31
|
-
def
|
32
|
-
|
36
|
+
# What percentage of requests are timed out?
|
37
|
+
def timed_out_percentage
|
38
|
+
timed_out_count > 0 ? timed_out_count / count : 0
|
33
39
|
end
|
34
40
|
|
35
41
|
# Basic statistical summary of ping latency
|
36
42
|
def to_hash
|
37
43
|
{
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
44
|
+
'started_at' => started_at,
|
45
|
+
'avg' => avg,
|
46
|
+
'sum' => sum,
|
47
|
+
'min' => min,
|
48
|
+
'max' => max,
|
49
|
+
'count' => count,
|
50
|
+
'timed_out_count' => timed_out_count,
|
51
|
+
'successful_count' => count - timed_out_count
|
42
52
|
}
|
43
53
|
end
|
44
54
|
|
55
|
+
# Print out a string for this that is suitable for flushing out to a socket
|
45
56
|
def to_s
|
46
57
|
to_hash.to_yaml
|
47
58
|
end
|
48
59
|
|
49
|
-
# Reset all of the counts to 0 and
|
60
|
+
# Reset all of the counts to 0 and start sampling it all again!
|
50
61
|
def reset
|
51
|
-
@sum
|
52
|
-
@
|
62
|
+
@sum = 0.0
|
63
|
+
@min = nil
|
64
|
+
@max = nil
|
65
|
+
@count = 0
|
66
|
+
@timed_out_count = 0
|
67
|
+
@samples = Array.new
|
53
68
|
@started_at = Time.now
|
54
69
|
end
|
55
70
|
end
|
data/lib/kingkong/ping.rb
CHANGED
@@ -3,9 +3,11 @@ require 'eventmachine'
|
|
3
3
|
module KingKong
|
4
4
|
# Encaspulates and calculates latency.
|
5
5
|
class Ping
|
6
|
-
|
6
|
+
NotStartedError= Class.new(RuntimeError)
|
7
7
|
|
8
|
-
|
8
|
+
attr_accessor :end_time, :start_time, :ttl
|
9
|
+
|
10
|
+
def initialize(ttl=self.class.default_ttl,sequencer=self.class.sequencer)
|
9
11
|
# Time out is when the ping should give up!
|
10
12
|
@ttl, @sequencer = ttl, sequencer
|
11
13
|
end
|
@@ -15,48 +17,77 @@ module KingKong
|
|
15
17
|
@id ||= @sequencer.next
|
16
18
|
end
|
17
19
|
|
18
|
-
#
|
19
|
-
def
|
20
|
-
|
21
|
-
@start_time = current_time
|
22
|
-
end
|
23
|
-
@start_time
|
20
|
+
# Start the ping and set a start time
|
21
|
+
def start
|
22
|
+
@start_time ||= current_time
|
24
23
|
end
|
24
|
+
alias :ping :start
|
25
25
|
|
26
|
-
#
|
27
|
-
def
|
26
|
+
# Stop the ping and set a stop time
|
27
|
+
def stop
|
28
|
+
raise Ping::NotStartedError.new("The ping must be started to stop") unless @start_time
|
29
|
+
# We have a start time? Cool! Lets stop this thing then
|
28
30
|
@end_time ||= current_time
|
29
31
|
end
|
32
|
+
alias :pong :stop
|
30
33
|
|
31
34
|
# How long did it take to clear the message?
|
32
35
|
def latency
|
33
|
-
end_time - start_time if end_time
|
36
|
+
end_time.to_f - start_time.to_f if end_time
|
37
|
+
end
|
38
|
+
|
39
|
+
# Figure out the state of this ping.
|
40
|
+
def status
|
41
|
+
if !end_time and !start_time
|
42
|
+
:not_started
|
43
|
+
elsif start_time and !end_time and start_time + ttl < current_time
|
44
|
+
:timed_out
|
45
|
+
elsif start_time and !end_time
|
46
|
+
:active
|
47
|
+
elsif start_time and end_time
|
48
|
+
:completed
|
49
|
+
end
|
34
50
|
end
|
35
51
|
|
36
52
|
# Did this get ponged yet?
|
37
53
|
def completed?
|
38
|
-
|
54
|
+
status == :completed
|
39
55
|
end
|
40
56
|
|
41
57
|
# Is this ping still on its journy? Will it make it back! It hasn't yet...
|
42
58
|
def active?
|
43
|
-
|
59
|
+
status == :active
|
44
60
|
end
|
45
61
|
|
46
62
|
# Did we not receive a pong?
|
47
63
|
def timed_out?
|
48
|
-
|
64
|
+
status == :timed_out
|
49
65
|
end
|
50
66
|
|
51
|
-
#
|
52
|
-
def
|
53
|
-
|
67
|
+
# Nothing happened ye
|
68
|
+
def not_started?
|
69
|
+
status == :not_started
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
"Ping(#{id}, :#{status})"
|
54
74
|
end
|
55
75
|
|
56
76
|
# Generates ids for pings
|
57
77
|
def self.sequencer
|
58
78
|
@sequencer ||= Sequencer.new
|
59
79
|
end
|
80
|
+
|
81
|
+
# Default TTL. Override this method if you want a different default.
|
82
|
+
def self.default_ttl
|
83
|
+
30 # 30 seconds that is!
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
# Give us the current time in seconds
|
88
|
+
def current_time
|
89
|
+
Time.now
|
90
|
+
end
|
60
91
|
end
|
61
92
|
|
62
93
|
# Keeps track of the results heading in/out. We could just use
|
@@ -74,7 +105,7 @@ module KingKong
|
|
74
105
|
def next
|
75
106
|
key @count += 1
|
76
107
|
end
|
77
|
-
|
108
|
+
|
78
109
|
private
|
79
110
|
# Spits out a key and run for a result
|
80
111
|
def key(count=count)
|
@@ -87,13 +118,13 @@ module KingKong
|
|
87
118
|
include EventMachine::Deferrable
|
88
119
|
|
89
120
|
# Setup a ttl for the ping so that it will timeout after ttl seconds
|
90
|
-
def
|
121
|
+
def start
|
91
122
|
timeout(ttl) # Setup an em TTL on this thing so it fails if the thing isn't called
|
92
123
|
super
|
93
124
|
end
|
94
125
|
|
95
126
|
# Succeed a block passed into the ping and mark the ping as suceeded
|
96
|
-
def
|
127
|
+
def stop(&block)
|
97
128
|
callback(&block) if block_given?
|
98
129
|
super
|
99
130
|
succeed self
|
data/lib/kingkong/pinger.rb
CHANGED
@@ -4,17 +4,19 @@ module KingKong
|
|
4
4
|
class Pinger
|
5
5
|
include Logging
|
6
6
|
|
7
|
-
|
7
|
+
attr_reader :wait
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
@
|
9
|
+
def initialize(wait=5,&block)
|
10
|
+
@wait = wait
|
11
|
+
@block = block if block_given?
|
12
|
+
self
|
11
13
|
end
|
12
14
|
|
13
15
|
# Starts the pinger
|
14
|
-
def start
|
16
|
+
def start
|
15
17
|
logger.debug "Starting pinger #{self}"
|
16
|
-
@timer = EventMachine::PeriodicTimer.new(
|
17
|
-
ping
|
18
|
+
@timer = EventMachine::PeriodicTimer.new(wait){ ping }
|
19
|
+
ping # Start a ping right away, the timer above will fire later.
|
18
20
|
end
|
19
21
|
|
20
22
|
# Stop the pinger from executing
|
@@ -27,22 +29,25 @@ module KingKong
|
|
27
29
|
def aggregator
|
28
30
|
@aggregatore ||= Aggregator.new
|
29
31
|
end
|
30
|
-
|
32
|
+
|
31
33
|
private
|
32
34
|
# Add all of the instrumentation callbacks into the ping so we can aggregate it later
|
33
|
-
def ping
|
34
|
-
ping = Ping::Deferrable.new(
|
35
|
+
def ping
|
36
|
+
ping = Ping::Deferrable.new(Ping.default_ttl, sequencer)
|
37
|
+
|
35
38
|
# Register the aggregator to process the ping
|
36
39
|
ping.callback {
|
37
40
|
logger.debug "Ping #{ping} successful"
|
38
41
|
aggregator.process ping
|
39
42
|
}
|
43
|
+
|
40
44
|
ping.errback {
|
41
45
|
logger.debug "Ping #{ping} error (probably a timeout)"
|
42
46
|
aggregator.process ping
|
43
47
|
}
|
48
|
+
|
44
49
|
# Now pass the ping into the block so we can start/stop it
|
45
|
-
|
50
|
+
@block.call(ping, self)
|
46
51
|
end
|
47
52
|
|
48
53
|
# Setup a squencer that's unique specifically to this pinger
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module KingKong
|
4
|
+
# Provides access to the pinger via a Socket or TCP port so that other
|
5
|
+
# services, like Munin for example, can graph the data.
|
6
|
+
module Reporting
|
7
|
+
class Server < EventMachine::Connection
|
8
|
+
Terminator = "\n\n"
|
9
|
+
Host = '/tmp/king_kong.socket'
|
10
|
+
Port = nil
|
11
|
+
|
12
|
+
attr_accessor :aggregators
|
13
|
+
|
14
|
+
# Accept a collection of aggregators that we'll use to report our stats.
|
15
|
+
def initialize(*aggregators)
|
16
|
+
@aggregators = aggregators
|
17
|
+
end
|
18
|
+
|
19
|
+
# Dump out the stats and close down the connection
|
20
|
+
def post_init
|
21
|
+
begin
|
22
|
+
send_data "#{aggregators.each(&:to_s).join("\n")}#{Terminator}"
|
23
|
+
rescue => e
|
24
|
+
send_data "Exception! #{e}\n#{e.backtrace}"
|
25
|
+
ensure
|
26
|
+
close_connection
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# A nice short-cut for peeps who aren't familar with EM to fire up
|
31
|
+
# an Reporting server with an array of aggregators, host, and a port.
|
32
|
+
def self.start(aggregators, host=Socket::Host, port=Socket::Port)
|
33
|
+
EventMachine::start_server(host, port, self, aggregators)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module KingKong
|
2
|
+
# Configure multiple pingers to run
|
3
|
+
class Runner
|
4
|
+
include Logging
|
5
|
+
|
6
|
+
# Array of pingers that we're running
|
7
|
+
def ping(name)
|
8
|
+
pinger_configurations[name]
|
9
|
+
end
|
10
|
+
|
11
|
+
# Setup the socket that this thing will write stats out to
|
12
|
+
def socket(host=KingKong::Reporting::Server::Host, port=KingKong::Reporting::Server::Port)
|
13
|
+
@socket_host , @socket_port = host, port
|
14
|
+
end
|
15
|
+
|
16
|
+
# Start all of the pingers given the configurations
|
17
|
+
def start
|
18
|
+
start_socket
|
19
|
+
start_pingers
|
20
|
+
end
|
21
|
+
|
22
|
+
# Stop all of the pingers
|
23
|
+
def stop
|
24
|
+
pingers.each(&:stop)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Open up the configuration DSL for this puppy
|
28
|
+
def configure(&block)
|
29
|
+
block.arity > 1 ? block.call(self) : instance_eval(&block)
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
# Shortcut for creating a new class and configuring it
|
34
|
+
def self.configure(&block)
|
35
|
+
new.configure(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Shortcut for configuring and starting a KingKong runner
|
39
|
+
def self.run(&block)
|
40
|
+
ensure_running_reactor {
|
41
|
+
runner = configure(&block)
|
42
|
+
runner.start
|
43
|
+
}
|
44
|
+
runner # For chaining
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
# Fire up the reporting server if a socket (and port, optionally) are given
|
49
|
+
def start_socket
|
50
|
+
Reporting::Server.start(pingers.map(&:aggregator), @socket_host, @socket_port) if @socket_host
|
51
|
+
end
|
52
|
+
|
53
|
+
# Fire up all the pingers
|
54
|
+
def start_pingers
|
55
|
+
pingers.each(&:start)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Hang onto pinger configuration so that we can configure pinger instances and fire them
|
59
|
+
# off. We'll also be using this for writing reports.
|
60
|
+
def pinger_configurations
|
61
|
+
@pinger_configurations ||= Hash.new{|hash,val| hash[val] = DSL::Pinger.new }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create instances of pingers from the configurations that we setup in the runner
|
65
|
+
def pingers
|
66
|
+
@pingers ||= pinger_configurations.values.map(&:pinger)
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.ensure_running_reactor(&block)
|
70
|
+
EM.reactor_running? ? block.call : EM.run(&block)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Runner
|
75
|
+
module DSL
|
76
|
+
# Encapsulate the configuration of a bunch of pingers that we can run in one KingKong file
|
77
|
+
class Pinger
|
78
|
+
# Time units expressed in seconds
|
79
|
+
module Unit
|
80
|
+
Second = 1
|
81
|
+
Minute = 60
|
82
|
+
Hour = 60 * 60
|
83
|
+
end
|
84
|
+
|
85
|
+
attr_accessor :block
|
86
|
+
|
87
|
+
def every(quantity)
|
88
|
+
@quantity = quantity
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
# Configure duration as seconds
|
93
|
+
def seconds(&block)
|
94
|
+
unitize Unit::Second, &block
|
95
|
+
end
|
96
|
+
alias :second :seconds
|
97
|
+
|
98
|
+
# Configure the duration as minutes
|
99
|
+
def minutes(&block)
|
100
|
+
unitize Unit::Minute, &block
|
101
|
+
end
|
102
|
+
alias :minute :minutes
|
103
|
+
|
104
|
+
# Configure the ping duration as hours
|
105
|
+
def hours(&block)
|
106
|
+
unitize Unit::Hour, &block
|
107
|
+
end
|
108
|
+
alias :hour :hours
|
109
|
+
|
110
|
+
# The number of seconds the timer will wait between ticks. We'll pass this into EM
|
111
|
+
def duration
|
112
|
+
@units * @quantity
|
113
|
+
end
|
114
|
+
|
115
|
+
# Return a configured instance of a pinger that we can fire up and do stuff with
|
116
|
+
def pinger
|
117
|
+
KingKong::Pinger.new(duration, &@block)
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
# Figures out the number of seconds that we multply by the units
|
122
|
+
def unitize(units, &block)
|
123
|
+
@units = units
|
124
|
+
@block = block
|
125
|
+
self
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/kingkong/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module KingKong
|
2
|
-
VERSION = "0.0.
|
3
|
-
end
|
2
|
+
VERSION = "0.0.3"
|
3
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "rspec/mocks/standalone"
|
3
|
+
|
4
|
+
describe KingKong::Aggregator do
|
5
|
+
before(:all) do
|
6
|
+
@agg = KingKong::Aggregator.new
|
7
|
+
|
8
|
+
@pings = (1..3).map do |n|
|
9
|
+
ping = KingKong::Ping.new
|
10
|
+
ping.stub(:latency){ n.to_f }
|
11
|
+
ping
|
12
|
+
end
|
13
|
+
|
14
|
+
@agg.process *@pings
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should calculate avg" do
|
18
|
+
@agg.avg.should eql(2.0)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should calculate sum" do
|
22
|
+
@agg.sum.should eql(6.0)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should calculate max" do
|
26
|
+
@agg.max.should eql(3.0)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should calculate min" do
|
30
|
+
@agg.min.should eql(1.0)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'timecop'
|
3
|
+
|
4
|
+
describe KingKong::Ping do
|
5
|
+
it "should have default 30 second ttl" do
|
6
|
+
KingKong::Ping.new.ttl.should eql(KingKong::Ping.default_ttl)
|
7
|
+
end
|
8
|
+
|
9
|
+
context "active" do
|
10
|
+
before(:each) do
|
11
|
+
@ping = KingKong::Ping.new # Open up a pinger
|
12
|
+
Timecop.freeze(@now = Time.now) # Freeze time
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:each) do
|
16
|
+
Timecop.return # Turn Timecop off
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should start" do
|
20
|
+
@ping.start.should eql(@now)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should be active" do
|
24
|
+
@ping.start
|
25
|
+
@ping.should be_active
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not be timed out" do
|
29
|
+
@ping.start
|
30
|
+
@ping.should_not be_timed_out
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should not be completed" do
|
34
|
+
@ping.start
|
35
|
+
@ping.should_not be_completed
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should raise exception if stopped but not started" do
|
39
|
+
lambda{
|
40
|
+
@ping.stop
|
41
|
+
}.should raise_exception(KingKong::Ping::NotStartedError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "timed out" do
|
46
|
+
before(:all) do
|
47
|
+
@ping = KingKong::Ping.new
|
48
|
+
@span = KingKong::Ping.default_ttl + 1
|
49
|
+
|
50
|
+
Timecop.freeze(@now = Time.now) # Freeze now
|
51
|
+
@ping.start # Start the ping
|
52
|
+
Timecop.freeze(@then = @now + @span) # Move to a timed-out state, and freeze again
|
53
|
+
end
|
54
|
+
|
55
|
+
after(:all) do
|
56
|
+
Timecop.return # Turn Timecop off
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should not have end time" do
|
60
|
+
@ping.end_time.should be_nil
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be timed out" do
|
64
|
+
@ping.should be_timed_out
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not be active" do
|
68
|
+
@ping.should_not be_active
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should not be completed" do
|
72
|
+
@ping.should_not be_completed
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "success" do
|
77
|
+
before(:all) do
|
78
|
+
@ping = KingKong::Ping.new
|
79
|
+
@span = KingKong::Ping.default_ttl - 1 # 1 under the timeout
|
80
|
+
|
81
|
+
Timecop.freeze(@now = Time.now) # Freeze time
|
82
|
+
@ping.start # Start the ping
|
83
|
+
Timecop.freeze(@then = @now + @span) # Move forward into time whene the ping is successful
|
84
|
+
@ping.stop # End the ping
|
85
|
+
end
|
86
|
+
|
87
|
+
after(:all) do
|
88
|
+
Timecop.return # Turn Timecop off
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should stop" do
|
92
|
+
@ping.stop.should eql(@then)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should have end time" do
|
96
|
+
@ping.end_time.should eql(@then)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should calculate latency" do
|
100
|
+
@ping.latency.should eql(@then - @now)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should not be active" do
|
104
|
+
@ping.should_not be_active
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should be completed" do
|
108
|
+
@ping.should be_completed
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe KingKong::Ping::Deferrable do
|
114
|
+
include EM::Ventually
|
115
|
+
|
116
|
+
it "should time out" do
|
117
|
+
@ping = KingKong::Ping::Deferrable.new(0.1)
|
118
|
+
@ping.start
|
119
|
+
@ping.errback{ @err_status = :timed_out }
|
120
|
+
ly(:timed_out){ @err_status }
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should callback with a ping if successful" do
|
124
|
+
@ping = KingKong::Ping::Deferrable.new
|
125
|
+
@ping.start
|
126
|
+
@ping.stop
|
127
|
+
@ping.callback{ |ping| @response = ping }
|
128
|
+
ly{ KingKong::Ping }.test{|type| @response.kind_of? type }
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe KingKong::Ping::Sequencer do
|
133
|
+
before(:all) do
|
134
|
+
@sequencer = KingKong::Ping::Sequencer.new(0)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should sequence" do
|
138
|
+
count, run = @sequencer.next.split(':')
|
139
|
+
count.should eql("1")
|
140
|
+
run.should eql("0")
|
141
|
+
end
|
142
|
+
end
|
@@ -1,19 +1,29 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe KingKong::Pinger do
|
4
|
-
|
4
|
+
include EM::Ventually
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
before(:all) do
|
7
|
+
@@pinged = false
|
8
|
+
@pinger = KingKong::Pinger.new(0.1) do |ping, pinger|
|
9
|
+
@@ping = ping
|
10
|
+
ping.start
|
11
|
+
ping.stop
|
12
|
+
pinger.stop
|
8
13
|
end
|
14
|
+
end
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
it "should ping" do
|
17
|
+
@pinger.start
|
18
|
+
ly(:completed) { @@ping.status }
|
19
|
+
end
|
13
20
|
|
14
|
-
|
15
|
-
|
16
|
-
end
|
21
|
+
it "should stop"
|
22
|
+
it "should start"
|
17
23
|
|
18
|
-
|
24
|
+
context "ping aggregation" do
|
25
|
+
it "should process timed-out"
|
26
|
+
it "should process successful"
|
27
|
+
it "should process errors"
|
28
|
+
end
|
19
29
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
describe KingKong::Runner do
|
5
|
+
include EM::Ventually
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@@google_pinged = @@twitter_pinged = false
|
9
|
+
|
10
|
+
@runner = KingKong::Runner.configure do |runner|
|
11
|
+
runner.socket '/tmp/king_kong.socket'
|
12
|
+
|
13
|
+
runner.ping(:google).every(0.1).seconds do
|
14
|
+
@@google_pinged = true
|
15
|
+
end
|
16
|
+
|
17
|
+
runner.ping(:twitter).every(2).seconds do
|
18
|
+
@@twitter_pinged = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should start pinging" do
|
24
|
+
@runner.start
|
25
|
+
ly(true) { @@google_pinged }
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should write data to socket" do
|
29
|
+
@runner.start
|
30
|
+
# puts UNIXSocket.new('/tmp/king_kong.socket').gets("\n\n")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe KingKong::Runner::DSL::Pinger do
|
35
|
+
# Lets kinda simulate this in our DSL
|
36
|
+
def ping(name)
|
37
|
+
KingKong::Runner::DSL::Pinger.new
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should accept minutes" do
|
41
|
+
config = ping(:google).every(3).minutes
|
42
|
+
config.duration.should eql(3 * 60)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should accept seconds" do
|
46
|
+
config = ping(:google).every(3).seconds
|
47
|
+
config.duration.should eql(3)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should accept hours" do
|
51
|
+
config = ping(:google).every(3).hours
|
52
|
+
config.duration.should eql(3 * 60 * 60)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should accept block" do
|
56
|
+
block = Proc.new {}
|
57
|
+
config = ping(:twitter).every(1).second &block
|
58
|
+
config.block.should eql(block)
|
59
|
+
end
|
60
|
+
|
61
|
+
context "pinger configuration" do
|
62
|
+
before(:each) do
|
63
|
+
@pinger = ping(:brad).every(2).hours.pinger
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should have wait" do
|
67
|
+
@pinger.wait.should eql(2 * KingKong::Runner::DSL::Pinger::Unit::Hour) # 2 hours
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kingkong
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-09-
|
12
|
+
date: 2011-09-13 00:00:00.000000000 -07:00
|
13
|
+
default_executable:
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: eventmachine
|
16
|
-
requirement: &
|
17
|
+
requirement: &70233419803860 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,7 +22,7 @@ dependencies:
|
|
21
22
|
version: '0'
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *70233419803860
|
25
26
|
description: Have you ever wanted to shoot a message throught Twitter, have your app
|
26
27
|
pick it up, do some work on it, and report how long it takes? KingKong makes it
|
27
28
|
slightly easier to do this with a DSL for writing custom pings and by providing
|
@@ -49,11 +50,17 @@ files:
|
|
49
50
|
- lib/kingkong/logging.rb
|
50
51
|
- lib/kingkong/ping.rb
|
51
52
|
- lib/kingkong/pinger.rb
|
53
|
+
- lib/kingkong/reporting.rb
|
54
|
+
- lib/kingkong/runner.rb
|
52
55
|
- lib/kingkong/server.rb
|
53
56
|
- lib/kingkong/version.rb
|
57
|
+
- spec/lib/kingkong/aggregator_spec.rb
|
58
|
+
- spec/lib/kingkong/ping_spec.rb
|
54
59
|
- spec/lib/kingkong/pinger_spec.rb
|
60
|
+
- spec/lib/kingkong/runner_spec.rb
|
55
61
|
- spec/lib/kingkong_spec.rb
|
56
62
|
- spec/spec_helper.rb
|
63
|
+
has_rdoc: true
|
57
64
|
homepage: ''
|
58
65
|
licenses: []
|
59
66
|
post_install_message:
|
@@ -74,11 +81,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
81
|
version: '0'
|
75
82
|
requirements: []
|
76
83
|
rubyforge_project: kingkong
|
77
|
-
rubygems_version: 1.
|
84
|
+
rubygems_version: 1.6.2
|
78
85
|
signing_key:
|
79
86
|
specification_version: 3
|
80
87
|
summary: Build complex network application health checks with Ruby and EventMachine
|
81
88
|
test_files:
|
89
|
+
- spec/lib/kingkong/aggregator_spec.rb
|
90
|
+
- spec/lib/kingkong/ping_spec.rb
|
82
91
|
- spec/lib/kingkong/pinger_spec.rb
|
92
|
+
- spec/lib/kingkong/runner_spec.rb
|
83
93
|
- spec/lib/kingkong_spec.rb
|
84
94
|
- spec/spec_helper.rb
|