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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
require 'net/http'
require 'net/https'
module God
module Conditions
# Condition Symbol :http_response_code
# Type: Poll
#
# Trigger based on the response from an HTTP request.
#
# Paramaters
# Required
# +host+ is the hostname to connect [required]
# --one of code_is or code_is_not--
# +code_is+ trigger if the response code IS one of these
# e.g. 500 or '500' or [404, 500] or %w{404 500}
# +code_is_not+ trigger if the response code IS NOT one of these
# e.g. 200 or '200' or [200, 302] or %w{200 302}
# Optional
# +port+ is the port to connect (default 80)
# +path+ is the path to connect (default '/')
# +headers+ is the hash of HTTP headers to send (default none)
# +times+ is the number of times after which to trigger (default 1)
# e.g. 3 (times in a row) or [3, 5] (three out of fives times)
# +timeout+ is the time to wait for a connection (default 60.seconds)
# +ssl+ should the connection use ssl (default false)
#
# Examples
#
# Trigger if the response code from www.example.com/foo/bar
# is not a 200 (or if the connection is refused or times out:
#
# on.condition(:http_response_code) do |c|
# c.host = 'www.example.com'
# c.path = '/foo/bar'
# c.code_is_not = 200
# end
#
# Trigger if the response code is a 404 or a 500 (will not
# be triggered by a connection refusal or timeout):
#
# on.condition(:http_response_code) do |c|
# c.host = 'www.example.com'
# c.path = '/foo/bar'
# c.code_is = [404, 500]
# end
#
# Trigger if the response code is not a 200 five times in a row:
#
# on.condition(:http_response_code) do |c|
# c.host = 'www.example.com'
# c.path = '/foo/bar'
# c.code_is_not = 200
# c.times = 5
# end
#
# Trigger if the response code is not a 200 or does not respond
# within 10 seconds:
#
# on.condition(:http_response_code) do |c|
# c.host = 'www.example.com'
# c.path = '/foo/bar'
# c.code_is_not = 200
# c.timeout = 10
# end
class HttpResponseCode < PollCondition
attr_accessor :code_is, # e.g. 500 or '500' or [404, 500] or %w{404 500}
:code_is_not, # e.g. 200 or '200' or [200, 302] or %w{200 302}
:times, # e.g. 3 or [3, 5]
:host, # e.g. www.example.com
:port, # e.g. 8080
:ssl, # e.g. true or false
:ca_file, # e.g /path/to/pem_file for ssl verification (checkout http://curl.haxx.se/ca/cacert.pem)
:timeout, # e.g. 60.seconds
:path, # e.g. '/'
:headers # e.g. {'Host' => 'myvirtual.mydomain.com'}
def initialize
super
self.port = 80
self.path = '/'
self.headers = {}
self.times = [1, 1]
self.timeout = 60.seconds
self.ssl = false
self.ca_file = nil
end
def prepare
self.code_is = Array(self.code_is).map { |x| x.to_i } if self.code_is
self.code_is_not = Array(self.code_is_not).map { |x| x.to_i } if self.code_is_not
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 valid?
valid = true
valid &= complain("Attribute 'host' must be specified", self) if self.host.nil?
valid &= complain("One (and only one) of attributes 'code_is' and 'code_is_not' must be specified", self) if
(self.code_is.nil? && self.code_is_not.nil?) || (self.code_is && self.code_is_not)
valid
end
def test
response = nil
connection = Net::HTTP.new(self.host, self.port)
connection.use_ssl = self.port == 443 ? true : self.ssl
connection.verify_mode = OpenSSL::SSL::VERIFY_NONE if connection.use_ssl?
if connection.use_ssl? && self.ca_file
pem = File.read(self.ca_file)
connection.ca_file = self.ca_file
connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
end
connection.start do |http|
http.read_timeout = self.timeout
response = http.get(self.path, self.headers)
end
actual_response_code = response.code.to_i
if self.code_is && self.code_is.include?(actual_response_code)
pass(actual_response_code)
elsif self.code_is_not && !self.code_is_not.include?(actual_response_code)
pass(actual_response_code)
else
fail(actual_response_code)
end
rescue Errno::ECONNREFUSED
self.code_is ? fail('Refused') : pass('Refused')
rescue Errno::ECONNRESET
self.code_is ? fail('Reset') : pass('Reset')
rescue EOFError
self.code_is ? fail('EOF') : pass('EOF')
rescue Timeout::Error
self.code_is ? fail('Timeout') : pass('Timeout')
rescue Errno::ETIMEDOUT
self.code_is ? fail('Timedout') : pass('Timedout')
rescue Exception => failure
self.code_is ? fail(failure.class.name) : pass(failure.class.name)
end
private
def pass(code)
@timeline << true
if @timeline.select { |x| x }.size >= self.times.first
self.info = "http response abnormal #{history(code, true)}"
true
else
self.info = "http response nominal #{history(code, true)}"
false
end
end
def fail(code)
@timeline << false
self.info = "http response nominal #{history(code, false)}"
false
end
def history(code, passed)
entry = code.to_s.dup
entry = '*' + entry if passed
@history << entry
'[' + @history.join(", ") + ']'
end
end
end
end
|