make self-daemonizing command synchronous
[god.git] / lib / god / conditions / http_response_code.rb
blobdbee675c3a766a79c9dbbb9dcabae73834e65709
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 valid?
33         valid = true
34         valid &= complain("Attribute 'host' must be specified", self) if self.host.nil?
35         valid &= complain("Attribute 'port' must be specified", self) if self.port.nil?
36         valid &= complain("Attribute 'path' must be specified", self) if self.path.nil?
37         valid &= complain("One (and only one) of attributes 'code_is' and 'code_is_not' must be specified", self) if
38           (self.code_is.nil? && self.code_is_not.nil?) || (self.code_is && self.code_is_not)
39         valid &= complain("Attribute 'timeout' must be specified", self) if self.timeout.nil?
40         valid
41       end
42       
43       def test
44         response = nil
45         
46         Net::HTTP.start(self.host, self.port) do |http|
47           http.read_timeout = self.timeout
48           response = http.head(self.path)
49         end
50         
51         actual_response_code = response.code.to_i
52         if self.code_is && self.code_is.include?(actual_response_code)
53           pass(actual_response_code)
54         elsif self.code_is_not && !self.code_is_not.include?(actual_response_code)
55           pass(actual_response_code)
56         else
57           fail(actual_response_code)
58         end
59       rescue Timeout::Error
60         self.code_is ? fail('Timeout') : pass('Timeout')
61       end
62       
63       private
64       
65       def pass(code)
66         @timeline << true
67         if @timeline.select { |x| x }.size >= self.times.first
68           self.info = "http response abnormal #{history(code, true)}"
69           @timeline.clear
70           @history.clear
71           true
72         else
73           self.info = "http response nominal #{history(code, true)}"
74           false
75         end
76       end
77       
78       def fail(code)
79         @timeline << false
80         self.info = "http response nominal #{history(code, false)}"
81         false
82       end
83       
84       def history(code, passed)
85         entry = code.to_s.dup
86         entry = '*' + entry if passed
87         @history << entry
88         '[' + @history.join(", ") + ']'
89       end
90       
91     end
92     
93   end
94 end