add condition clearing on entry, report contact match failures
[god.git] / lib / god / conditions / http_response_code.rb
blobdd14c6e0d09ccf729e19a8a2e151c26131ecf346
1 require 'net/http'
3 module God
4   module Conditions
5     
6     class HttpResponseCode < PollCondition
7       attr_accessor :code_is,      # e.g. 500 or '500' or [404, 500] or %w{404 500}
8                     :code_is_not,  # e.g. 200 or '200' or [200, 302] or %w{200 302}
9                     :times,        # e.g. 3 or [3, 5]
10                     :host,         # e.g. www.example.com
11                     :port,         # e.g. 8080
12                     :timeout,      # e.g. 60.seconds
13                     :path          # e.g. '/'
14       
15       def initialize
16         super
17         self.times = [1, 1]
18       end
19       
20       def prepare
21         self.code_is = Array(self.code_is).map { |x| x.to_i } if self.code_is
22         self.code_is_not = Array(self.code_is_not).map { |x| x.to_i } if self.code_is_not
23         
24         if self.times.kind_of?(Integer)
25           self.times = [self.times, self.times]
26         end
27         
28         @timeline = Timeline.new(self.times[1])
29         @history = Timeline.new(self.times[1])
30       end
31       
32       def reset
33         @timeline.clear
34         @history.clear
35       end
36       
37       def valid?
38         valid = true
39         valid &= complain("Attribute 'host' must be specified", self) if self.host.nil?
40         valid &= complain("Attribute 'port' must be specified", self) if self.port.nil?
41         valid &= complain("Attribute 'path' must be specified", self) if self.path.nil?
42         valid &= complain("One (and only one) of attributes 'code_is' and 'code_is_not' must be specified", self) if
43           (self.code_is.nil? && self.code_is_not.nil?) || (self.code_is && self.code_is_not)
44         valid &= complain("Attribute 'timeout' must be specified", self) if self.timeout.nil?
45         valid
46       end
47       
48       def test
49         response = nil
50         
51         Net::HTTP.start(self.host, self.port) do |http|
52           http.read_timeout = self.timeout
53           response = http.head(self.path)
54         end
55         
56         actual_response_code = response.code.to_i
57         if self.code_is && self.code_is.include?(actual_response_code)
58           pass(actual_response_code)
59         elsif self.code_is_not && !self.code_is_not.include?(actual_response_code)
60           pass(actual_response_code)
61         else
62           fail(actual_response_code)
63         end
64       rescue Timeout::Error
65         self.code_is ? fail('Timeout') : pass('Timeout')
66       end
67       
68       private
69       
70       def pass(code)
71         @timeline << true
72         if @timeline.select { |x| x }.size >= self.times.first
73           self.info = "http response abnormal #{history(code, true)}"
74           true
75         else
76           self.info = "http response nominal #{history(code, true)}"
77           false
78         end
79       end
80       
81       def fail(code)
82         @timeline << false
83         self.info = "http response nominal #{history(code, false)}"
84         false
85       end
86       
87       def history(code, passed)
88         entry = code.to_s.dup
89         entry = '*' + entry if passed
90         @history << entry
91         '[' + @history.join(", ") + ']'
92       end
93       
94     end
95     
96   end
97 end