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.path = '/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