File: socket_responding.rb

package info (click to toggle)
ruby-god 0.13.7-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster
  • size: 832 kB
  • sloc: ruby: 6,641; ansic: 237; makefile: 3
file content (142 lines) | stat: -rw-r--r-- 3,999 bytes parent folder | download | duplicates (3)
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