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 CHANGED
@@ -12,4 +12,6 @@ group :test, :development do
12
12
  gem 'growl'
13
13
  gem 'rb-fsevent'
14
14
  gem 'em-ventually'
15
+ gem 'timecop'
16
+ gem 'ruby-debug19'
15
17
  end
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
- | | KingKong gets what KingKong wants!
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
- KingKing.monitor {
36
- socket '/tmp/kingkong.socket' # Munin can check this for stats
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 = http('http://www.google.com/').get
40
-
41
- ping.on_timeout {
42
- # ZOMG! Email the admin! Google is down!
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!
@@ -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)
@@ -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
@@ -1,55 +1,70 @@
1
1
  module KingKong
2
- # Processes and aggregates pings for reporting. These stats will probably
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, :max_count, :pings, :started_at
5
+ attr_reader :sum, :timed_out_count, :count, :started_at, :min, :max
6
6
 
7
- def initialize(max_count=10_000)
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(ping)
15
- @sum+=ping.latency
16
- pings.push ping
17
-
18
- # Take out the old ping if we're approaching our max sample size and substruct it
19
- # from the running latency sum
20
- if count > max_count and ping = pings.shift
21
- @sum-=ping.latency
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
- # Average latency of pings
31
+ # avg latency of samples
26
32
  def avg
27
33
  count.zero? ? 0 : sum / count
28
34
  end
29
35
 
30
- # Number of pings
31
- def count
32
- pings.size
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
- :avg => avg,
39
- :count => count,
40
- :sum => sum,
41
- :started_at => started_at
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 empty all of the pings
60
+ # Reset all of the counts to 0 and start sampling it all again!
50
61
  def reset
51
- @sum = 0.0
52
- @pings = Array.new
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
@@ -3,9 +3,11 @@ require 'eventmachine'
3
3
  module KingKong
4
4
  # Encaspulates and calculates latency.
5
5
  class Ping
6
- attr_accessor :end_time, :ttl
6
+ NotStartedError= Class.new(RuntimeError)
7
7
 
8
- def initialize(ttl=30,sequencer=self.class.sequencer)
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
- # When did the ping start? This is also what starts the time for this thing
19
- def start_time
20
- unless @start_time
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
- # Set the end time
27
- def pong
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
- !!end_time
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
- !timed_out? or !completed?
59
+ status == :active
44
60
  end
45
61
 
46
62
  # Did we not receive a pong?
47
63
  def timed_out?
48
- start_time + ttl < current_time and not end_time
64
+ status == :timed_out
49
65
  end
50
66
 
51
- # Give us the current time in seconds
52
- def current_time
53
- Time.now.to_f
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 start_time
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 pong(&block)
127
+ def stop(&block)
97
128
  callback(&block) if block_given?
98
129
  super
99
130
  succeed self
@@ -4,17 +4,19 @@ module KingKong
4
4
  class Pinger
5
5
  include Logging
6
6
 
7
- attr_accessor :aggregator, :duration
7
+ attr_reader :wait
8
8
 
9
- def initialize(duration=5)
10
- @duration = duration
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(&block)
16
+ def start
15
17
  logger.debug "Starting pinger #{self}"
16
- @timer = EventMachine::PeriodicTimer.new(duration){ ping(&block) }
17
- ping(&block)
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(&block)
34
- ping = Ping::Deferrable.new(30, sequencer)
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
- yield ping if block_given?
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
@@ -1,3 +1,3 @@
1
1
  module KingKong
2
- VERSION = "0.0.2"
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
- include EM::Ventually
4
+ include EM::Ventually
5
5
 
6
- before(:all) do
7
- @pinger = KingKong::Pinger.new
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
- it "should start pinging" do
11
- @pinger.start
12
- end
16
+ it "should ping" do
17
+ @pinger.start
18
+ ly(:completed) { @@ping.status }
19
+ end
13
20
 
14
- it "should stop pinging" do
15
- @pinger.stop
16
- end
21
+ it "should stop"
22
+ it "should start"
17
23
 
18
- it "should sequence pings"
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
@@ -4,5 +4,9 @@ require 'eventmachine'
4
4
  require 'logger'
5
5
  require 'em-ventually'
6
6
 
7
+ RSpec.configure do |config|
8
+ config.mock_framework = :rspec
9
+ end
10
+
7
11
  # Squelch the logger so we can see our specs passing
8
12
  KingKong.logger = Logger.new('/dev/null')
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.2
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-06 00:00:00.000000000Z
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: &70207424167020 !ruby/object:Gem::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: *70207424167020
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.8.6
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