1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
require 'socket'
include Socket::Constants
module God
module Conditions
# Condition Symbol :socket_running
# Type: Poll
#
# Trigger when a TCP or UNIX socket is running or not
#
# Parameters
# Required
# +family+ is the family of socket: either 'tcp' or 'unix'
# --one of port or path--
# +port+ is the port (required if +family+ is 'tcp')
# +path+ is the path (required if +family+ is 'unix')
#
# Optional
# +responding+ is the boolean specifying whether you want to trigger if the socket is responding (true)
# or if it is not responding (false) (default false)
#
# Examples
#
# Trigger if the TCP socket on port 80 is not responding or the connection is refused
#
# on.condition(:socket_responding) do |c|
# c.family = 'tcp'
# c.port = '80'
# end
#
# Trigger if the socket is not responding or the connection is refused (use alternate compact +socket+ interface)
#
# on.condition(:socket_responding) do |c|
# c.socket = 'tcp:80'
# end
#
# Trigger if the socket is responding
#
# on.condition(:socket_responding) do |c|
# c.socket = 'tcp:80'
# c.responding = true
# end
#
# Trigger if the socket is not responding or the connection is refused 5 times in a row
#
# on.condition(:socket_responding) do |c|
# c.socket = 'tcp:80'
# c.times = 5
# end
#
# Trigger if the Unix socket on path '/tmp/sock' is not responding or non-existent
#
# on.condition(:socket_responding) do |c|
# c.family = 'unix'
# c.port = '/tmp/sock'
# end
#
class SocketResponding < PollCondition
attr_accessor :family, :addr, :port, :path, :times, :responding
def initialize
super
# default to tcp on the localhost
self.family = 'tcp'
self.addr = '127.0.0.1'
# Set these to nil/0 values
self.port = 0
self.path = nil
self.responding = false
self.times = [1, 1]
end
def prepare
if self.times.kind_of?(Integer)
self.times = [self.times, self.times]
end
@timeline = Timeline.new(self.times[1])
@history = Timeline.new(self.times[1])
end
def reset
@timeline.clear
@history.clear
end
def socket=(s)
components = s.split(':')
if components.size == 3
@family,@addr,@port = components
@port = @port.to_i
elsif components[0] =~ /^tcp$/
@family = components[0]
@port = components[1].to_i
elsif components[0] =~ /^unix$/
@family = components[0]
@path = components[1]
end
end
def valid?
valid = true
if self.family == 'tcp' and @port == 0
valid &= complain("Attribute 'port' must be specified for tcp sockets", self)
end
if self.family == 'unix' and self.path.nil?
valid &= complain("Attribute 'path' must be specified for unix sockets", self)
end
valid = false unless %w{tcp unix}.member?(self.family)
valid
end
def test
self.info = []
if self.family == 'tcp'
begin
s = TCPSocket.new(self.addr, self.port)
rescue SystemCallError
end
status = self.responding == !s.nil?
elsif self.family == 'unix'
begin
s = UNIXSocket.new(self.path)
rescue SystemCallError
end
status = self.responding == !s.nil?
else
status = false
end
@timeline.push(status)
history = "[" + @timeline.map {|t| t ? '*' : ''}.join(',') + "]"
if @timeline.select { |x| x }.size >= self.times.first
self.info = "socket out of bounds #{history}"
return true
else
return false
end
end
end
end
end
|